feat: add PCARS 1 & 2 UDP telemetry parser (port 5606)

- Auto-detect PCARS1 (1367-byte V1) vs PCARS2 (559-byte V2) packet format
- Parse speed, RPM, gear, inputs, tyres, engine temps, damage, weather
- Map PCARS-specific fields to TelemetryFrame (oil/water temp, damage, etc.)
- Add FeedSource::Pcars + 16 PCARS-specific Option fields to TelemetryFrame
- Port 5606 as optional bind-listen feed (like Telemetry Tool)
- 5 new unit tests for PCARS parser (32 total)
- Shared binary_utils module for all parsers
This commit is contained in:
Jan-Peter von Hunnius 2026-06-04 09:49:00 +02:00
parent 7a1c37fc86
commit a50d3af290
4 changed files with 570 additions and 0 deletions

View file

@ -41,6 +41,22 @@ pub fn read_i16(data: &[u8], offset: usize) -> i16 {
i16::from_le_bytes(buf)
}
/// Read a little-endian `u16` from the data at the given offset.
/// Returns 0 if the offset is out of bounds (safe fallback for malformed packets).
pub fn read_u16(data: &[u8], offset: usize) -> u16 {
let buf: [u8; 2] = data
.get(offset..offset + 2)
.and_then(|s| s.try_into().ok())
.unwrap_or([0u8; 2]);
u16::from_le_bytes(buf)
}
/// Read a signed `i8` from the data at the given offset.
/// Returns 0 if the offset is out of bounds.
pub fn read_i8(data: &[u8], offset: usize) -> i8 {
data.get(offset).map(|&b| b as i8).unwrap_or(0)
}
/// Read a little-endian `f32` from the data at the given offset.
/// Returns 0.0 if the offset is out of bounds (safe fallback for malformed packets).
pub fn read_f32(data: &[u8], offset: usize) -> f32 {