Read the parity the way the sensors write it

Their sensors are on the air, five of them, and every message was arriving
intact.  The bytes they sent, recovered from their own capture:

    A7 1B 44 A6 09 4B 00     channel B  id 271B   22.7 C  38%
    F9 35 44 2B 09 C9 6F     channel A  id 3935   22.5 C  43%
    21 E5 84 1E 0A 9F 51     channel C  id 21E5   31.1 C  30%  battery low
    F9 7D 44 28 09 50 3B     channel A  id 397D   23.2 C  40%
    0D D6 44 A9 09 CF A8     channel C  id 0DD6   23.1 C  41%

Every checksum correct, every message type 0x04, every reading plausible.  The
framing was right, the bit offset was right, the byte order was right, the
pulses had been recovered perfectly for days.  One bit of convention was
wrong: the parity in the top bit of each payload byte is even, and this
required it to be odd.  Twenty payload bytes across five independent messages,
every one of them even, which is not something twenty bytes do by chance.

That is the whole fault.  Everything else changed in this and the two commits
before it was real and worth doing, and none of it was why nothing decoded.

Three things follow.

The five messages are now a test, checked byte for byte against the weather
they carry.  They are worth more than everything else in that file put
together: every other test there puts a reading in through an encoder written
from the same description as the decoder, so the two agree by construction and
agree about anything they are both wrong about -- which is exactly what
happened.  An encoder tested against its own decoder cannot find a fault in
the description they share, and no amount of it would ever have found this.

The emptiness check earns its place now.  Odd parity rejects a byte of all
zeroes; even parity accepts one, so a run of silence read as zeroes satisfies
both the parity and a sum of zero, and the only thing standing between that
and a display full of sensors is the test that some byte is non-zero.  It was
there for tidiness and is now load-bearing; the comment says so.

And the readings of a burst are tried in order and the search stops at the
first that yields anything, rather than pooling them.  Half a dozen readings
at two byte orders is sixteen times the chances for a coincidence to satisfy a
twelve-bit check, and sensors that were not there began appearing in the
invented garden the moment the alternatives went in -- caught by the test that
asks whether everything heard is something that exists.  Stopping early costs
nothing: a burst that reads correctly the ordinary way never reaches the
alternatives, and one that does not reaches them exactly as before.

Full suite 2351 passed, checked against three more deliberately broken builds.
Sixty seconds of receiver noise yields nothing and eight hundred seconds of
the invented garden yields no sensor that is not there.  Built as
2026-09-07_05.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
This commit is contained in:
The Dust Council 2026-09-07 23:03:16 -07:00
parent dfea5cb2f2
commit 872eadac37
6 changed files with 193 additions and 36 deletions

View file

@ -1906,9 +1906,13 @@ transmitting, with an identity that stays the same, is worth a line on its
own. `--no-unknown` leaves them off.
These formats are implemented from their published descriptions and are
checked against frames built from the same descriptions. That proves the
framing, the parity, the checksums and the arithmetic; it is not the same as
having held every one of these sensors.
checked against frames built from the same descriptions — which proves the
framing, the parity, the checksums and the arithmetic, and proves nothing
about anything the description and this both get wrong. The tower sensor is
also checked against five messages recovered from real hardware, byte for
byte, and those are worth more than all the rest put together: they are what
caught the parity, which no amount of testing an encoder against its own
decoder ever could.
### Why nothing false gets through
@ -1918,8 +1922,10 @@ looks at every bit offset of every burst will find a message in noise if it is
allowed to. Four things stop it.
**The checks the message carries.** The three newer models have an eight-bit
sum plus odd parity in the top bit of every payload byte — twelve to fourteen
bits of check on a message of seven to nine bytes.
sum plus **even** parity in the top bit of every payload byte — twelve to
fourteen bits of check on a message of seven to nine bytes. Even, not odd:
that one bit of convention was wrong here for four days and the cost was that
nothing decoded at all, every other check passing and saying so.
**Arriving twice.** The two older models carry one byte of check between them,
which is one false message in two hundred and fifty-six, and that is not a
@ -1931,6 +1937,16 @@ times in a row, for exactly this reason.
hardware could not have sent. Nothing outside −40 to 70 °C, 0 to 100% or a
wind the anemometer cannot physically report is accepted.
**How many readings are tried.** The readings of a burst are tried in order of
likelihood and the search stops at the first that yields anything, rather than
pooling all of them. Half a dozen readings at two byte orders is sixteen times
as many chances for a coincidence to satisfy a checksum, and twelve bits of
check is a rate that is comfortable once and uncomfortable sixteen times over
— sensors that were not there began appearing the moment the alternatives went
in. Stopping early costs nothing: a burst that reads correctly the ordinary
way never reaches the alternatives, and one that does not reaches them exactly
as before.
**Where the message sits.** This is the one that is easy to get wrong. A
seven-byte message read out of the front of a real eight-byte one is made of
that message's own payload bytes, whose parity is *already correct* — so the