Two things, both found by measuring rather than by reading the code.
The voice-activity filter inside the recogniser is off. It was costing
words: across a night of land-mobile captures it dropped 5-15% of what
the same model finds without it -- 491 against 507, 339 against 384,
263 against 310 -- because a single-word over between two
transmissions looks to a VAD exactly like the noise it exists to
remove, and on a scanner those short replies are the ones worth
having.
Turning it off has a cost, and the cost is that Whisper hands back
"You" for five seconds of hiss as confidently as it hands back a
sentence. So the whole capture is now asked once whether anything in
it rises above its own noise. Digital silence measures 0.0 dB of
contrast and hiss at any level 0.7, while the quietest real capture of
that night measures 8.9 and most measure 10-27; the bar sits at 3, an
order of magnitude clear of both. It can veto a capture but never trim
one, which is the whole difference between it and the filter it
replaces.
The second thing: callsigns like WQVF960 were being missed entirely.
The shape being matched was the amateur one -- prefix, district digit,
suffix -- and everything else the FCC licenses is written the other
way round, the letters first and then the digits. On the GMRS and
business channels that is most of what is said: nine callsigns across
five transcripts of one evening went by unrecognised, and now do not.
The shape is written as the three allocations that exist rather than
as "letters then digits", which claims KN95, WD40 and KC135. Its
letters are checked against the word list even when they arrive as a
single token, which the amateur shape does not need -- no English word
has a digit in the middle of it, but "west 120" and "word 100" fit
this one exactly.
Lookups now fall back to hamdb.org when callook has nothing. Not a
spare copy: callook holds United States amateur licences only, so
DL1ABC and VE3ABC are INVALID there and resolve perfectly well from
the other. And a GMRS callsign is not looked up at all -- every
database reachable without an account is an amateur register, so
reporting WQVF960 as "unlisted" would blame the callsign for the
absence of a source.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
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.