Add reliable Android BLE ride recorder

This commit is contained in:
Jay
2026-08-20 12:40:39 -04:00
parent 3bad2275cc
commit 6e408a3048
40 changed files with 2781 additions and 120 deletions
+55 -1
View File
@@ -1,4 +1,5 @@
import hashlib
import json
import shutil
import subprocess
import sys
@@ -18,7 +19,7 @@ from trikke_protocol import ( # noqa: E402
StreamParser,
sample_to_csv_row,
)
from trikke_ble import BleFrameReassembler, encode_ack # noqa: E402
from trikke_ble import BleFrameReassembler, encode_ack, encode_begin_session # noqa: E402
class ProtocolContractTest(unittest.TestCase):
@@ -197,6 +198,10 @@ class ProtocolContractTest(unittest.TestCase):
self.assertIsNone(reassembler.feed(fragment(0, 40)))
self.assertEqual(frame, reassembler.feed(fragment(40, len(frame) - 40)))
self.assertEqual(b"ACK1" + sequence.to_bytes(4, "little"), encode_ack(sequence))
self.assertEqual(
b"BGN1\x08\x07\x06\x05\x04\x03\x02\x01",
encode_begin_session(0x0102030405060708),
)
self.assertEqual(0, reassembler.rejected_fragment_count)
self.assertIsNone(reassembler.feed(fragment(20, 20)))
@@ -327,6 +332,25 @@ class ProtocolContractTest(unittest.TestCase):
"transport_invalid_ack_count": 0,
},
},
"ride_20260820_090607.trk": {
"sha256": "c938934c0905748d2f8d8be61cff1ff446d8415b34850628654967398fd7d91f",
"sample_count": 19984,
"first_sequence": 0,
"last_sequence": 19983,
"max_dropped": 0,
"timing_anomalies": 8745,
"accel_overruns": 6,
"gaps": [],
"final_status": {
"sensor_read_failure_count": 0,
"queue_overflow_count": 0,
"transport_begin_retry_count": 2,
"transport_disconnect_count": 1,
"transport_send_failure_count": 0,
"transport_replay_count": 1,
"transport_invalid_ack_count": 0,
},
},
}
for name, contract in expected.items():
@@ -379,6 +403,11 @@ class ProtocolContractTest(unittest.TestCase):
contract["timing_anomalies"],
integrity.timing_anomaly_count,
)
if "accel_overruns" in contract:
self.assertEqual(
contract["accel_overruns"],
integrity.accel_overrun_count,
)
self.assertEqual(
contract["max_dropped"],
integrity.final_dropped_sample_count,
@@ -403,6 +432,31 @@ class ProtocolContractTest(unittest.TestCase):
]
self.assertEqual(contract["gaps"], gaps)
def test_android_reconnect_session_sidecar(self) -> None:
data = (
ROOT
/ "tests"
/ "fixtures"
/ "ride_20260820_090607.session.json"
).read_bytes()
self.assertEqual(
"b16a8f0c213a14fd2760b6efba1c6288f3631e57a331c553893e281dce047888",
hashlib.sha256(data).hexdigest(),
)
summary = json.loads(data)
self.assertTrue(summary["complete"])
self.assertIsNone(summary["error"])
self.assertEqual("ride_20260820_090607.trk", summary["captureFile"])
self.assertEqual(2578, summary["frames"])
self.assertEqual(19984, summary["samples"])
self.assertEqual(1, summary["duplicateReplays"])
self.assertEqual(0, summary["packetGaps"])
self.assertEqual(0, summary["sampleGaps"])
self.assertEqual(0, summary["droppedSamples"])
self.assertEqual(0, summary["queueOverflows"])
self.assertEqual(1, summary["transportDisconnects"])
self.assertEqual(1, summary["transportReplays"])
def test_exact_usb_raw_wire_evidence(self) -> None:
expected = {
"direct_usb_3c95f3d": {