Name the band beside every frequency, and map who was heard

Two additions, both about turning a number into something meaningful.

A band column.  Next to every frequency -- on the live display, in the
line-per-hit output, in saunterbrowse's list and details -- is the name of
the band it falls in.  421 MHz is the 70 cm amateur band, and being told
so is quicker than remembering where the edges are.

The names come from the existing preset table, so there is one band plan
to keep right rather than two, but naming is not the job that table was
shaped for: several presets cover any frequency, some of them whole-tuner
sweeps that say nothing.  So the candidates are ranked.  Sweeps and the
"-complete" duplicates are dropped outright.  The narrowest of what is
left wins, because it says the most -- 146.52 MHz comes back as the 2 m
simplex calling channel rather than as the whole 2 m band.  Two exceptions
where the narrowest would be the wrong answer: ISM yields to the
allocation it shares (433.92 is 70 cm first, 915 is 33 cm first), and
shortwave broadcast yields to amateur where the two overlap, because
3.9-4.0 and 7.2-7.3 MHz are Region 1 and 3 broadcast but Region 2 amateur,
and this plan is documented as Region 2.  6 MHz really is 49 m shortwave
and is left alone.

The name is written into each capture's sidecar, so it travels with the
recording and an edit to the plan later cannot rewrite history, and
saunterbrowse searches on it: /70 cm finds the band without anyone having
to remember 420-450 MHz.

A map.  A licence says where its holder is, so a list of callsigns is also
a map.  Callsigns heard during a scan are now looked up as the transcripts
come in, announced on the display, and written to callsigns.kml in the
output directory; saunterbrowse --kml builds the same file from recordings
already on disk, and the two continue one map rather than starting two.

One placemark per station, not one per transmission: the same repeater
heard twenty times in an evening is one operator, and twenty pins on one
rooftop would say less than one.  Each pin carries the callsign, the
licensee, the town, the grid square, and every frequency and time it was
heard on.  The file is read back on open and added to, so later scans
build it up rather than replacing it.

Where a licence has no coordinates the grid square's centre is used and
the placemark says so -- a square is kilometres across where an address is
a street.  A callsign with no licence at all is still recorded, in a
folder that starts switched off, because that a station was heard is worth
keeping even when nothing says where.  A file already there that is not
readable as KML is never overwritten.

Also fixed along the way:

- The hit list's "no signals recorded yet" placeholder was one cell short
  of its row, so it landed in the SNR column and wrapped, making the panel
  taller than the layout had budgeted for and scrolling the display off a
  short terminal.  The identification column can no longer wrap either,
  which is what _hit_capacity has always assumed.

- Licence lookups now record coordinates.  The cache is versioned so that
  entries written before this are asked about again, rather than pinning
  every station to its grid square for good.

- CallsignBook.wait dropped joined threads; an all-night scan calls it
  after every transcript and the list only ever grew.

- Tests redirect XDG_CACHE_HOME, so a run no longer reads or writes the
  real lookup cache.

676 -> 761 tests.
This commit is contained in:
The Dust Council 2026-08-28 11:02:16 -07:00
parent 739a2faaf4
commit fb2bb3344b
23 changed files with 2155 additions and 42 deletions

View file

@ -583,6 +583,41 @@ Each result carries a confidence and the reasoning behind it:
Low SNR reduces confidence rather than producing a confident wrong answer.
### Which band it is in
Next to every frequency, on the live display and in the line-per-hit output,
is the name of the band it falls in:
```
time frequency band dur SNR identified as
21:14:07 146.52 MHz 2 m FM Simplex 5.0s 20.0 Narrowband FM voice
21:14:31 462.5625 MHz GMRS / FRS 4.2s 18.3 Narrowband FM voice
21:15:02 421 MHz 70 cm Amateur 12.7s 22.9 Narrowband FM voice
21:15:40 162.55 MHz NOAA Weather Radio 30.0s 31.4 Narrowband FM voice
```
`421 MHz` is the 70 cm amateur band, and being told so is quicker than
remembering where the band edges are. The names come from the same band plan
the presets do, so there is one table to keep right rather than two.
Several allocations usually cover any given frequency, and the narrowest wins
because it says the most: `146.52 MHz` comes back as *2 m FM Simplex* rather
than *2 m Amateur*, and `14.050 MHz` as *20 m CW / Digital*. Two exceptions,
both because the obvious answer would be the wrong one:
- **ISM yields to the allocation it shares.** 433.92 and 915 MHz are ISM
bands, but they are also 70 cm and 33 cm. A signal there is far more likely
to be worth naming as the amateur band, so it is — unless nothing else
covers it, in which case ISM is still the right answer.
- **Shortwave broadcast yields to amateur, where they overlap.** 3.94.0 and
7.27.3 MHz are broadcast in ITU Regions 1 and 3, and amateur in Region 2,
which is what this plan describes. 6 MHz really is 49 m shortwave, and
there is no amateur band anywhere near it, so that one is left alone.
The band name is written into each recording's sidecar too, so it travels with
the capture, and `saunterbrowse` will search on it — typing `/70 cm` finds
everything in the band without having to remember 420450 MHz.
### Trunked systems and their control channels
Police, fire and most large business radio in the US runs on *trunked*
@ -1058,6 +1093,39 @@ there is — callook.info holds US licences only.
US amateur licence records are public by law and include the licensee's
address; that is what is shown.
### The map
A licence says where its holder is, so a list of callsigns is also a map. The
scanner writes one as it runs — `callsigns.kml` in the output directory —
which opens in Google Earth, QGIS, Marble or OsmAnd:
```bash
saunterbrowse --kml # build one from recordings already on disk
saunterbrowse --kml ~/heard.kml # or somewhere else
bandsaunter scan --kml "" # turn it off
```
Each station is **one placemark, not one per transmission**. Hearing the same
repeater twenty times in an evening is one operator, and twenty pins stacked on
the same rooftop would say less than one. The pin carries the callsign, the
licensee, the town, the grid square, and every frequency and time you heard
them, so clicking it answers "when did I hear this, and where on the dial".
The file is added to rather than replaced — by later scans, and by
`saunterbrowse --kml` over the same directory — so over a few weeks it stops
being a snapshot of one evening and becomes a picture of what your aerial can
actually reach.
Where a licence carries no coordinates the grid square is used instead, and the
placemark says so: a grid square is kilometres across where a licensed address
is a street. A callsign with no licence on file at all is still recorded, in a
folder named *no location on file* which starts switched off — that a station
was heard is worth keeping even when nothing says where it was.
It is XML, written atomically, so a scan interrupted halfway through leaves a
file that still opens. A file already there that is *not* readable as KML is
never overwritten.
### Searching what was said
Searching the transcripts is the point of it: *"did anyone mention the