Skip to content

modem: show the waterfall while transmitting - #175

Open
rafael2k wants to merge 1 commit into
mercuryv2from
tx-waterfall
Open

modem: show the waterfall while transmitting#175
rafael2k wants to merge 1 commit into
mercuryv2from
tx-waterfall

Conversation

@rafael2k

Copy link
Copy Markdown
Contributor

Every radio shows you your own signal on transmit — an IC-7300 or FT-710 does.
Mercury froze the display on key-up, because the spectrum slot was fed only
from the capture path.

Approach

Feed the same slot from the transmitted burst. Same slot, same lock, same
sequence counter
as RX, so the publisher thread and the on-the-wire spectrum
frame need no change at all. Only one writer is ever active — the link is half
duplex, so RX decode and TX modulation never overlap — and the FFT runs inline
under the lock exactly as it already does for RX.

Two details decide whether this actually looks like a waterfall:

  • Pacing. Publishing once per burst would paint a single line every 3.7 s
    on DATAC16. Instead it publishes from inside the drain loop that
    send_modulated_data already runs while the burst plays out, indexed by
    elapsed playout. The ring is filled far faster than it drains, so drain
    progress — not write progress — is what paces the display.
  • Frequency axis. Samples are taken at modem rate, which is what
    g_spectrum_sample_rate already reports, so the axis matches RX and the
    display does not jump scale on key-up.

Honours the existing waterfall switch: with the UI waterfall off there is no
publisher thread, so the modem skips the work rather than computing frames
nobody consumes.

The bug worth recording

g_spectrum_stats is opened lazily, and TX now gets there first — mercury
sends CALL before it has decoded anything. Omitting modem_stats_open() ran
the first burst of every connect over an unopened MODEM_STATS and hung the
connect at ~10 s.

Only the integration suite caught it. Build was clean and the unit suite
passed, because neither transmits. Worth remembering for anything touching the
TX path.

Two theories I chased first were both wrong and are recorded here so nobody
re-runs them: it is not a lock-order inversion (the instance lock is released
at modem.c:1116, well before the drain loop at 1160+, so the publish holds no
modem locks), and "don't compute an FFT under the spectrum lock" is advice the
RX path already ignores successfully at modem.c:2229.

Gate

unit All Tests Passed
integration 245.5 s vs a 245–246 s baseline
TSan 1 warning — the pre-existing hamlib debugmsgsave2; no mercury frames, 0 lock-order inversions

Caveat on TSan: that run is -x null idle RX, so it does not exercise the new
TX path. The ASan+UBSan CI job runs an integration session, which does.

Not yet verified visually — the waterfall should be watched on a real UI during
a transmit to confirm it scrolls and the scale is stable across key-up.

Every radio shows you your own signal on transmit; mercury froze the display
on key-up, because the spectrum slot was fed only from the capture path.

Feed it from the transmitted burst as well.  Same slot, same lock, same
sequence counter the RX path uses, so the publisher thread and the on-the-wire
spectrum frame need no change whatsoever.  Only one writer is ever active --
the link is half duplex, so RX decode and TX modulation never overlap -- and
the FFT runs inline under the lock exactly as it already does for RX.

Two details that decide whether this looks right:

  - Publishing once per burst would paint a single line every 3.7 s on
    DATAC16.  Instead it publishes from inside the drain loop that
    send_modulated_data already runs while the burst plays out, indexed by
    elapsed playout.  The ring is filled far faster than it drains, so drain
    progress -- not write progress -- is what paces the display.

  - Samples are taken at modem rate, which is what g_spectrum_sample_rate
    already reports, so the frequency axis matches RX and the display does not
    jump scale on key-up.

Gated by g_spectrum_enabled, the switch the RX FFT already uses, so the UI
checkbox and -W turn off both directions at once: one waterfall control, one
setter.  Unlike RX there is no second consumer to keep it alive -- the
channel-busy detector shares the RX FFT, but occupancy means nothing while we
are the ones occupying the channel.

The stats struct is opened lazily and TX now gets there first -- mercury sends
CALL before it has decoded anything -- so the open is done here too.  Omitting
it ran the first burst of every connect over an unopened MODEM_STATS, which
hung the connect; caught by the integration suite, which is the only gate that
transmits.

Gate: unit suite green; integration 245.1 s against a 245-246 s baseline;
mercury and the embedded fyne UI both build, go vet clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant