bandsaunter/bandsaunter
The Dust Council 872eadac37 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
2026-09-07 23:03:16 -07:00
..
__init__.py Read the parity the way the sensors write it 2026-09-07 23:03:16 -07:00
__main__.py Initial commit: bandsaunter, an RTL-SDR signal scanner 2026-08-21 20:50:20 -07:00
_quiet.py Initial commit: bandsaunter, an RTL-SDR signal scanner 2026-08-21 20:50:20 -07:00
acurite.py Read the parity the way the sensors write it 2026-09-07 23:03:16 -07:00
adsb.py Give the aerodromes a colour of their own, and say what each aircraft is 2026-09-04 22:45:41 -07:00
aircraft.py Read the weather sensors on 433 MHz, and let them be given names 2026-09-07 13:40:33 -07:00
announce.py Initial commit: bandsaunter, an RTL-SDR signal scanner 2026-08-21 20:50:20 -07:00
apt.py Read the pictures, the aircraft, the meters and the sensors 2026-08-29 19:38:37 -07:00
bandplan.py Name the band beside every frequency, and map who was heard 2026-08-28 11:02:56 -07:00
basemap.py Ask a service that knows which leg it is, and fetch a map worth the screen 2026-09-04 20:19:53 -07:00
browse.py Say what the terms are, and how to install under them 2026-09-03 22:58:18 -07:00
callsign.py Hear the short replies, and read the other kind of callsign 2026-09-02 00:30:02 -07:00
classify.py Read the stations that never say a word 2026-08-29 14:59:25 -07:00
cli.py Listen the way the tools that work on this band listen 2026-09-07 21:01:00 -07:00
config.py Draw the waterfall for everything that never spoke 2026-09-03 18:36:00 -07:00
decode.py Read the pictures, the aircraft, the meters and the sensors 2026-08-29 19:38:37 -07:00
demod.py Tune SSB to the carrier, and read the sideband from the signal 2026-08-21 23:09:44 -07:00
device.py Initial commit: bandsaunter, an RTL-SDR signal scanner 2026-08-21 20:50:20 -07:00
dsp.py Initial commit: bandsaunter, an RTL-SDR signal scanner 2026-08-21 20:50:20 -07:00
fax.py Read the pictures, the aircraft, the meters and the sensors 2026-08-29 19:38:37 -07:00
flags.py A sharper map, and aircraft that fade rather than blink out 2026-09-04 19:04:09 -07:00
flightlog.py Give the aerodromes a colour of their own, and say what each aircraft is 2026-09-04 22:45:41 -07:00
flightmap.py Make the aircraft breathe, and send a ring out from each of them 2026-09-06 17:03:16 -07:00
flights.py Give the aerodromes a colour of their own, and say what each aircraft is 2026-09-04 22:45:41 -07:00
images.py Ask a service that knows which leg it is, and fetch a map worth the screen 2026-09-04 20:19:53 -07:00
ism.py Read the weather sensors on 433 MHz, and let them be given names 2026-09-07 13:40:33 -07:00
kml.py Read the pictures, the aircraft, the meters and the sensors 2026-08-29 19:38:37 -07:00
librtlsdr.py Initial commit: bandsaunter, an RTL-SDR signal scanner 2026-08-21 20:50:20 -07:00
livemap.py Make the aircraft breathe, and send a ring out from each of them 2026-09-06 17:03:16 -07:00
morse.py Read the Morse a station sends over its own carrier 2026-09-02 07:57:18 -07:00
payload.py Read the pictures, the aircraft, the meters and the sensors 2026-08-29 19:38:37 -07:00
pictures.py Read the pictures, the aircraft, the meters and the sensors 2026-08-29 19:38:37 -07:00
protocols.py Decode data signals, starting with on-off keying 2026-08-28 12:55:37 -07:00
quality.py Read the stations that never say a word 2026-08-29 14:59:25 -07:00
ranges.py Remember runtime lock-outs, and let a lock-out be a span 2026-08-21 23:44:24 -07:00
recorder.py Draw the waterfall for everything that never spoke 2026-09-03 18:36:00 -07:00
scanner.py Never call a string of Morse tones "words" 2026-09-03 21:44:14 -07:00
schedules.py Ask a service that knows which leg it is, and fetch a map worth the screen 2026-09-04 20:19:53 -07:00
sensors.py Read the weather sensors on 433 MHz, and let them be given names 2026-09-07 13:40:33 -07:00
settings.py Draw the waterfall for everything that never spoke 2026-09-03 18:36:00 -07:00
simulator.py Read the pictures, the aircraft, the meters and the sensors 2026-08-29 19:38:37 -07:00
sstv.py Read the pictures, the aircraft, the meters and the sensors 2026-08-29 19:38:37 -07:00
themes.py Let the map brightness reach the map, and put the options in groups 2026-09-06 12:26:00 -07:00
transcribe.py Draw the waterfall for everything that never spoke 2026-09-03 18:36:00 -07:00
tui.py Read the weather sensors on 433 MHz, and let them be given names 2026-09-07 13:40:33 -07:00
ui.py Read the weather sensors on 433 MHz, and let them be given names 2026-09-07 13:40:33 -07:00
waterfall.py Write the aircraft down, and draw where they went 2026-09-03 22:22:49 -07:00
weather.py Say which check failed, and read a byte from either end 2026-09-07 21:36:35 -07:00
weatherlog.py Read the weather sensors on 433 MHz, and let them be given names 2026-09-07 13:40:33 -07:00