Frame the map on the receiver, and throw out what never happened

A first real capture came back as a map spanning 240 degrees north to 20
south, with the aircraft an indistinguishable smudge in one corner.  Two
separate faults, one of them mine from the start.

A position is sent as half a position -- an even frame and an odd one --
and the pair only means anything while the aircraft has not moved between
them.  The registry kept the last of each forever and paired them
regardless of age, so an even frame from ten minutes ago decoded against
a fresh odd one to a place on the wrong side of the world.  Measured: a
pair 300 seconds apart puts the aircraft 2,566 nm from where it is, and
one night's log had it happening to two aircraft in three, with eleven
positions off the planet altogether.  A pair is now good for ten seconds,
the answer has to be on Earth, and the aircraft has to have been able to
reach it.

--radius, defaulting to a hundred, frames the picture on the receiver
rather than on whatever was heard, so the scale is the same from one
evening to the next.  In the same unit as the speeds.  The centre is the
median of everything heard, which a handful of wrong positions cannot
move, or --at LAT,LON says where the aerial is.

--recheck repairs a log recorded before all this: for each aircraft it
keeps the longest run of positions that could describe one aeroplane.
Not a forward walk dropping whatever disagrees with the last position
kept -- that lets one bad fix become the reference, and on the same log
it discarded a fifth of everything, most of it the truth.

Two calibrations came from the recording rather than from taste.  The
failures separate cleanly -- a hundred artefacts under a mile, twelve
hundred real errors over fifty, and nothing in between -- because
positions are stamped to the millisecond, so two a thousandth of a second
apart imply thousands of knots across a few yards.  Nothing under two
miles is called an error.  Afterwards the worst surviving jump is 513 kt.

One rendering bug the tighter frame exposed: an aircraft just off the top
left painted 743,774 pixels of an 844,200-pixel picture, because the dot
at a marker's centre clamped its near edge and left the far one alone, and
numpy reads a negative slice end as counting back from the far side.  And
a still of a whole evening was dead-reckoning every aircraft forward to
the final moment, which for a ten-hour log flew 291 of 335 clean off the
picture; a still now draws each where it was last actually heard.

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-04 12:25:39 -07:00
parent 96fc21ac7d
commit e50d43d6e2
13 changed files with 999 additions and 36 deletions

View file

@ -1,5 +1,5 @@
.\" Generated by packaging/make-man.py -- do not edit by hand.
.TH BANDSAUNTER 1 "2026-09-04" "bandsaunter 2026-09-04_01" "User Commands"
.TH BANDSAUNTER 1 "2026-09-04" "bandsaunter 2026-09-04_02" "User Commands"
.SH NAME
bandsaunter \- scan, record and identify radio signals with an RTL-SDR
.SH SYNOPSIS
@ -1380,6 +1380,42 @@ where ffmpeg is installed, or a
for the whole evening in one picture. Altitude is the colour, low warm to high
cold. The GIF is written from first principles \[em] a palette, an LZW stream
and frame differencing \[em] so nothing but numpy is needed to draw one.
.SS How far the map reaches
The picture is framed on the receiver rather than on whatever was heard. An
aerial reaches a hundred miles on a good day and a position that decoded
wrongly can land anywhere on Earth, so a map drawn to fit everything heard is
drawn to fit the mistakes: the aircraft come out a pixel wide in the middle of
an empty continent.
.PP
.BI \-\-radius " MILES"
is how far the map reaches, in the same unit as the speeds, and defaults to a
hundred; zero goes back to fitting whatever turned up. The centre is the
median of everything heard \[em] a receiver hears aircraft all round it, and a
median cannot be dragged anywhere by a handful of bad positions \[em] or
.BI \-\-at " LAT,LON"
says where the receiver is, which is worth doing to keep the same frame every
night. Positions outside the radius are left off the drawing, one at a time
rather than one aircraft at a time, so a single bad fix in the middle of a
real flight does not take the flight with it. Nothing is dropped from the log.
.SS Positions that never happened
A position is sent as half a position \[em] an even frame and an odd one \[em] and
the pair only means anything while the aircraft has not moved between them.
Logs written before this version paired them however old they were, so an even
frame kept from ten minutes ago decoded against a fresh odd one to a place on
the wrong side of the world and wrote it down as confidently as a real one.
.PP
.B \-\-recheck
reads such a log back and keeps, for each aircraft, the longest run of
positions that could describe one aeroplane. It is deliberately not a forward
walk dropping whatever disagrees with the last position kept: one bad fix then
becomes the reference and it is the truth that gets discarded. Nothing in the
log is changed.
.PP
An aircraft that goes quiet for five minutes and is heard again a long way off
is an aeroplane rather than an error, and is not second-guessed; the radius is
what keeps those off the picture. New logs need none of this, as the decoder
now refuses a stale pair, a position that is not on Earth, and one the
aircraft could not have reached, as the frames arrive.
.SS The ground under it
A real map is drawn under the aircraft: standard {z}/{x}/{y} raster tiles,
OpenStreetMap by default, fetched the first time an area is drawn and

View file

@ -773,6 +773,42 @@ where ffmpeg is installed, or a
for the whole evening in one picture. Altitude is the colour, low warm to high
cold. The GIF is written from first principles \[em] a palette, an LZW stream
and frame differencing \[em] so nothing but numpy is needed to draw one.
.SS How far the map reaches
The picture is framed on the receiver rather than on whatever was heard. An
aerial reaches a hundred miles on a good day and a position that decoded
wrongly can land anywhere on Earth, so a map drawn to fit everything heard is
drawn to fit the mistakes: the aircraft come out a pixel wide in the middle of
an empty continent.
.PP
.BI \-\-radius " MILES"
is how far the map reaches, in the same unit as the speeds, and defaults to a
hundred; zero goes back to fitting whatever turned up. The centre is the
median of everything heard \[em] a receiver hears aircraft all round it, and a
median cannot be dragged anywhere by a handful of bad positions \[em] or
.BI \-\-at " LAT,LON"
says where the receiver is, which is worth doing to keep the same frame every
night. Positions outside the radius are left off the drawing, one at a time
rather than one aircraft at a time, so a single bad fix in the middle of a
real flight does not take the flight with it. Nothing is dropped from the log.
.SS Positions that never happened
A position is sent as half a position \[em] an even frame and an odd one \[em] and
the pair only means anything while the aircraft has not moved between them.
Logs written before this version paired them however old they were, so an even
frame kept from ten minutes ago decoded against a fresh odd one to a place on
the wrong side of the world and wrote it down as confidently as a real one.
.PP
.B \-\-recheck
reads such a log back and keeps, for each aircraft, the longest run of
positions that could describe one aeroplane. It is deliberately not a forward
walk dropping whatever disagrees with the last position kept: one bad fix then
becomes the reference and it is the truth that gets discarded. Nothing in the
log is changed.
.PP
An aircraft that goes quiet for five minutes and is heard again a long way off
is an aeroplane rather than an error, and is not second-guessed; the radius is
what keeps those off the picture. New logs need none of this, as the decoder
now refuses a stale pair, a position that is not on Earth, and one the
aircraft could not have reached, as the frames arrive.
.SS The ground under it
A real map is drawn under the aircraft: standard {z}/{x}/{y} raster tiles,
OpenStreetMap by default, fetched the first time an area is drawn and

View file

@ -1,5 +1,5 @@
.\" Generated by packaging/make-browse-man.py -- do not edit by hand.
.TH SAUNTERBROWSE 1 "2026-09-04" "bandsaunter 2026-09-04_01" "User Commands"
.TH SAUNTERBROWSE 1 "2026-09-04" "bandsaunter 2026-09-04_02" "User Commands"
.SH NAME
saunterbrowse \- read and listen to what a bandsaunter scan collected
.SH SYNOPSIS