Files

Trikke Recorder for Android

Prototype v0.2 records the existing reliable BLE transport to an authoritative .trk file on an Android phone. It deliberately does not filter, fuse, convert, or upload telemetry.

The permanent Android application ID and Kotlin namespace are com.jsjdesigns.trikkerecorder.

Platform and behavior

  • Android 12 (API 31) or newer
  • BLE central connection to the TrikkeSensor service
  • User-started connected-device foreground service
  • Partial wake lock during an active recording
  • App-private capture storage with explicit export through Android's document UI
  • A .session.json sidecar containing phone wall/monotonic clock anchors and final integrity counters

Every notification is reassembled using its packet sequence, offset, and total size. A complete frame must also pass the TRK1 shape and CRC checks. For a new frame the recorder then performs this ordering:

  1. Append the unchanged frame to the .trk file.
  2. Flush the stream and synchronize its file descriptor.
  3. Update in-memory integrity state.
  4. Write the exact ACK1 packet sequence to the C3.

If the ACK is lost, the firmware replays the frame. An exact replay of the last persisted packet is not appended again, but it is acknowledged again. The app refuses to acknowledge a CRC-invalid frame or the same sequence carrying different bytes.

At Start, the app generates a random session token and sends it to the Control characteristic before subscribing. A new token makes the C3 perform one controlled software restart. The app reconnects with the same token, after which the C3 starts acquisition with empty queues and zeroed counters. Temporary BLE reconnects during that recording reuse the token and therefore preserve queued samples instead of resetting the session.

The validated phone/C3 pair takes about 10.8 seconds from tapping Start to the first persisted frame because a new session deliberately includes a controlled C3 restart and two connection passes. A panic, watchdog, brownout, or power-on reset during recording invalidates the retained token; recovery adds another controlled restart, and the resulting packet/sample sequence reset remains visible in the sidecar integrity counters.

Build and install

Android Studio is the easiest route: open the android/ directory, allow the Gradle sync, select the phone, and run the app configuration.

The command-line equivalent on this Mac is:

cd android
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
export ANDROID_HOME="$HOME/Library/Android/sdk"
./gradlew testDebugUnitTest lintDebug assembleDebug
"$ANDROID_HOME/platform-tools/adb" install -r app/build/outputs/apk/debug/app-debug.apk

The tests exercise the Android parser against the real ble_mtu_race_4bf00eb.trk hardware fixture as well as fragment replay, malformed ordering, CRC rejection, unsigned sequence wrap, durable append, and replay deduplication.

Record and export

  1. Power the sensor and open Trikke Recorder.
  2. Tap Start recording and grant Nearby Devices and notification permission.
  3. Wait for Recording: Connected and subscribed and confirm that the sample count is increasing.
  4. The activity may be left, the screen may be locked, and the phone may be put in a pocket. Keep the persistent recording notification active.
  5. Reopen the app and tap Stop and close safely.
  6. After the status reaches Stopped, tap Export last .trk and select a destination.

The app-private .session.json starts with complete: false. A normal Stop closes and synchronizes the binary file, then rewrites the sidecar with complete: true, final counts, and first/last phone-to-device clock anchors. If Android or the user force-stops the process, every previously acknowledged frame remains in the .trk file, while the incomplete sidecar makes the abnormal end visible.

Phone receipt time is only an alignment anchor; BLE delivery latency means it is not the sensor's physical sample time. Analysis must continue to use the device timestamps carried by each TRK1 frame.

First coordinated acceptance test

Do this before a ride:

  1. Record for two minutes with the enclosure flat and still.

  2. Lock the phone for at least one minute and verify the sample counter resumes visibly when the app is reopened.

  3. Cause a roughly three-second outage by switching Bluetooth off, switch it back on, and wait for Recording again.

  4. Stop and export the .trk file.

  5. Decode it from the repository root:

    python3 tools/decode_binary.py ride_YYYYMMDD_HHMMSS.trk ride.csv
    

Acceptance requires a valid complete decode, no unexplained packet or sample gaps, and no queue overflow for the short interruption. Duplicate replays and the firmware disconnect/replay counters may increase and are expected.

After that passes, repeat for 1530 minutes with the screen locked and include several short real-world range/interference interruptions.

Prototype limitations

  • The BLE service is still unauthenticated and single-connection, as documented in docs/ble-transport-v2.md.
  • The recorder supports one active session and one known sensor.
  • Only the binary capture is exported from the UI in this pass. The sidecar is retained app-private for diagnosis.
  • A phone force-stop cannot run cleanup code. The persisted binary prefix remains useful, but the session is intentionally marked incomplete.
  • No GPS, Samsung Health import, CSV rendering, or live motion analysis is in this milestone.