add reliable BLE telemetry transport

This commit is contained in:
Jay
2026-08-18 06:24:10 -04:00
parent 00f52ecf0f
commit 7617010d8e
26 changed files with 1499 additions and 47 deletions
+66
View File
@@ -0,0 +1,66 @@
"""BLE fragment reassembly and acknowledgement helpers for TRK1 frames."""
from __future__ import annotations
import struct
BLE_FRAGMENT_HEADER = struct.Struct("<IHH")
BLE_ACK = struct.Struct("<4sI")
BLE_MIN_FRAME_SIZE = 36
BLE_MAX_FRAME_SIZE = 196
class BleFrameReassembler:
def __init__(self) -> None:
self.rejected_fragment_count = 0
self._sequence: int | None = None
self._total_size = 0
self._frame = bytearray()
def reset(self) -> None:
self._sequence = None
self._total_size = 0
self._frame.clear()
def feed(self, fragment: bytes) -> bytes | None:
if len(fragment) <= BLE_FRAGMENT_HEADER.size:
self.rejected_fragment_count += 1
self.reset()
return None
sequence, offset, total_size = BLE_FRAGMENT_HEADER.unpack_from(fragment)
data = fragment[BLE_FRAGMENT_HEADER.size :]
if (
total_size < BLE_MIN_FRAME_SIZE
or total_size > BLE_MAX_FRAME_SIZE
or offset >= total_size
or offset + len(data) > total_size
):
self.rejected_fragment_count += 1
self.reset()
return None
# Offset zero is an explicit replay boundary, including when the same
# packet restarts after an ACK timeout or reconnect.
if offset == 0:
self._sequence = sequence
self._total_size = total_size
self._frame = bytearray()
if (
self._sequence != sequence
or self._total_size != total_size
or offset != len(self._frame)
):
self.rejected_fragment_count += 1
self.reset()
return None
self._frame.extend(data)
if len(self._frame) != self._total_size:
return None
frame = bytes(self._frame)
self.reset()
return frame
def encode_ack(packet_sequence: int) -> bytes:
return BLE_ACK.pack(b"ACK1", packet_sequence & 0xFFFFFFFF)