@@ -16,7 +16,7 @@ class RideDbHelper(context: Context) :
1616
1717 companion object {
1818 // Incrementado para v11 para adicionar as novas colunas
19- const val DATABASE_VERSION = 15
19+ const val DATABASE_VERSION = 19
2020 const val DATABASE_NAME = " BikeRides.db"
2121 private const val TAG = " RideDbHelper"
2222
@@ -185,27 +185,27 @@ class RideDbHelper(context: Context) :
185185 val values = ContentValues ().apply {
186186 put(TelemetryEntry .COLUMN_RIDE_ID , rideId)
187187
188- // DADOS CRUCIAIS: Data vinda do Info
189- put(TelemetryEntry .COLUMN_PACKET_DATE , infoMap[" date" ] as ? String ) // "yyyy-MM-dd HH:mm:ss"
188+ put(TelemetryEntry .COLUMN_PACKET_DATE , infoMap[" date" ] as ? String )
190189 put(TelemetryEntry .COLUMN_PACKET_TIME , infoMap[" time" ] as ? String )
191190
192191 put(TelemetryEntry .COLUMN_GPS_TIMESTAMP , gpsMap[" timestamp" ] as ? String )
193- put(TelemetryEntry .COLUMN_LATITUDE , gpsMap[" latitude" ] as ? Double )
194- put(TelemetryEntry .COLUMN_LONGITUDE , gpsMap[" longitude" ] as ? Double )
195- put(TelemetryEntry .COLUMN_ALTITUDE , gpsMap[" altitude" ] as ? Double )
196- put(TelemetryEntry .COLUMN_GPS_SPEED , gpsMap[" speed" ] as ? Double )
197- put(TelemetryEntry .COLUMN_DIRECTION , gpsMap[" direction" ] as ? Double )
192+ put(TelemetryEntry .COLUMN_LATITUDE , ( gpsMap[" latitude" ] as ? Number )?.toDouble() )
193+ put(TelemetryEntry .COLUMN_LONGITUDE , ( gpsMap[" longitude" ] as ? Number )?.toDouble() )
194+ put(TelemetryEntry .COLUMN_ALTITUDE , ( gpsMap[" altitude" ] as ? Number )?.toDouble() )
195+ put(TelemetryEntry .COLUMN_GPS_SPEED , ( gpsMap[" speed" ] as ? Number )?.toDouble() )
196+ put(TelemetryEntry .COLUMN_DIRECTION , ( gpsMap[" direction" ] as ? Number )?.toDouble() )
198197 put(TelemetryEntry .COLUMN_FIX_SATELLITES , gpsMap[" fix_satellites" ] as ? Int )
199198 put(TelemetryEntry .COLUMN_FIX_QUALITY , gpsMap[" fix_quality" ] as ? Int )
200199
201200 crankMap?.let {
202- put(TelemetryEntry .COLUMN_POWER , it[" power" ] as ? Double )
203- put(TelemetryEntry .COLUMN_CADENCE , it[" cadence" ] as ? Double )
204- put(TelemetryEntry .COLUMN_JOULES , it[" joules" ] as ? Double )
205- put(TelemetryEntry .COLUMN_CRANK_CALORIES , it[" calories" ] as ? Double )
206- put(TelemetryEntry .COLUMN_CRANK_SPEED_MS , it[" speed_ms" ] as ? Double )
207- put(TelemetryEntry .COLUMN_CRANK_SPEED , it[" speed" ] as ? Double )
208- put(TelemetryEntry .COLUMN_CRANK_DISTANCE , it[" distance" ] as ? Double )
201+ // AQUI ESTAVA O BUG: Usamos (Number)?.toDouble() para aceitar Zeros
202+ put(TelemetryEntry .COLUMN_POWER , (it[" power" ] as ? Number )?.toDouble())
203+ put(TelemetryEntry .COLUMN_CADENCE , (it[" cadence" ] as ? Number )?.toDouble())
204+ put(TelemetryEntry .COLUMN_JOULES , (it[" joules" ] as ? Number )?.toDouble())
205+ put(TelemetryEntry .COLUMN_CRANK_CALORIES , (it[" calories" ] as ? Number )?.toDouble())
206+ put(TelemetryEntry .COLUMN_CRANK_SPEED_MS , (it[" speed_ms" ] as ? Number )?.toDouble())
207+ put(TelemetryEntry .COLUMN_CRANK_SPEED , (it[" speed" ] as ? Number )?.toDouble())
208+ put(TelemetryEntry .COLUMN_CRANK_DISTANCE , (it[" distance" ] as ? Number )?.toDouble())
209209 }
210210 }
211211 return db.insert(TelemetryEntry .TABLE_NAME , null , values) != - 1L
@@ -279,17 +279,14 @@ class RideDbHelper(context: Context) :
279279 fun calculateRideStatistics (rideId : Long ): Map <String , Any ?>? {
280280 val db = this .readableDatabase
281281
282- // 1. Adicionei COLUMN_CRANK_DISTANCE na projeção, pois estava faltando para cumprir o TODO da distância
283282 val cursor = db.query(
284283 TelemetryEntry .TABLE_NAME ,
285284 arrayOf(
286285 TelemetryEntry .COLUMN_PACKET_DATE ,
287- TelemetryEntry .COLUMN_LATITUDE , // Mantido caso queira debug ou fallback, mas o cálculo principal mudou
288- TelemetryEntry .COLUMN_LONGITUDE ,
289286 TelemetryEntry .COLUMN_ALTITUDE ,
290287 TelemetryEntry .COLUMN_GPS_SPEED ,
291288 TelemetryEntry .COLUMN_CRANK_SPEED ,
292- TelemetryEntry .COLUMN_CRANK_DISTANCE , // <-- ADICIONADO
289+ TelemetryEntry .COLUMN_CRANK_DISTANCE ,
293290 TelemetryEntry .COLUMN_POWER ,
294291 TelemetryEntry .COLUMN_CADENCE ,
295292 TelemetryEntry .COLUMN_CRANK_CALORIES
@@ -304,27 +301,28 @@ class RideDbHelper(context: Context) :
304301 return null
305302 }
306303
307- // Variáveis de Estado
308304 var startTime: Long? = null
309305 var endTime: Long? = null
310306 val dateFormat = SimpleDateFormat (" yyyy-MM-dd HH:mm:ss" , Locale .getDefault())
311307
312- // Acumuladores e Rastreadores de Máximas
308+ // Variáveis para Máximas
313309 var maxSpeed = 0.0
314310 var maxPower = 0.0
315311 var maxCadence = 0.0
316312 var maxAltitude = 0.0
317313
318- // Listas para médias que NÃO são cumulativas
314+ // Listas para calcular as médias
315+ val speeds = mutableListOf<Double >()
319316 val powers = mutableListOf<Double >()
317+ val cadences = mutableListOf<Double >()
320318 val altitudes = mutableListOf<Double >()
321319
322- // Variáveis para valores "Last Known" (já acumulados pelo sensor)
323- var lastTotalDistanceKm = 0.0
320+ // Acumuladores finais
321+ var lastTotalDistanceMeters = 0.0
324322 var lastTotalCalories = 0.0
325- var lastAvgCadence = 0.0
326323
327324 cursor.use {
325+ // Índices das colunas
328326 val dateIdx = it.getColumnIndexOrThrow(TelemetryEntry .COLUMN_PACKET_DATE )
329327 val altIdx = it.getColumnIndexOrThrow(TelemetryEntry .COLUMN_ALTITUDE )
330328 val gpsSpeedIdx = it.getColumnIndexOrThrow(TelemetryEntry .COLUMN_GPS_SPEED )
@@ -335,7 +333,7 @@ class RideDbHelper(context: Context) :
335333 val calIdx = it.getColumnIndexOrThrow(TelemetryEntry .COLUMN_CRANK_CALORIES )
336334
337335 do {
338- // --- 1. Tempo (Start/End) ---
336+ // 1. Tempo
339337 val dateStr = it.getString(dateIdx)
340338 if (dateStr != null ) {
341339 try {
@@ -345,77 +343,76 @@ class RideDbHelper(context: Context) :
345343 if (startTime == null ) startTime = ts
346344 endTime = ts
347345 }
348- } catch (e: Exception ) {
349- Log .e(TAG , " Erro parsing packet_date: $dateStr " )
350- }
346+ } catch (e: Exception ) { Log .e(TAG , " Erro parse data: $dateStr " ) }
351347 }
352348
353- // --- 2. Distância (#TODO: PEGAR ULTIMO VALOR) ---
349+ // 2. Distância (Acumulada em Metros)
354350 if (! it.isNull(crankDistIdx)) {
355351 val dist = it.getDouble(crankDistIdx)
356- // Assume que o sensor envia valor cumulativo. Atualizamos sempre o "último visto".
357- if (dist > lastTotalDistanceKm) {
358- lastTotalDistanceKm = dist
359- }
352+ if (dist > lastTotalDistanceMeters) lastTotalDistanceMeters = dist
360353 }
361354
362- // --- 3. Altitude (Média e Máxima) ---
355+ // 3. Altitude (Sempre adicionamos, pois existe altitude mesmo parado)
363356 if (! it.isNull(altIdx)) {
364357 val alt = it.getDouble(altIdx)
358+ // Filtramos 0 absoluto pois geralmente é erro de GPS iniciando,
359+ // mas não filtramos "parado"
365360 if (alt != 0.0 ) {
366361 altitudes.add(alt)
367362 if (alt > maxAltitude) maxAltitude = alt
368363 }
369364 }
370365
371- // --- 4. Velocidade Máxima (#TODO: PRIORIDADE CRANK) ---
366+ // 4. Velocidade
372367 val cSpeed = if (! it.isNull(crankSpeedIdx)) it.getDouble(crankSpeedIdx) else null
373368 val gSpeed = if (! it.isNull(gpsSpeedIdx)) it.getDouble(gpsSpeedIdx) else null
374-
375- // Prioridade: Se tem CrankSpeed, usa ele. Se não, usa GPS. Se nenhum, 0.0
369+ // Prioriza sensor de roda, senão GPS, senão 0
376370 val currentSpeed = cSpeed ? : gSpeed ? : 0.0
371+
372+ speeds.add(currentSpeed) // Adiciona na lista para média
377373 if (currentSpeed > maxSpeed) maxSpeed = currentSpeed
378374
379- // --- 5. Potência (#TODO: CALCULAR MAX E MEDIA) ---
380- // Potência é instantânea, então acumulamos na lista para média e verificamos pico.
381- if (! it.isNull(powerIdx)) {
382- val p = it.getDouble(powerIdx)
383- powers.add(p)
384- if (p > maxPower) maxPower = p
385- }
375+ // 5. Potência
376+ val p = if (! it.isNull(powerIdx)) it.getDouble(powerIdx) else 0.0
377+ powers.add(p)
378+ if (p > maxPower) maxPower = p
386379
387- // --- 6. Cadência (#TODO: JA VEM SOMADO, PEGAR ULTIMA COLUNA) ---
388- // Se "já vem somado" (ou é uma média cumulativa do sensor), guardamos o último valor para a média final.
389- // Mas para Máxima, ainda precisamos varrer os valores instantâneos se o sensor oscilar.
390- if (! it.isNull(cadIdx)) {
391- val c = it.getDouble(cadIdx)
392- lastAvgCadence = c // O último valor lido será a média/total final
393- if (c > maxCadence) maxCadence = c
394- }
380+ // 6. Cadência
381+ val c = if (! it.isNull(cadIdx)) it.getDouble(cadIdx) else 0.0
382+ cadences.add(c)
383+ if (c > maxCadence) maxCadence = c
395384
396- // --- 7. Calorias (#TODO: JA VEM SOMADO) ---
385+ // 7. Calorias
397386 if (! it.isNull(calIdx)) {
398387 val cal = it.getDouble(calIdx)
399- if (cal > lastTotalCalories) {
400- lastTotalCalories = cal
401- }
388+ if (cal > lastTotalCalories) lastTotalCalories = cal
402389 }
403390
404391 } while (it.moveToNext())
405392 }
406393
407- // --- CÁLCULOS FINAIS ---
394+ // --- CÁLCULOS FINAIS COM LÓGICA DE "ATIVA" (IGNORANDO ZEROS) ---
408395
409- val avgPower = if (powers.isNotEmpty()) powers.average() else 0.0
410- val avgAltitude = if (altitudes.isNotEmpty()) altitudes.average() else 0.0
396+ // 1. Correção de Unidade: Metros para KM
397+ val totalDistanceKm = lastTotalDistanceMeters / 1000.0
398+
399+ // 2. Velocidade Média em Movimento (Average Moving Speed)
400+ // Filtramos velocidades muito baixas (< 1.0 km/h) que são ruído
401+ val activeSpeeds = speeds.filter { it > 1.0 }
402+ val avgSpeed = if (activeSpeeds.isNotEmpty()) activeSpeeds.average() else 0.0
411403
412- // Velocidade Média calculada com base na Distância Final (do sensor) / Tempo Total
413- val startTs = startTime ? : 0L
414- val endTs = endTime ? : 0L
415- val durationMillis = if (endTs >= startTs) endTs - startTs else 0L
416- val durationHours = durationMillis / 1000.0 / 3600.0
404+ // 3. Potência Média Ativa (Average Active Power)
405+ // Filtramos potências < 1.0 Watt (ignora roda livre/parado)
406+ val activePowers = powers.filter { it > 1.0 }
407+ val avgPower = if (activePowers.isNotEmpty()) activePowers.average() else 0.0
417408
418- val avgSpeed = if (durationHours > 0.002 ) lastTotalDistanceKm / durationHours else 0.0
409+ // 4. Cadência Média Ativa
410+ // Filtramos zeros (ignora quando parou de pedalar)
411+ val activeCadences = cadences.filter { it > 0 }
412+ val avgCadence = if (activeCadences.isNotEmpty()) activeCadences.average() else 0.0
413+
414+ // 5. Altitude Média (Geralmente se usa média total)
415+ val avgAltitude = if (altitudes.isNotEmpty()) altitudes.average() else 0.0
419416
420417 val outputDateFmt = SimpleDateFormat (" yyyy-MM-dd HH:mm:ss.SSS" , Locale .getDefault())
421418
@@ -424,21 +421,17 @@ class RideDbHelper(context: Context) :
424421 " start_time" to if (startTime != null ) outputDateFmt.format(Date (startTime!! )) else null ,
425422 " end_time" to if (endTime != null ) outputDateFmt.format(Date (endTime!! )) else null ,
426423
427- // Distância direta do sensor (último valor)
428- " total_distance_km" to lastTotalDistanceKm,
429-
430- // Calorias direta do sensor (último valor)
424+ " total_distance_km" to totalDistanceKm, // Agora corrigido para KM
431425 " calories" to lastTotalCalories,
432426
433427 " max_velocity_kmh" to maxSpeed,
434- " avg_velocity_kmh" to avgSpeed,
428+ " avg_velocity_kmh" to avgSpeed, // Média apenas em movimento
435429
436430 " max_power" to maxPower,
437- " avg_power" to avgPower,
431+ " avg_power" to avgPower, // Média apenas pedalando
438432
439433 " max_cadence" to maxCadence,
440- // Cadência média baseada no último valor lido (conforme TODO "pegar ultima coluna")
441- " avg_cadence" to lastAvgCadence,
434+ " avg_cadence" to avgCadence, // Média apenas pedalando
442435
443436 " max_altitude" to maxAltitude,
444437 " avg_altitude" to avgAltitude
0 commit comments