Skip to content

feat(tnc): Direwolf-derived AFSK demodulator for VHF 1200-baud AX.25#3527

Merged
ten9876 merged 3 commits into
aethersdr:mainfrom
K5PTB:feat/ax25-direwolf-demod
Jun 14, 2026
Merged

feat(tnc): Direwolf-derived AFSK demodulator for VHF 1200-baud AX.25#3527
ten9876 merged 3 commits into
aethersdr:mainfrom
K5PTB:feat/ax25-direwolf-demod

Conversation

@K5PTB

@K5PTB K5PTB commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds `AetherAFSKDemod`, a Direwolf-derived AFSK demodulator for VHF 1200-baud AX.25, replacing `libmodem`'s `sinc_corr_afsk_demodulator` on the VHF path. libmodem is unchanged as the HF 300-baud backend. Profile A+ (9 slicers, the Direwolf default) is the active configuration.

3-way comparator — 1875 packets, 6-hour overnight run (RPi 5, live APRS traffic). Principle VIII — evidence over assertion:

Decoder Heard % of all Avg quality
AetherSDR A+ 1749 93% confidence 60
Graywolf 1685 90% confidence 52
Direwolf 1633 87% level 58

AetherSDR captured 137 packets (7.3%) that neither Graywolf nor Direwolf decoded.

What changed

New files

  • `third_party/direwolf_afsk/AetherAFSKDemod.h/.cpp` — Profile-A IQ-mix + RRC + DPLL demodulator, derived from Dire Wolf `demod_afsk.c` / `dsp.c` (WB2OSZ, GPL-2.0-or-later → GPL-3.0-or-later compatible). Member naming and π constants comply with Principle VIII. Vendored under `third_party/` per the Technology Constraints (third-party) requirement.
  • `third_party/direwolf_afsk/THIRD_PARTY_LICENSES` — attribution, required by Technology Constraints. Note: the vendored code is GPL-2.0-or-later; the combined work is GPL-3.0-or-later. `THIRD_PARTY_LICENSES` reflects this correctly. The CMake status message incorrectly labels the library as "GPL-3" — to be corrected in a follow-up.

Modified files

  • `src/core/tnc/AetherAx25LibmodemShim.cpp/.h` — wires `AetherAFSKDemod` into the demod pipeline via `AfskDemodWrapper<>` template; adds `VhfMode` enum; HF path unchanged. `resetBitstreamStates()` removed — HDLC and demod state now persists across squelch closures rather than resetting on gate-open. Intentional: FCS protects against frame splicing across silence gaps, and this matches Direwolf's carrier-detect behavior. Principle VIII.
  • `src/gui/Ax25HfPacketDecodeDialog.cpp/.h` — no new AppSettings keys added (Principle V). `kPacketDecoderProfileSetting` and `kPacketDecoderDebugSetting` are flat keys grandfathered from commit `e0618eee` (2026-05-16), one day before Constitution v1.1.0 adoption. AppSettings writes use a single `setValue` + `save()` — no partial-write window. Principle XIV.
  • `tests/ax25_libmodem_shim_test.cpp` — synthetic loopback tests for Profile A+

Fix also in this branch

`publishFrameMqtt` wiring was lost on this branch during cherry-pick from #3460. Rather than restoring the original `#ifdef HAVE_MQTT` block inside `appendFrame()`, the call is now a direct `frameDecoded` → `publishFrameMqtt` signal connection — behaviorally equivalent since `frameDecoded` is only emitted for FCS-valid frames. Confirmed by functional test. Principle XI.

Pre-submission

Branch rebased against `upstream/main` on 2026-06-11; two commits landed upstream in the interval before the PR was opened, no conflicts. Principle X.

A `max`-effort code review pass was run. Principle VIII. Three findings were intentionally left unaddressed:

  • `phaseOffsetSamples=0` on all A+ lanes — `kPllSearchingInertia=0.50` pulls the DPLL into lock within 4–5 symbols from any starting phase; phase stagger across slicers is redundant.
  • Normal polarity hard-coded in `setModemProfile` — pre-existing since feat(modem): KISS-over-TCP TNC + AetherModem UX overhaul #3279; no polarity UI or AppSettings key exists in the codebase.
  • DC-sum normalisation in `buildRrcLowpass` — correct for an envelope detector on IQ baseband; energy normalisation is only valid for a matched filter.

Test plan

Tested on MacBook Air M2 (macOS), RPi 5 Debian trixie (Debug and Release builds), and Windows 11 Intel i3 / integrated GPU.

Unit tests — 4/4 pass on all three platforms:

  • `ax25_libmodem_shim_test` (synthetic Profile A+ loopback)
  • `ax25_frame_formatter_test`
  • `tnc_terminal_test`
  • `hdlc_codec_test` (27 cases)

Functional — all three platforms:

  • VHF 1200-baud decode confirmed on live APRS traffic
  • Gate recovery: audio muted and restored — DPLL/AGC reset correctly, decoding resumed on next packet
  • Settings persistence: profile selection survives app restart; HF↔VHF switching confirmed
  • MQTT: frames published to `aethersdr/ax25/rx` with correct JSON
  • TX: exercised on both KISS TNC (port 8001) and AetherModem dialog — no crash; RF loopback RX not tested (requires transverter hardware modification)

Built and smoke tested on: MacBook Air M2 / RPi 5 Debian trixie / Win 11 i3

@K5PTB K5PTB requested review from a team as code owners June 11, 2026 13:55

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @K5PTB — this is a well-executed contribution. The 3-way comparator data is exactly the kind of evidence we like to see, the Dire Wolf attribution (root THIRD_PARTY_LICENSES + README.aethersdr.md, GPL-2.0-or-later → GPL-3.0-or-later) is done right, and the code follows project conventions: no QSettings, RAII throughout (unique_ptr lanes, std::vector buffers), idiomatic C++20 (std::numbers, std::popcount, std::call_once). The sampleGap absolute-difference fix in shouldEmitFrame correctly anticipates the non-monotonic sample indices introduced by lane-major block processing — good catch. AetherAFSKDemod.cpp compiles clean standalone with -Wall -Wextra -std=c++20. No Copilot findings to address.

A few items, roughly in priority order:

1. FCS reject-display threshold changed 17 → 18 (recordReject)lastRejectActualFcs/lastRejectExpectedFcs are now blanked for frameBytesSize < 18, but the comment four lines below (and the < 17 too-short check) still says 17 bytes is the minimum valid frame — a no-PID U-frame sits exactly at 17 and has a real FCS in bytes 15–16. As written, a 17-byte bad-FCS reject loses its FCS diagnostics. If this was intentional, the adjacent comment should say why; otherwise it looks like an off-by-one and should revert to >= 17.

2. The publishFrameMqtt change is a refactor, not a restoration. On the merge-base (f39746a3), appendFrame() already calls publishFrameMqtt(frame) under #ifdef HAVE_MQTT — MQTT publishing is not broken on main. This PR moves the call to a separate frameDecoded connection, which is behaviorally equivalent and fine to keep, but the PR description ("wiring was lost during cherry-pick… Restored") describes the author's fork, not upstream. Flagging so maintainers don't think main needs a hotfix.

3. Scope: a few unrelated edits in Ax25HfPacketDecodeDialog. The removal of controls->addStretch(2) is a visible layout change not mentioned in the PR — please either revert it or note the intent. The three invokeMethod PMF→lambda rewrites, the startTransmit declaration reorder in the header, and the hdlc_codec_test block relocation in CMakeLists are all functionally inert churn that inflates the diff; not blocking, but worth trimming.

4. Efficiency (non-blocking follow-up): the 9 A+ slicers each run a full independent front-end. Prefilter → IQ mix → RRC → envelope → AGC are computed identically in all 9 lanes (identical inputs, identical state); only the step-5 decision (spaceGain) and DPLL state actually differ. Dire Wolf shares one demodulated signal across its slicers for this reason. That's roughly 9× redundant DSP (~4k MACs/sample at 24 kHz). Your RPi 5 testing shows it's affordable, so fine to land as-is, but sharing the front-end across slicers would be a nice follow-up and would also shrink per-lane state.

5. Minor, diagnostics-only: the totalHdlcFrameStarts dedupe window (+ 10 samples) is half a symbol at 24 kHz/1200 baud; independent DPLLs commonly land flag detection a symbol or more apart, so cross-lane duplicates will still be counted — and with lane-major ordering, a later lane processing earlier samples fails the > comparison asymmetrically. Consider widening to a symbol-or-two window, or just documenting that the counter is approximate.

6. Trivia: the kPrefilterLenSym comment says "~8.3 symbol-times" but 383 × 1200⁄44100 ≈ 10.4; and the __restrict__ MSVC shim in AetherAFSKDemod.h is unused (nothing in the file uses __restrict__) and defines a reserved identifier — suggest dropping it.

Two FYIs, no action needed: the branch's actual merge-base is f39746a3 (two commits behind main at open — contradicts the "rebased immediately before opening" note, but there are no conflicts), and VhfMode::Off produces zero lanes so processMonoFloat early-returns with frozen diagnostics — fine today since nothing selects Off, just confirm that's the intended semantic.

None of the above is structural — item 1 is the only one I'd want resolved before merge. Nice work, and 73.


🤖 aethersdr-agent · cost: $19.8929 · model: claude-fable-5

@K5PTB

K5PTB commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

@AetherClaude — thanks for the thorough review. Addressing each item:

#1 — FCS display threshold (>= 18 → >= 17)
Off-by-one introduced in the initial commit — the adjacent comment correctly states 17 bytes as the minimum, and a 17-byte U-frame has a real FCS at bytes [15–16]. No rationale for the 18; reverted.

#2publishFrameMqtt wiring
You're correct that main was never broken. What happened: during the cherry-pick/split process that separated this branch from the profiles work, the #ifdef HAVE_MQTT block inside appendFrame() was lost on our branch. We noticed MQTT wasn't working and fixed it by connecting publishFrameMqtt directly to frameDecoded rather than restoring the original #ifdef path — a refactor, not a restoration. Behaviorally equivalent since frameDecoded is only emitted for FCS-valid frames. PR description updated to reflect this accurately.

#3 — Inadvertent churn in Ax25HfPacketDecodeDialog
All four items (addStretch(2), the three invokeMethod PMF→lambda rewrites, the startTransmit declaration reorder, and the hdlc_codec_test CMakeLists relocation) were artifacts of the same cherry-pick/split process — changes from the profiles branch scope that came along for the ride and weren't fully restored. Reverted to merge-base state.

#4 — Independent front-end per slicer
Acknowledged. Sharing the front-end across slicers is the right long-term direction and we intend to pursue it as a follow-up. It touches the core DSP path so we want to test it carefully in isolation rather than include it here.

#5totalHdlcFrameStarts dedup window
Widened from 10 to 20 samples (one full symbol at 24 kHz/1200 baud). Added a comment documenting that the counter is approximate.

#6kPrefilterLenSym comment and __restrict__ shim
Comment corrected (8.3 → 10.4). The __restrict__ MSVC shim was unused and defined a reserved identifier; removed.

FYIs
VhfMode::Off producing zero lanes with frozen diagnostics is intentional — added a confirming comment at the early-return. On the merge-base: the rebase ran against upstream/main on 2026-06-11 but two commits landed upstream in the interval before the PR was opened; no conflicts resulted. PR description updated accordingly.

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @K5PTB — this is a well-prepared PR. The vendoring follows the repo's third_party conventions (attribution in THIRD_PARTY_LICENSES, refresh notes, GPL-2.0-or-later → GPL-3.0-or-later is fine), the new code is clean RAII/C++20 (std::numbers, std::popcount, std::call_once, unique_ptr lanes), and the comparator data is appreciated. I verified two claims from the description against main:

  • MQTT rewiring: confirmed equivalent. appendFrame()'s only early return is if (!frame.fcsOk), the shim sets fcsOk = true on every frame it emits, and appendFrame has no callers other than the frameDecoded connection. The direct frameDecoded → publishFrameMqtt connection preserves behavior.
  • Settings: only whitespace changes to the grandfathered flat keys; no new AppSettings keys. ✓

Findings, none blocking:

1. Nine slicer lanes redundantly recompute an identical front end (perf, suggest follow-up). Each A+ lane runs the full ~209-tap BPF + IQ mix + 4× RRC convolutions on the same input samples; the lanes differ only at the slicer decision (m_spaceGain) and DPLL. Direwolf computes the filter chain once per channel and fans out only the per-slicer comparison — that structure would be both more faithful and ~9× cheaper on the VHF path (~94M MACs/s currently). Your RPi 5 results show it's workable as-is, so I'd take this as a follow-up refactor (shared front end producing mAmp/sAmp, per-slicer decision + DPLL state), not a blocker for this PR.

2. pllAlpha is accepted but silently ignored — kVhf1200PllAlpha is now dead. AetherAFSKDemod's constructor discards pllAlpha (and the other libmodem tuning params) in favor of the internal Direwolf inertia constants — documented in the header, but the shim still defines kVhf1200PllAlpha = 0.010 and passes it through addLaneA, so a maintainer tuning that constant would see no effect. Suggest dropping the constant and the parameter from addLaneA (or at minimum a comment at the call site noting the value is unused on the VHF path).

3. Gate-open no longer resets lane bitstream state — please confirm intentional. resetBitstreamStates() was removed along with its call in updateReceiveGate, so HDLC/demod state now persists across squelch closures. FCS protects against splicing, it matches Direwolf's behavior, and your test plan covers gate recovery — so this looks deliberate, but it isn't mentioned in the PR description's change list, and it's a behavior change independent of the new demodulator. Worth a sentence in the description for future archaeology.

4. Doc nits.

  • README.aethersdr.md says the header provides a sinc_corr_afsk_demodulator alias — it doesn't; the shim adapts via AfskDemodWrapper. Please update.
  • The CMake comment/status message labels the library "GPL-3"; the vendored code is GPL-2.0-or-later (GPL-3 only as combined work). Minor, but the THIRD_PARTY_LICENSES entry gets it right — worth matching.

5. Minor. The HDLC frame-start dedup window is hardcoded to 20 samples (one VHF symbol), but processBit is shared with the HF 300 path where a symbol is 80 samples — the counter is diagnostics-only and commented as approximate, so fine, but deriving it from config.sampleRate / config.baud would make it correct for both. Also, AetherAFSKDemod's filter design divides by bitrate/sampleRate unguarded — fine with today's hardcoded 24000/1200 callers, but a cheap assert would protect future ones.

Nice touches I want to call out: the quint64 cast fixing potential overflow in the duplicate-window computation, and the absolute-gap handling in shouldEmitFrame correctly accounting for the now non-monotonic currentDecodeSampleIndex across per-lane block processing.


🤖 aethersdr-agent · cost: $7.1404 · model: claude-fable-5

ten9876 added a commit to K5PTB/AetherSDR that referenced this pull request Jun 14, 2026
…cy (aethersdr#3527 review)

Maintainer fix-up addressing the three small items from the second
aethersdr-agent review pass that were left open / deferred:

- CMakeLists.txt: the vendored Dire Wolf source is GPL-2.0-or-later, not
  "GPL-3" — the build comment and STATUS message now state that, with the
  combined-work GPL-3.0-or-later note (matching THIRD_PARTY_LICENSES, which
  was already correct). No license-version mislabel ships.
- AetherAx25LibmodemShim: drop the dead `kVhf1200PllAlpha` constant and the
  unused `pllAlpha` parameter from `addLaneA`. AetherAFSKDemod ignores
  pll_alpha (uses Direwolf's internal DPLL inertia — confirmed by the
  `/*pllAlpha*/` unnamed ctor param), so the named constant was a false
  tunable. The 0.010 positional value is preserved inline + documented, so
  behavior is byte-identical; the HF libmodem lane keeps its real pll_alpha.
- README.aethersdr.md: the header does not provide a
  `sinc_corr_afsk_demodulator` alias — it's adapted via `AfskDemodWrapper<>`.
  Corrected.

Verified: clean build; ax25_libmodem_shim_test (Profile A+ loopback) +
ax25_frame_formatter_test + hdlc_codec_test + tnc_terminal_test all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ten9876

ten9876 commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

@K5PTB — pushed a small maintainer fix-up (c8f11129) closing the three loose ends from @aethersdr-agent's second review pass, so they land in this PR rather than a follow-up. Great work on the demodulator — the comparator evidence is excellent.

  • CMake GPL label — the build comment + STATUS message said "GPL-3" for code that's GPL-2.0-or-later. Corrected to state the vendored source is GPL-2.0-or-later with the combined-work GPL-3.0-or-later note. (THIRD_PARTY_LICENSES was already correct.) For a GPL-vendoring PR I'd rather not ship a knowingly-wrong license string, so I folded the deferred follow-up in here.
  • Dead kVhf1200PllAlpha — removed the constant and the unused pllAlpha param from addLaneA. AetherAFSKDemod ignores pll_alpha (confirmed by the /*pllAlpha*/ unnamed ctor param — it uses Direwolf's internal DPLL inertia), so the named constant was a false tunable. The 0.010 value is preserved inline + documented, so behavior is byte-identical; the HF libmodem lane keeps its real pllAlpha.
  • README — corrected the AetherAFSKDemod.h line (no sinc_corr_afsk_demodulator alias; it's adapted via AfskDemodWrapper<>).

Verified locally: clean build, and all four AX.25 tests pass (ax25_libmodem_shim_test Profile A+ loopback, ax25_frame_formatter_test, hdlc_codec_test, tnc_terminal_test).

The 9-slicer shared-front-end de-duplication remains a good follow-up (not a blocker), as you noted. With these in, this is on my list to approve once CI re-greens. 73.

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at head c8f11129 — the new AetherAFSKDemod, the shim block-processing rewrite, the GUI/MQTT rewiring, CMake, and the test. The findings from the previous round all look correctly resolved, and I didn't turn up anything blocking. 🎉 Nice work, @K5PTB — the 3-way comparator evidence is genuinely impressive.

Confirmed resolved from the prior pass

  • CMake STATUS message + comment now correctly state the vendored source is GPL-2.0-or-later (combined work GPL-3.0). THIRD_PARTY_LICENSES and the README agree.
  • Dead kVhf1200PllAlpha constant and the false pllAlpha tunable removed; addLaneA passes 0.010 inline with a comment explaining AetherAFSKDemod ignores it (uses Direwolf's internal DPLL inertia). HF lane keeps its real pllAlpha.
  • MQTT is now a direct frameDecoded → publishFrameMqtt connection guarded by #ifdef HAVE_MQTT, and the duplicate call inside appendFrame() is gone — behaviorally equivalent since frameDecoded only fires for FCS-valid frames.
  • Cross-lane dedup window widened to 20 samples (one symbol) with the "approximate counter" comment.

Things I checked that are correct

  • Build linkage is complete. All four targets that compile AetherAx25LibmodemShim.cpp now link aether_afskdemod: the main AetherSDR target (via CORE_SOURCESALL_SOURCES), plus ax25_libmodem_shim_test, ax25_replay, and ax25_session_analyze. No orphaned consumer that would hit a link error.
  • The non-monotonic currentDecodeSampleIndex. The new per-lane block loop sets currentDecodeSampleIndex from each bit's absolute index and then re-walks it per lane, so it's no longer globally monotonic within a processMonoFloat call. The switch in shouldEmitFrame to an absolute-difference sampleGap() correctly handles that — good defensive change. (The lastHdlcFrameStartSampleIndex gating is technically subject to the same non-monotonicity, but it only feeds the approximate totalHdlcFrameStarts diagnostic, so it's harmless and already documented.)
  • s_cosTable init is call_once-guarded; reads after init are fine across instances/threads.
  • AGC/DPLL/confidence paths and the single-vs-multi-slicer decision look faithful to the documented profile-A algorithm.

Non-blocking observations (no action needed for this PR)

  • The shared-front-end-per-slicer de-dup is the right follow-up, as already noted — each of the 9 A+ lanes currently re-runs the prefilter + 4 LP convolutions independently. Fine to defer.
  • VhfMode has no UI/AppSettings exposure and is hard-coded to APlus via the config default. That matches the PR's stated scope; just flagging that "A vs A+" is currently a compile-time-only choice.

CI was still in progress when I looked (build / check-macos / check-windows / CodeQL all running, none failed). Worth letting it green before merge, as @ten9876 noted. Thanks again for the thorough test plan and the careful attribution handling on the vendored code.


🤖 aethersdr-agent · cost: $3.2236 · model: claude-opus-4-8

K5PTB and others added 3 commits June 13, 2026 18:01
Adds AetherAFSKDemod — Profile A+ (9 slicers, Direwolf default) AFSK
demodulator derived from Dire Wolf demod_afsk.c / dsp.c (WB2OSZ,
GPL-2.0-or-later → GPL-3.0-or-later). Replaces libmodem's
sinc_corr_afsk_demodulator on the VHF 1200-baud path; libmodem is
unchanged as the HF 300-baud backend.

Performance — 1875-packet overnight comparator run, RPi 5, live APRS
traffic (Principle VIII — evidence over assertion):
  AetherSDR A+  93%  avg confidence 60
  Graywolf      90%  avg confidence 52
  Direwolf      87%  avg level 58
  137 packets (7.3%) decoded exclusively by AetherSDR

New files:
  third_party/direwolf_afsk/AetherAFSKDemod.h/.cpp — vendored per
    Technology Constraints (third-party); THIRD_PARTY_LICENSES updated
  third_party/direwolf_afsk/THIRD_PARTY_LICENSES

Modified:
  src/core/tnc/AetherAx25LibmodemShim.cpp/.h — AfskDemodWrapper<>
    template; VhfMode enum (Off/A/APlus); HF path unchanged
  src/gui/Ax25HfPacketDecodeDialog.cpp/.h — profile persistence
  tests/ax25_libmodem_shim_test.cpp — synthetic Profile A+ loopback

Also restores publishFrameMqtt wiring to frameDecoded signal, lost
during cherry-pick from aethersdr#3460. Demonstrated by functional test
confirming frames on aethersdr/ax25/rx. Principle XI.

No new AppSettings keys added. kPacketDecoderProfileSetting and
kPacketDecoderDebugSetting are flat keys grandfathered from e0618ee
(2026-05-16), one day before Constitution v1.1.0. Principle V.
AppSettings writes use single setValue + save(). Principle XIV.

Pre-submission: max-effort code review pass run; all blocking findings
addressed. Branch rebased against current upstream/main. Principle X.
Member naming and π constants comply with AGENTS.md §147/§144.
Principle VIII.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ethersdr#6 on feat/ax25-direwolf-demod

aethersdr#1 — Revert FCS display threshold from >= 18 back to >= 17.
  Off-by-one in the initial commit; a 17-byte no-PID U-frame has a real
  FCS at bytes [15-16]. The adjacent comment already said 17 was the
  minimum; the threshold contradicted it without rationale.

aethersdr#3 — Revert inadvertent churn in Ax25HfPacketDecodeDialog.
  Four changes were cherry-pick/split artifacts from the profiles branch
  that were not restored when PR3 was separated: the controls->addStretch(2)
  removal (visible layout regression), three invokeMethod PMF->lambda
  rewrites (functionally inert), the startTransmit declaration reorder in
  the header, and the hdlc_codec_test block relocation in CMakeLists.
  All reverted to merge-base state.

aethersdr#5 — Widen totalHdlcFrameStarts dedup window from 10 to 20 samples.
  One symbol = 20 samples at 24 kHz/1200 baud. The previous 10-sample
  window was half a symbol, allowing cross-lane frame-start duplicates
  to increment the counter when DPLLs on different lanes land detection
  slightly apart. Added comment documenting the counter as approximate.

aethersdr#6 — Fix kPrefilterLenSym comment (8.3 -> 10.4 symbol-times) and drop
  the __restrict__ MSVC shim in AetherAFSKDemod.h. The shim was unused
  (nothing in the file uses __restrict__) and defined a reserved
  identifier.

FYI/VhfMode::Off — added confirming comment at the lanes.empty()
  early-return in processMonoFloat: frozen diagnostics when Off is
  intentional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cy (aethersdr#3527 review)

Maintainer fix-up addressing the three small items from the second
aethersdr-agent review pass that were left open / deferred:

- CMakeLists.txt: the vendored Dire Wolf source is GPL-2.0-or-later, not
  "GPL-3" — the build comment and STATUS message now state that, with the
  combined-work GPL-3.0-or-later note (matching THIRD_PARTY_LICENSES, which
  was already correct). No license-version mislabel ships.
- AetherAx25LibmodemShim: drop the dead `kVhf1200PllAlpha` constant and the
  unused `pllAlpha` parameter from `addLaneA`. AetherAFSKDemod ignores
  pll_alpha (uses Direwolf's internal DPLL inertia — confirmed by the
  `/*pllAlpha*/` unnamed ctor param), so the named constant was a false
  tunable. The 0.010 positional value is preserved inline + documented, so
  behavior is byte-identical; the HF libmodem lane keeps its real pll_alpha.
- README.aethersdr.md: the header does not provide a
  `sinc_corr_afsk_demodulator` alias — it's adapted via `AfskDemodWrapper<>`.
  Corrected.

Verified: clean build; ax25_libmodem_shim_test (Profile A+ loopback) +
ax25_frame_formatter_test + hdlc_codec_test + tnc_terminal_test all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ten9876 ten9876 force-pushed the feat/ax25-direwolf-demod branch from c8f1112 to 4008280 Compare June 14, 2026 01:03
@ten9876

ten9876 commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

@K5PTB — rebased onto current main and force-pushed (head 40082806). Your two commits keep your authorship; the rebased commits (and my earlier fix-up) are signed with the maintainer key. Please git fetch before any further local work.

Good news: the rebase was conflict-free. This PR doesn't touch MainWindow.cpp, so it sailed past the whole #3351 decomposition that's been churning everyone else's rebases — the AFSK demod lives entirely in core/tnc + third_party/ + the standalone Ax25HfPacketDecodeDialog, none of which the MainWindow split moved. Nothing to relocate.

Verified on the rebased head: clean build, all four AX.25 ctests pass (ax25_libmodem_shim_test, ax25_frame_formatter_test, hdlc_codec_test, tnc_terminal_test), and the review fix-ups (GPL-2.0-or-later label, dead-constant removal, README) are intact.

Once CI re-greens this is ready for maintainer approval — it needs mine specifically since it touches THIRD_PARTY_LICENSES. 73.

@ten9876 ten9876 merged commit 5912f64 into aethersdr:main Jun 14, 2026
6 checks passed
ten9876 added a commit to jensenpat/AetherSDR that referenced this pull request Jun 14, 2026
… settings (aethersdr#3565 review)

Maintainer fix-up addressing the two should-fix items from the aethersdr#3565 review
(folded in on top of a rebase onto current main):

- THIRD_PARTY_LICENSES: add entry aethersdr#16 for the vendored QGeoView (LGPL-3.0-or-later,
  (C) 2018-2025 Andrey Yaroshenko, https://github.com/AmonRaNet/QGeoView). The
  PR vendored ~7k lines of LGPL code but the root attribution registry — which
  lists all 10 other bundled libs and is itself maintainer-gated — didn't list
  it. The bundled LICENSE + AETHERSDR-PATCHES.md remain; this is the canonical
  registry entry, matching the Dire Wolf (aethersdr#3527) precedent.

- PskReporterMapDialog: consolidate the two new flat AppSettings keys
  (PskReporterShowPaths, PskReporterUpdateIntervalMs) into one nested-JSON blob
  under a single "PskReporter" key (Constitution Principle V), via read-modify-
  write helpers. These keys are brand-new in this PR, so no migration is needed.
  (PskReporterMapGeometry stays — that's PersistentDialog window geometry, a
  separate established convention.)

Builds clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ten9876 added a commit that referenced this pull request Jun 14, 2026
## Release prep for v26.6.3

Bumps the version and refreshes documentation for the **58 commits since
v26.6.2** (2026-06-07). No tag is created — tagging is the release
trigger (the #3567 MS Store automation fires on `v*` tags), so that step
is left to the maintainer after this merges.

### Version
- `CMakeLists.txt`: `VERSION 26.6.2 → 26.6.3` (drives the MSIX
`Identity.Version`).
- README.md + AGENTS.md "current version" strings updated to match.

### CHANGELOG.md — v26.6.3 entry
Covers: WFM software demodulator, APRS client, PSK Reporter reception
map + reusable Qt mapping engine (QGeoView), Direwolf-derived VHF AFSK
demodulator, DAX-IQ made fully usable, profile-load recovery hardening,
DEXP protocol fix, Multi-Flex ping-watchdog grace, MQTT enhancements,
the #3351 MainWindow decomposition + `RadioSession`, the MS Store CI
auto-stage, and the fix roll-up.

### Doc refresh (reviewed all repo-root + `docs/` files)
- **README.md** — Highlights: added WFM, AetherModem packet/APRS, PSK
Reporter map.
- **ROADMAP.md** — cycle → `post-v26.6.3`; moved **AetherModem Phase 1**
from Queued to **Recently shipped** alongside WFM / AFSK / PSK-Reporter
map / DAX-IQ / the decomposition.
- **AGENTS.md** — version, added `RadioSession` to Key classes, threads
**11 → 12**.
- **CONTRIBUTING.md** — thread count 11 → 12.
- **docs/architecture/pipelines.md** — added the **AX.25 TNC thread**
(#3473) to the list + table; **11 → 12**.
- **docs/MODEM.md** — VHF 1200 baud now documents the **Direwolf-derived
Profile A+ demodulator (9 slicers)** that replaced the libmodem lane
bank (#3527); the APRS client + map are no longer "out of scope" (HF
300-baud section unchanged).

Reviewed (via two parallel doc-review agents) and found **no changes
needed** in: CONSTITUTION, GOVERNANCE, SUPPORT, SECURITY,
CODE_OF_CONDUCT, CLAUDE/GEMINI (pointer files), and the rest of `docs/`
(audio-pipeline, vita49-format, tci-*, style/, theming/, qa/, etc.).

### Verification
- Builds clean with `VERSION 26.6.3`; full suite **38/38 pass**.

### To roll the release (maintainer)
After merge: `git tag -s v26.6.3 && git push origin v26.6.3` → triggers
the Windows installer / release workflows (and the MS Store draft-stage
once its credentials are set).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@K5PTB K5PTB deleted the feat/ax25-direwolf-demod branch June 14, 2026 20:08
G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
…ethersdr#3527)

Direwolf profile-A AFSK demodulator (IQ-mix + RRC + DPLL, 9-slicer A+) replacing libmodem's VHF 1200-baud path; HF 300-baud stays on libmodem. Vendored Dire Wolf source is GPL-2.0-or-later (combined work GPL-3.0-or-later, see THIRD_PARTY_LICENSES). 3-way comparator: AetherSDR A+ 93% vs Graywolf 90% vs Direwolf 87% over 1875 packets.

Authored by @K5PTB with a maintainer review fix-up (GPL label, dead-constant cleanup, README).

Co-authored-by: K5PTB <k5ptb@users.noreply.github.com>
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.

2 participants