T03 — telemetry.dart, format.dart, live_telemetry.dart, plus pure domain models (Trip/Segment/TrackPoint/RideStats) with no persistence dependency, so Drift can map to them in T08 rather than the domain depending on the database. T04 — ride_statistics.dart including ElevationAccumulator, ported structurally faithfully: moving average, reversal hysteresis, gainIncludingPending, and the finish() reconciliation against lastRaw. T07 (early, because T04 forced it) — tool/parity/ drives identical fixtures through the real Kotlin files and the Dart port, then diffs. Result: every value byte-identical, including noisy_gain=38.959594555022136 to the last digit. The sole difference is run_avg_speed, where Kotlin's 32-bit Float widens to double with artefacts Dart's binary64 does not reproduce. Documented, not papered over. That harness settled a real question. The ported elevation test failed at 50.9m against Kotlin's 35m bound, which looked like a porting bug. It was not: Kotlin's and Dart's Random(42) are different streams. On a shared LCG fixture both produce 39.0m -- which would also fail Kotlin's own bound. The native guard passes on seed luck rather than on a property of the algorithm. The Dart test now uses the shared LCG, asserts bit-equality with Kotlin, and sets its bound from measured behaviour (25 seeds spanned 24.7-46.7m). 52 tests passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 lines
473 B
Kotlin
11 lines
473 B
Kotlin
package com.rippr.data
|
|
data class TrackPoint(
|
|
val id: Long = 0, val tripId: Long, val segmentId: Long, val timestamp: Long,
|
|
val latitude: Double, val longitude: Double, val speedKmh: Float,
|
|
val altitudeM: Double, val accuracyM: Float = 0f, val bearingDeg: Float = 0f,
|
|
val synced: Boolean = false,
|
|
)
|
|
data class Segment(
|
|
val id: Long = 0, val tripId: Long, val startedAt: Long, val endedAt: Long? = null,
|
|
) { val isOpen: Boolean get() = endedAt == null }
|