From 83a8d0e9486e7bd40fb1a7ca4cec41eb6a3a13ea Mon Sep 17 00:00:00 2001 From: Benjamin Larsson Date: Sun, 19 Jul 2026 03:15:21 +0200 Subject: [PATCH] Improve Schrader-MRXBC5A4 false-positive rejection (issue #3611) The 16-bit wake+sync+start header was assumed rather than checked, so any 61-bit noise burst that happened to pass the weak 2-bit parity check (~1/4 of random payloads) would decode. All 4 real captures share the identical 16-bit prefix 0111111111111111, so verify it via bitbuffer_search() instead. Also reject bitbuffer->num_rows != 1, and tighten the temperature sanity bound from the sensor chip's own -40/+125 C survival rating down to 85 C, a realistic tire temperature. Verified byte-for-byte identical output on all 4 real captures. --- src/devices/schraeder.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/devices/schraeder.c b/src/devices/schraeder.c index 48260705..92b9929c 100644 --- a/src/devices/schraeder.c +++ b/src/devices/schraeder.c @@ -359,6 +359,11 @@ Packet structure (61 bits): - C: 2 bits integrity check (C1, C2) - T: 8 bits temperature offset by 50, range -50 to 205 degrees C +The leading wake+sync+start (W SSSSSSSSSSSSS s) is a fixed 16-bit +`0111111111111111` in every real capture seen so far (issue #3611: the +2-bit integrity check alone lets through ~1 in 4 noise bursts, verifying +this fixed prefix rejects far more of them). + Integrity check (C1C2): The 2-bit integrity value is computed over the 35-bit payload (III+PPP+CC), i.e. id, pressure, and the check bits themselves, but not the flags: @@ -391,11 +396,21 @@ static int schrader_MRXBC5A4_decode(r_device *decoder, bitbuffer_t *bitbuffer) unsigned int pressure; // kPa int temperature; // degree C + if (bitbuffer->num_rows != 1) + return DECODE_ABORT_EARLY; + /* Check for incorrect number of bits received */ if (bitbuffer->bits_per_row[0] != 61) return DECODE_ABORT_LENGTH; - /* Discard the first 15 bits (1 wake + 13 sync + 1 start) */ + /* Verify the fixed 16-bit wake+sync+start prefix instead of assuming it */ + uint8_t const sync_pattern[] = {0x7f, 0xff}; + if (bitbuffer_search(bitbuffer, 0, 0, sync_pattern, 16) != 0) { + decoder_log(decoder, 2, __func__, "sync pattern not found"); + return DECODE_ABORT_EARLY; + } + + /* Discard the first 16 bits (wake + sync + start) */ bitbuffer_extract_bytes(bitbuffer, 0, 16, b, 46); /* Get data fields: @@ -442,10 +457,13 @@ static int schrader_MRXBC5A4_decode(r_device *decoder, bitbuffer_t *bitbuffer) temperature = ((b[4] & 0x03) << 5) | (b[5] >> 3); /* The 2-bit integrity check above only rejects 3 out of 4 random/corrupt - payloads. Add a plausibility bound on pressure and temperature (a - generous margin around real TPMS operating ranges) as a second, - independent filter for the noise the parity check lets through. */ - if (pressure > 450 || temperature - 50 < -40 || temperature - 50 > 125) { + payloads. Add a plausibility bound on pressure and temperature as a + second, independent filter for the noise the parity check lets + through. Temperature is bounded to a real tire's operating range + (well under the sensor chip's own -40/+125 C survival rating -- a + tire never actually reaches that in normal use) rather than the + full field range. */ + if (pressure > 450 || temperature - 50 < -40 || temperature - 50 > 85) { decoder_logf(decoder, 2, __func__, "implausible pressure/temperature: %u kPa, %d C", pressure, temperature - 50); return DECODE_FAIL_SANITY;