Draw the waterfall for everything that never spoke
Every capture that is not voice, or whose voice yields five characters or fewer of transcript, now gets a PNG of the waterfall it would have painted on screen: spectrogram from the IQ where it was kept, from the demodulated audio otherwise, captioned and labelled either way. The browser shows it in the picture panel, but only when there is no transcript, Morse or decoded data to show instead. bandsaunter waterfall [PATH...] [--all] [--redraw] [--min-chars N] draws them after the fact for recordings already on disk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
This commit is contained in:
parent
7e8b9b268d
commit
dee262e130
17 changed files with 1383 additions and 35 deletions
|
|
@ -1024,3 +1024,64 @@ def test_no_callsigns_means_no_map(tmp_path, monkeypatch):
|
|||
meta={"category": "voice"})
|
||||
assert main([str(tmp_path), "--kml"]) == 1
|
||||
assert not list(tmp_path.glob("*.kml"))
|
||||
|
||||
|
||||
# -- waterfalls --------------------------------------------------------------
|
||||
|
||||
def _with_waterfall(directory, mhz=146.94, when="2026-08-22_10_00_00",
|
||||
meta=None):
|
||||
"""A recording with a drawing of the signal beside it."""
|
||||
wav = make_capture(directory, mhz, when, "nfm",
|
||||
meta=meta or {"category": "digital",
|
||||
"classification": "OOK / ASK data burst"})
|
||||
(directory / (wav.stem + "_waterfall.png")).write_bytes(b"\x89PNG\r\n\x1a\n")
|
||||
return wav
|
||||
|
||||
|
||||
def test_a_waterfall_is_offered_where_there_is_nothing_to_read(tmp_path):
|
||||
"""A data burst has no transcript and no picture off the air, so the
|
||||
drawing of it is the only thing the panel can offer."""
|
||||
_with_waterfall(tmp_path)
|
||||
b = browser(tmp_path)
|
||||
cap = b.view[0]
|
||||
assert cap.waterfall.endswith("_waterfall.png")
|
||||
assert cap.picture_headline == "waterfall"
|
||||
out = frame(b)
|
||||
assert "waterfall" in out
|
||||
assert "_waterfall.png" in out
|
||||
|
||||
|
||||
def test_the_list_still_says_what_the_signal_was(tmp_path):
|
||||
""""waterfall" says less about a capture than "OOK / ASK data burst"
|
||||
does, and the summary column has room for one of them."""
|
||||
_with_waterfall(tmp_path)
|
||||
listing = frame(browser(tmp_path)).split("recordings in")[1]
|
||||
assert "OOK / ASK data burst" in listing
|
||||
|
||||
|
||||
def test_a_picture_off_the_air_outranks_a_drawing_of_the_signal(tmp_path):
|
||||
"""One is a transmission and the other is a view of one."""
|
||||
wav = _with_waterfall(tmp_path, meta={"category": "image",
|
||||
"image_kind": "SSTV",
|
||||
"image_mode": "Martin M1"})
|
||||
(tmp_path / (wav.stem + ".png")).write_bytes(b"\x89PNG\r\n\x1a\n")
|
||||
cap = browser(tmp_path).view[0]
|
||||
assert cap.picture_headline.startswith("SSTV")
|
||||
assert cap.waterfall # still there, still listed
|
||||
assert any(p.endswith("_waterfall.png") for p in cap.images)
|
||||
|
||||
|
||||
def test_o_prints_the_drawing_when_there_is_nothing_else(tmp_path):
|
||||
"""There is no listening to a data burst."""
|
||||
_with_waterfall(tmp_path)
|
||||
b = browser(tmp_path)
|
||||
assert b.handle("o") is False
|
||||
assert b.message.endswith("_waterfall.png")
|
||||
|
||||
|
||||
def test_the_drawing_moves_and_is_deleted_with_the_recording(tmp_path):
|
||||
wav = _with_waterfall(tmp_path)
|
||||
b = browser(tmp_path)
|
||||
assert any(p.name.endswith("_waterfall.png") for p in b.view[0].files())
|
||||
b.handle("N")
|
||||
assert (tmp_path / "noise" / (wav.stem + "_waterfall.png")).exists()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue