Skip to content

fix(otasim): two-GUI handshake completes + sim-mode QoL fixes#29

Merged
secup merged 2 commits into
mainfrom
fix/otasim-gui-quality-of-life
May 18, 2026
Merged

fix(otasim): two-GUI handshake completes + sim-mode QoL fixes#29
secup merged 2 commits into
mainfrom
fix/otasim-gui-quality-of-life

Conversation

@secup
Copy link
Copy Markdown
Owner

@secup secup commented May 18, 2026

Summary

Two-station ultra_gui -sim instances pointed at a single
ota_simulator serve daemon now complete the full
PING → PONG → CONNECT → CONNECT_ACK → MODE_CHANGE → CONNECTED handshake
and exchange OFDM-CHIRP DQPSK R1/4 ACK traffic in-session.

Five bugs fixed across two commits:

`f0ad88c` — sim-mode quality-of-life:

  • `--monitor-audio` flag for `ultra_gui` — plays OTASim RX through a
    local SDL output device so the simulator sounds like a real radio.
  • Short-circuit Hamlib rig open/poll in `-sim` mode — stops the
    `port_open: serial_open(...) status=-6` spam when no radio is
    attached.
  • `--log-file` now captures every category for the entire session
    (previously `initLog()` clobbered the destination after ~12 lines).

`1f215a7` — handshake completes end-to-end:

  • OtaAudioBackend RX cap dropped from 20 s → 480 ms. Behaves like a
    real soundcard's bounded driver buffer; consumer stall drops oldest
    instead of accumulating multi-second latency.
  • In `-sim` mode, modem callsign is forced to `--station-id` so
    inbound frames addressed to ALPHA/BRAVO aren't dropped by
    `deliverFrame()` as "different station".

ACK diversity (each ACK repeated ~2x ~440 ms apart) and CONNECT_ACK
rescue retry (one proactive resend) are intentional HF robustness
features, visible-but-harmless on OTASim's clean AWGN. Documented in
CHANGELOG with link to the in-source rationale.

Test plan

  • `cmake --build build -j4` clean
  • `ctest --test-dir build -R "Otasim|UltraGuiOta|UltraTncSimAudio|SessionContext" --output-on-failure -j1` → all pass
  • Manual two-GUI QSO over OTASim localhost: both stations reach
    state 3 (CONNECTED), MODE_CHANGE negotiated, in-session ACKs
    decode in OFDM control profile
  • Hardware audio path (SDL Mac↔Pi5 cable) untouched — only the
    OTASim client backend cap and GUI sim-mode wiring changed
  • CI: Linux / macOS / Windows full matrix

🤖 Generated with Claude Code

secup and others added 2 commits May 18, 2026 06:57
1. --monitor-audio flag for ultra_gui

   Add `--monitor-audio` (+ optional `--monitor-device <name>`) to
   ultra_gui. When -sim is on, opens an SDL output device and tees the
   received OTASim RX audio into local playback. Restores the "feels
   like a real sim" experience that the in-process loopback used to
   provide before the OTASim refactor.

   Queue depth is capped at ~500ms (24000 samples / 96000 bytes); on
   overflow we clear and resync rather than letting latency build.

2. Short-circuit PTT/Hamlib in sim mode

   In sim mode there is no radio, so the Hamlib serial-open spam ("port
   open: serial_open(/dev/cu.usbserial-…) status=-6") that polled every
   ~1.5s when a CAT preset was saved in settings was pure noise.
   ensurePttReadyLocked() and updateWaterfallFrequencyDisplay() now
   bail early when simulation_enabled_ is set, releasing any existing
   ptt_driver_ and skipping the rig open / frequency poll.

3. --log-file actually captures the whole session

   initLog() in app.cpp was unconditionally calling
   ultra::setLogFile(g_gui_log_file) after opening logs/gui.log, which
   clobbered whatever --log-file had set in main_gui.cpp. The result
   was 12 lines of modem init in the chosen log file, then everything
   silently rerouted to logs/gui.log. initLog() now adopts ultra::g_log_file
   if it is already set externally, so --log-file captures every
   category for the entire session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three independent bugs were compounding to block the OTASim two-GUI
connect handshake. With the fix, ALPHA + BRAVO ultra_gui instances
pointed at a single ota_simulator serve daemon now complete the full
PING / PONG / CONNECT / CONNECT_ACK / MODE_CHANGE / CONNECTED sequence
and exchange OFDM-CHIRP DQPSK R1/4 ACK traffic in-session.

1. OtaAudioBackend client RX cap was 20 s

   kMaxRxBufferSamples = 960000 at 48 kHz allowed up to 20 seconds of
   audio to silently accumulate when the GUI render loop briefly
   stalled (waterfall spike, ImGui frame). Real audio frames then sat
   behind multi-second silence, well past the ARQ window and the
   modem's sync-search budget. Lowered to 23040 samples = 480 ms,
   which mirrors a real soundcard's bounded driver buffer (continuous
   in, consumer drains at real-time rate, oldest drops on stall).
   ~8x a 60 Hz render budget so typical jitter is absorbed without
   building multi-second latency.

   Also added opt-in `#ifdef ULTRA_OTASIM_AUDIO_DIAGNOSTICS` queue-
   depth counters in OtaAudioBackend for future similar work. Off
   by default.

2. Modem callsign defaulted to 8P9QC even in -sim mode

   GUI's `Connect to <remote>` passes the OTASim --station-id as the
   destination callsign in the frame header. The modem's local
   callsign was loaded from settings.callsign (default "8P9QC") so
   ALPHA's modem saw frames addressed to "ALPHA" with local_call=
   "8P9QC", classified them as "different station", and dropped them
   silently at TRACE level. LDPC reported 3/3 CWs decoded OK; the
   frame never reached deliverFrame()'s INFO log and never reached
   the protocol layer.

   In -sim mode with a non-empty --station-id, force the modem's
   local callsign and protocol callsign to the station id so peer-
   addressed frames are accepted.

3. --log-file was overridden by initLog()

   App::initLog() unconditionally called ultra::setLogFile(g_gui_log_file)
   after opening logs/gui.log, clobbering whatever main_gui.cpp had
   set from --log-file. The chosen per-station log file captured ~12
   lines of modem init, then went silent for the rest of the session.
   initLog() now adopts ultra::g_log_file if it is already set
   externally, so --log-file captures every category for the full
   session.

Test gate:

  ctest --test-dir build -R "Otasim|UltraGuiOta|UltraTncSimAudio|SessionContext"
    --output-on-failure -j1
  → 3/3 (or 4/4) pass; the new latency / soundcard-cap assertions in
  test_ultra_gui_ota_client.cpp validate idle backlog and round-trip
  latency on the in-process passthrough.

ACK diversity (each ACK sent ~2x ~440 ms apart) and CONNECT_ACK
rescue retry (one proactive resend) are intentional HF robustness
mechanisms. They are visible-but-harmless on OTASim's clean AWGN; the
receiver's SR-ARQ correctly de-duplicates at the base/bitmap level.
See selective_repeat_arq.cpp:1266 comment. Documented in CHANGELOG.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@secup secup merged commit 48ffcae into main May 18, 2026
5 checks passed
@secup secup deleted the fix/otasim-gui-quality-of-life branch May 18, 2026 11:50
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