Repository navigation
feat(kat500): add tuner applet and Advanced maintenance - #6452
Conversation
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
There was a problem hiding this comment.
Reviewed the PR head at 38718c8c8, diffed against the stacked base 453240e9 so only this PR's work is in scope. Findings below are reasoned from code, not reproduced at runtime — I have no build, no test run and no way to drive the GUI here.
This is genuinely careful work. The TX sequencing in Kat500TxController is the strongest part: the carrier is a captured TxController::Input (a TxCoordinator::Request taken at the operator's click), every stop() path calls carrier.stop(), and the destructor stops the carrier before cancelling the tuner. I went looking for a key-on path that escapes the coordinator and did not find one. Two things still need to change before merge, and two need a maintainer's call.
1. Issue fit
Partially — and deliberately so. The PR says Refs #4953, not "Fixes", which is right: #4953 asks for KPA500 and KAT500, and this PR is the KAT500 half (KPA500 is #5991). Against the KAT500 half of that request — tuner status, initiate tuning, SWR, antenna selection, bypass state, frequency/band sync, connection status — every item is addressed, and the implementation follows the issue's own architectural preference ("a separate hardware integration module, rather than mixing KPA500/KAT500 commands into the FlexRadio code"). Nothing in the issue is left untouched.
Test coverage for the layer is right and not merely decorative. tests/kat500_tx_controls_test.cpp asserts the negative cases that matter: tune:off on tuner disconnect (:204), on mode change (:210), on sweep abort (:329); "timeout cannot be revived by a late tuner reply" (:218); "SAVE never requests a search or carrier" (:237); "one captured radio carrier; internal ATU never starts" (:143). Those would fail against an unfixed implementation. All seven new targets have add_test (tests/tests.cmake:4601,4609,4622,4633,4643,8445) and none sets an explicit TIMEOUT, so they inherit the retrofit default rather than opting out.
Socket-test disclosure (reporting only, not a finding): none. grep for QTcpServer/QTcpSocket/QSerialPort across all six tests/kat500_*.cpp returns nothing — the body's "inject transport and time; no fake radio/tuner socket peer enters the default graph" holds.
2. Scope
| File / group | What it changes | Claimed? | Verdict |
|---|---|---|---|
src/core/Kat500{Protocol,Maintenance}.*, src/models/Kat500{Model,Types,TxController}.* |
New tuner protocol, model, maintenance, TX sequence | Yes | In scope |
src/gui/Kat500{Applet,AdvancedWidget}.* |
New applet + Advanced pane | Yes | In scope |
src/gui/{AppletPanel,MainWindow,MainWindow_Wiring,RadioSetupDialog*}.* |
Registration/wiring for the above | Yes | In scope |
src/gui/AtuPreTuneDialog.*, TxApplet.cpp KAT500 routing |
External-tuner route + sweep | Yes | In scope |
docs/superpowers/plans/ + specs/ |
New top-level docs dir; agent planning artifacts | No | Blocker — see #2 |
TxApplet.cpp:198,223,382,389,780,786 % suffix |
Unconditional % on RF Pwr / Tune Pwr for every radio |
Body says "when the KPA500 is powered" | Undisclosed — see nits |
docs/user/docs/tx-controls.md "20 W" → "20%" |
Pre-tune warning wording | No | In scope — genuine fix (AtuPreTuneDialog.cpp:91 kAutoModeTunePowerWarnPercent = 20; the old "W" was wrong) |
tests/tests.cmake ×11 existing targets |
Kat500AdvancedWidget.cpp added to unrelated radio_setup_* / firmware_close_dialog / flex_control_visibility targets |
No | In scope — link consequence of RadioSetupDialog_Peripherals.cpp referencing it |
docs/user/src/components/LogAnalyzer/rules.json |
14 RadioModel.cpp line renumbers |
No | Generated; conflict-prone (nit) |
docs/architecture/aetherd-touchpoint* |
Manifest totals 259→262; one unrelated row re-encodes — as — |
No | Generated; nit |
tools/docs/gen_reference.py (1 line) |
— | No | Trivial, generator-side |
Process, independent of the code: the base is the temporary branch codex/kpa500-5991-base, and the body says "Do not merge into the temporary base branch." Consistent with that, only 4 checks ran on this head — Static checks, Sanitizer-option-configures, Build-docs-site, and a skipped PDF step. The cross-platform build matrix never executed, so there is no CI compile evidence on Linux, macOS or Windows for ~7,400 new lines. The body discloses this. As insurance I grepped the new sources for the usual Windows-only breaks (M_PI, bare std::min/std::max with windows.h, missing NOMINMAX) and found none, which is consistent with the claimed local MSVC build.
Blockers
1. docs/user/docs/kat500-tuner-control.md:37 + src/gui/Kat500Applet.cpp:766 and :503 — the documented "hold Space" gesture on the SWR pill does not preview bypass SWR; it keys the transmitter.
The user guide says:
Press and hold its pill (or hold Space while it has keyboard focus) to preview the last bypass SWR in the same meter
and the same claim is in the pill's tooltip and its accessible description (Kat500Applet.cpp:766):
tr("%1. Hold this pill or Space to view the last bypass SWR; release to return to adjusted SWR. "m_swr is a QPushButton (Kat500Applet.h:72) wired on pressed/released (Kat500Applet.cpp:290-291), so the gesture depends entirely on Qt's native Space activation reaching the widget. There is no Key_Space handler anywhere in the applet, and Kat500Applet::eventFilter (:952) only handles FocusOut/Hide/WindowDeactivate to release a stuck setDown.
The Space key never arrives. MainWindow installs an application-level filter (MainWindow.cpp:1491 qApp->installEventFilter(this)), which runs before the target widget; that filter calls handlePttHoldShortcut and consumes the event (MainWindow_Shortcuts.cpp:736):
if (handlePttHoldShortcut(ke, event->type()))
return true;Qt::Key_Space is registered as ptt_hold with keysTx=true (MainWindow_Shortcuts.cpp:1236), and the only gate on a new press is textEntryCaptured() (:324) — a focused QPushButton is not text entry, so the step resolves to PttHoldKeyStep::KeyTx. The design intent is explicit in the comment right above it (:322-325): "so a focused non-editable combo ... doesn't swallow the first Space/PTT press." Space is meant to win over focused non-editable widgets.
Failure scenario: radio connected, keyboard shortcuts on, operator tabs or clicks to the SWR pill, follows the documentation and holds Space. The radio keys (PTT hold, via the Quindar/PTT coordinator path); releasing un-keys. The bypass-SWR preview never happens. A screen-reader user gets this instruction read to them as the control's accessible description.
The cheap and correct fix is to stop claiming it — the mouse press-and-hold works fine and is the real gesture. Three edits:
docs/user/docs/kat500-tuner-control.md:37:
The single **SWR** meter shows adjusted SWR. Press and hold its pill to preview the last bypass SWR in the same meter; the pill becomes yellow and reads **BYP**.and drop "or Space" from the tooltip/accessible description at Kat500Applet.cpp:766 and the Settings help text at :503. Making Space actually work would require an exemption inside handlePttHoldShortcut, which is a TX-path change and a maintainer's call — not something to land inside this PR.
2. docs/superpowers/plans/2026-10-13-kat500.md and docs/superpowers/specs/2026-10-13-kat500.md — agent planning artifacts in a directory that does not exist on main, carrying private infrastructure details into a public repo.
git ls-tree origin/main docs/superpowers/ is empty — there is no such convention in the tree. These two files are workflow residue, and they publish:
/Users/ozy/Documents/AetherSDR/elecraft-rea/kat500-investigation/{runtime-findings.md,...}— local paths referenced as the "Research authority", so the spec cites sources no reader can openKAT500 bridge 100.100.216.93:4001; KPA500 :4004— a private address and port mapecho-ms300prd via ozy SSH— a private hostname and access method
They also read as internal task tracking ("Do not claim ready for review if checks are incomplete", unchecked - [ ] task boxes, "User authorizes stopping bridge temporarily"), which will confuse a reader who finds them under docs/. Please delete both files from the PR. If any of the design rationale is worth keeping, the right home is the PR body or an RFC under the existing docs/architecture/, with the private addresses and local paths stripped.
Needs maintainer decision
Use KAT500 for TX Controlsdefaults ON. A connected KAT500 reporting Auto or Manual silently takes over the TX Controls applet:AUTO/ATUretarget to the tuner,MEMbecomesSAVE, and aKAT500pill replaces the Mem indicator. No issue describes the current radio-ATU behavior as defective, so this is a new default on an existing TX control surface rather than a fix. The body already flags defaults as "subject to maintainer/CODEOWNERS review" — this is the one that matters most, because it changes what an existing button does. The implementation is honest about it (TxApplet.cpp:507-515: explicit Bypass restores the radio controls, andKat500TxController::refreshat:140-144deselects whenuseTxControls()is off), so flipping the default to OFF would be a one-line change if that is the call.- No RFC. #4953 is a feature request (labeled
maintainer-review, assigned to @Ozy311) whose body is largely AI-drafted architectural speculation; it is not a ruling. This PR adds a new peripheral family, a new log category, a new Advanced maintenance surface with EEPROM write paths, and a route takeover of an existing TX control. GOVERNANCE.md wants an RFC for architectural changes. I am raising this as a maintainer call rather than a blocker because #4953 carriesmaintainer-reviewand an assignment, which may already constitute the ruling — I could not find a comment that settles it either way.
Nits (non-blocking)
src/gui/TxApplet.cpp:198and:223— the%suffix is unconditional, not KPA500-gated. The body says "RF Power/Tune Pwr show percentages when the KPA500 is powered", butpercentText(:95-98) isQStringLiteral("%1%").arg(value)with no gate, and it is applied in bothvalueChangedhandlers (:382,:389), bothsyncFromModelpaths (:780,:786) and both drag formatters (:195,:220). Every radio and every backend now shows100%where it showed100. I think the change is correct — the sliders' own accessible descriptions already say "0 to 100 percent of maximum", andkAutoModeTunePowerWarnPercentconfirms the unit — but it changes existing UI for all users and the body describes it as conditional. Please correct the body; no code change needed.src/models/Kat500Model.cpp:14—Q_LOGGING_CATEGORY(lcKat500, "aethersdr.kat500")is the only category not under theaether.*prefix every sibling uses. I checked that this is harmless:LogManagerholds an explicit category list (LogManager.cpp:71) and does no prefix filtering, so the category still appears in the picker and support bundles. Still worth renaming toaether.kat500for consistency, in bothKat500Model.cpp:14andLogManager.cpp.src/models/Kat500TxController.cpp:223— the Carrier-phase watchdog assumes a synchronous Tune readback.setPhase(Phase::Carrier, 30000)runs beforem_carrier.start(), with the comment "the radio publishes Tune state synchronously." The 100 ms watchdog (:24) then callsrefresh(), which stops the carrier if!m_radio->transmitModel().isTuning()(:147-151). On a backend whose tune readback lags more than 100 ms, the sequence would abort itself immediately. This fails safe — it stops the carrier rather than leaving it up — so it is a robustness/flakiness note, not a safety issue. Worth a comment naming which backends the synchronous assumption holds for.- Regenerated artifacts (
docs/architecture/aetherd-touchpoints.mdtotals,rules.jsonline numbers) will conflict with every other open PR that regenerates them; expect one rebase. Thetags.jsonhunk also re-encodes—as—on the unrelatedcore/FirmwareCurrency.hrow — generator encoding drift, harmless.
Security audit
I ran the required audit (security-audit, CodeGuard rule set) over the flagged files. No confirmed exploitable defect in the new KAT500 code. Specifically cleared, with the bounds checked against the source:
Kat500Maintenance.cpp:128-136decodeBackup— the attacker-controlled file supplies the record address, butaddressTextis pinned to exactly 4 hex digits andcountTextto 2, and:131rejects the record unlessdata->size() == countandrangeValid(!internal, address, count), which requiresaddress <= (external ? 65536 : 1024) - lengthwithlength <= 64. Thememory[address + offset]store is always in bounds.:135rejects overlapping records and:139demands exact full coverage, so there is no partial or uninitialized restore image. Input capped at 256 KiB, lines at 512 bytes, forced printable ASCII.parseEepromReply(:57-68) anddecodeFaultHistory(:157) — both re-validate the reply's own address/length and the exact buffer size before anymid().buildEepromWrite(:48-51) floors at P≥0x20 / S≥0x1000, matching the header's documented restore regions and keeping identity bytes out of reach.- No unauthenticated listener: both
QTcpSockets are outbound clients; noQTcpServer/bind/listenin the new code. NoQProcess/system/execanywhere in the changed files. File paths all originate from an interactiveQFileDialog, writes go throughQSaveFile.
All CodeGuard CS-VLN-CMDI-, CS-VLN-PATH-, CS-VLN-SSRF-* and CS-PII-* hits on these files are the known lexical false-positive classes (Markdown-quoted protocol text in comments, docs JSON, CI YAML; the CS-PII-IP hits at Kat500Maintenance.cpp:18 and Kat500Protocol.h:7 are the CRC polynomial and a version string). Dropped.
One item worth routing elsewhere: src/core/Kpa500Connection.cpp:64 emits a rejected reply's argument verbatim into the diagnostics pane and support bundle (report(...).arg(cmd, arg)), where the KPA500 frame parser permits \r, \n, NUL and bytes >0x7F — log/terminal-escape injection from whatever is on the serial line. The same file already does this correctly at :517 via bytes.toPercentEncoding("^; "), and the KAT500 parser discards non-printable frames before any report (Kat500Protocol.cpp:80), so this is a KPA500-only gap. It is not in this PR's 52 changed files — it belongs to #5991. Flagging it so it can be routed there, not as a finding against this PR.
What I tried to break
- Carrier left keyed. Walked every exit from
Kat500TxController: tuner disconnect (:43-47), radio disconnect (:50-53), 30 s Carrier deadline (:25-32),operationFailed(:39-41), authorization/frequency/serial drift via the 100 ms watchdog (:147-151), explicitcancel()(:269), and the destructor (:63-71). All route throughstop(), which doesstd::exchange(m_carrier, {})thencarrier.stop(). I could not construct a path that returns to Idle with the carrier up. TheoriginalIntentValid()fence re-checks serial, frequency, mode,powerOnandcarrier.belongsTo(m_radio)on every tick. - A new key-on path escaping the coordinator. No —
TxController::Inputwraps aTxCoordinator::Requestcaptured at the operator boundary (TxController.h:25-48), and the controller carries that captured value through every async step rather than re-capturing at callback time. This is the sanctioned producer path, not a bypass. - Settings canon.
Kat500Applet.cpp:176/:935use the flat key"Kat500Applet", but it holds a single JSON document of client-owned, app-global view preferences (Mini/History/FollowTx/Verbose) — exactly the shape and precedent of"Kpa500Applet"(Kpa500Applet.cpp:111). Not per-radio state, so noradio_settingsscope is owed. I also confirmed no device configuration (mode, antenna, relay masks) is persisted, matching the body's claim. - a11y. No interactive
QLabels — the pills areQPushButtons, and there are 24setAccessibleName/setAccessibleDescriptioncalls in the applet and 16 in the Advanced widget. The one a11y defect I did find is the Space claim in blocker #1. - A latched SAVE button. The
toggledhandler early-returns when KAT500 is selected without restoring the check state, which looked like a stuck-latch bug — butsyncAtuIndicatorssetssetCheckable(false)under aQSignalBlockerwhen external (TxApplet.cpp:700-706) and restoressetCheckable(true)at:722, sotogglednever fires on that path. No bug. - Raw wire text above the seam. Grepped the new GUI/model files for
sendCmd,"transmit,"atu,"slice— nothing. Consistent withcheck_command_planenot needing a baseline bump and that file not appearing in the diff. - Could not check: anything requiring a build or a running GUI, the real
.KAT500Configinterop claim (whether Elecraft's Utility accepts a file whoseKAT500 Utility Version = AetherSDR), and whetherdecodeMemoryBin's fixed 6-slots-per-bin assumption (Kat500Protocol.cpp:289) holds when the per-bin slot count is configured to fewer than 6. Those need hardware. The body is candid that restore, Memorize, memory erase, factory reset and any RF carrier were never exercised on real hardware in this pass.
Recommendation
Needs maintainer decision, and not mergeable as it stands regardless of the code — the PR targets a temporary branch and the required cross-platform matrix has never run.
Concrete next steps, in order: (1) fix blocker #1 by dropping the "or Space" claim from the doc, tooltip and accessible description; (2) delete the two docs/superpowers/ files; (3) correct the body's % description; (4) get a maintainer ruling on the Use KAT500 for TX Controls default and on whether #4953's maintainer-review label already stands in for an RFC; (5) retarget to main once #5991 lands and let the full matrix run.
On the code itself I have no blocker. Both items above are documentation and repository hygiene, and the TX-path engineering — captured authorization carried through every async step, a fence re-checked on a 100 ms tick, and tests that assert the carrier stops rather than only that it starts — is better than most of what comes through here. Thanks for the unusually honest validation-boundaries section; the explicit list of what was not exercised on hardware made this much faster to review.
🤖 aethersdr-agent · cost: $22.6008 · model: claude-opus-5
|
Tightening one sentence in my review above, so the evidence and the claim match exactly. I wrote "On the code itself I have no blocker." That is stronger than what I actually verified, and the coverage boundary belongs on the record: I did not read the So the body's "reconnect never replays" and "late replies cannot overwrite newer observations" claims are untested by me. More precisely, then: the two blockers I raised are both documentation and repository hygiene, and I found no blocker in the code I did read — the TX sequence, the maintenance/protocol decoders, the settings path and the TX Controls wiring. That is not the same as a clean bill of health for the model's lifecycle, and a reviewer with a build should spend their time there. Nothing else in the review changes, and the recommendation stands. 🤖 aethersdr-agent · cost: $25.1096 · model: claude-opus-5 |
|
Cisco CodeGuard — static analysis of this PR (614 finding(s))
Automated static scan by Cisco DefenseClaw CodeGuard on the changed files. Advisory — some may be false positives; the review above verifies them. 🤖 aethersdr-agent · cost: $25.6336 · model: claude-opus-5 |
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
|
Addressed the actionable review findings at
Current-head Static Checks, docs and sanitizer configuration are green. The KPA dependency also passed its full cross-platform CI. Validation: native Mac full app build, all 12 KAT500/KPA500 suites, the keyboard-routing suite, both documentation production builds, generated references and log rules passed. The two new KPA malformed-input regressions were observed failing before the fix. I also checked the KAT model's queue/reset/generation boundaries and the late-EEPROM/canceled-memory/reconnect assertions; the model suite passed. This is bounded offline evidence, not a claim of a comprehensive independent lifecycle audit or physical RF/restore validation. No transmission or destructive hardware operation occurred. The contributing operator explicitly requested default-on routing. It remains unchanged pending the maintainer's ruling, as does the need to settle RFC approval; #4953 is a feature request, not proof of approval. The operator chose to track completion through #4953 instead of opening a separate RFC: #5991 references the issue, and #6452 closes it after retargeting to main and merging after KPA. No separate RFC issue was created. #6452 still targets the temporary dependency base and must be retargeted to 73, Ozy K6OZY · prepared with Codex |
rfoust
left a comment
There was a problem hiding this comment.
The reviewed head addresses the KAT500 half of #4953; KPA500 remains the stacked dependency #5991. Status, SWR, antenna/bypass control, explicit tuning, optional frequency following and connection status are covered. Recommendation: Needs maintainer decision. I found no confirmed code blocker, and all eight focused socket-free tests pass against this stack merged with the main revision listed below.
Scope
| File/group | Change | Claimed? | Verdict |
|---|---|---|---|
| Kat500Protocol/Maintenance | Command/status parsing, configuration and backup codecs | Yes | In scope |
| Kat500Model/Types/TxController | Transport, scheduling, maintenance and captured TX sequence | Yes | In scope |
| Kat500Applet/AdvancedWidget | New operating controls, history, diagnostics and staged maintenance | Yes | In scope |
| AppletPanel, MainWindow, RadioSetupDialog | Registration, connection preferences and wiring | Yes | In scope |
| TxApplet, AtuPreTuneDialog | External routing, SAVE, cancellation, sweep/erase and universal percentage suffixes | Yes | Existing UX/default needs maintainer decision |
| TxApplet inactive-route ATU stylesheet | Restyles the existing radio button even without external routing | No | Additional visual scope; see inline decision 2 |
| Tests, fixtures, tests.cmake, CMakeLists.txt | Socket-free coverage and build/link registration | Yes | In scope; no new dependency or gate expansion |
| User docs/screenshots, sidebar, log category, manifests/log references | Documentation and generated integration records | Yes | In scope |
| tools/docs/gen_reference.py | Also recognizes aethersdr.* category rows | Not explicit | Related generator residue; current category is aether.kat500 |
The commit author dates form a coherent implementation sequence. KPA dependency integration is disclosed and excluded by the current stacked-base comparison. No CHANGELOG edit, private planning artifacts, new dependency or test-gate weakening remains.
Blockers
None confirmed in code. The maintainer decisions and main-target CI prerequisites below prevent a ready-to-land recommendation.
Maintainer decisions
- Default-on external routing: see the inline comment on MainWindow_Wiring.cpp:7528. Jeremy/KK7GWY should explicitly settle the default and the changed ATU/MEM behavior. #4953 contains no maintainer approval of that default; the standalone-applet RFC exception does not settle a takeover of existing TX Controls.
- Existing radio ATU appearance: see the inline comment on TxApplet.cpp:720–723. The inactive-route branch installs a different stylesheet for ordinary radio users. Disclose this visual scope and obtain a ruling, or preserve the original radio-button appearance when KAT500 routing is inactive. This finding is from source, without a before/after main-window screenshot.
Nits — non-blocking
- The guide says numeric C/L Set chooses the closest combination, but it rounds down. The inline documentation suggestion corrects the prose without changing behavior: 29 pF selects 22 pF even though 30 pF is closer, as the passing protocol test explicitly asserts.
- Refresh generated Log Analyzer locations after retargeting/merging main. All 707 rule checks pass on the synthetic main merge, with 14 moved-line notices from intervening main changes.
Verification and attacks
- Stacked base:
6a49aeeb19c32ce13693b87b4d6f725c4c2789b2; head:bbbbfdd7513abf39a2eadace2d3db2d057836697. - GitHub stacked merge:
9f78c38fcfd0ff34fcb793ee53bffb01fa887787. Separately merged the stack with main709093e269766cca8f0dacda5ad55708cb490bb7in a detached scratch worktree: clean, tested treefa6b24c1f13e1ea390a03526a3f1b351c51e87d9. - Prescribed ARM64 toolchain, RADE enabled; focused targets compile. Host/system processors and inspected test executables are arm64; no RNNoise x86 sources in the Ninja graph. This was a focused-target build, not a full AetherSDR.app build.
- 8/8 CTests pass: kat500_protocol, maintenance, model, applet, advanced_widget, tx_controls, radio_setup_pair_grid and tx_applet_power_reconciliation. Added/modified tests use injected transport/time; no socket-opening synthetic device test was run.
- Registration, frozen CI gate, touchpoint manifest, generated references and strict engine-boundary checks pass; engine checking has tracked legacy warnings and zero blockers. Log rules pass as noted above.
- Late EEPROM acknowledgments/readbacks, overdue memory terminators, canceled compound-response drains, reconnect queue clearing, unsafe RF readback, partial backups and single-admission frequency following survived. Disabling active-request expiration makes four model checks fail; restoring it restores the pass.
- Captured authorization, delayed internal bypass, revocation, competing MOX, disconnect, route change, timeout, controller destruction, SAVE without RF and multi-point sweeps survived. Removing
carrier.stop()makes four TX Controls checks fail; restored code passes. - Offscreen Qt tests exercise the actual applets/dialogs, selected/disabled readback, history sizing, diagnostics scrolling/pause, confirmation cancellation and file-dialog lifetime. Removing the Debug resize-only followLatest call, then the resize guard/override behavior, did not fail the applet test on this Qt build; these runs do not demonstrate that particular guard is necessary. No claim of complete mutation coverage.
- Security source review found no confirmed vulnerability in the 26 changed production source files. Parser bounds/checksums, complete backup coverage, device identity fencing and protected EEPROM ranges were checked. Actual Utility interoperability and physical write persistence remain unverified.
- No main-window bridge session or live hardware was driven: the simulator does not implement a connected KAT500. No RF, physical EEPROM writes or destructive maintenance occurred. Windows/Linux and the docs production build were not rerun by this reviewer. The automated code-review skill was unavailable; a separate lifecycle/failure-path pass was performed directly. The prior review's Space-key instruction and private planning-file blockers are removed in this head.
- Current head has passing Static checks, Sanitizer option configures and Build docs site; PDF attachment is skipped. The required cross-platform PR build matrix has not run because ci.yml targets PRs to main. The sanitizer-option check is not a sanitizer execution.
After Jeremy/KK7GWY settles the existing-control default and presentation, land #5991, retarget #6452 to main, regenerate moved references and run the required cross-platform checks. Do not merge into the temporary base branch. The focused evidence supports continued review; physical maintenance/RF acceptance remains open.
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
2 similar comments
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
CI triage: touchpoint manifest — already fixed at the live headShort version: this was a one-cell generated-file drift inherited from the #5991 dependency, it is not your KAT500 code, and you have already fixed it. No action needed. Details below so the record is complete. What failedOnly one job was red on The error was the generic staleness message: I reproduced it in a worktree at -| `models/MeterModel.h` | 6 | mixed(flex) — Meter registry+values are core-profile; …
+| `models/MeterModel.h` | 7 | mixed(flex) — Meter registry+values are core-profile; …Why it driftedThat column counts how many
The seventh includer is Worth noting this is not the usual failure mode for this gate — normally it's a semantic merge conflict where both branches bump the same count to the same value and git takes the line verbatim. Here both parents genuinely shared Already resolved
One thing to keep in mind (confirming your own note)On the temporary base Reviewer commentsTo close that loop explicitly: none of the open review items relate to this failure. @rfoust's two maintainer-decision requests (default-on Thanks for the careful stacked-PR bookkeeping on this one — catching and regenerating both heads yourself, upstream and down, is exactly the right fix and it saved the next person the merge-base walk. Nice work. 73 🤖 aethersdr-agent · cost: $7.3905 · model: claude-opus-5 |
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
|
Published shared RFC #6477 for the KAT500 and KXPA100 TX Controls changes: default-on behavior, AUTO/ATU/SAVE and route indication, optional-tuner coexistence, and shared amplifier-meter precedence. It records that implementation preceded the RFC and requests maintainer approval rather than treating existing code or operator approval as that decision. The KXPA100 precedence findings are explicit open items; this link does not mark them resolved. |
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
2 similar comments
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
|
At Also addressed the transport parity note from the KXPA100 review in this device's own branch: bounded the TCP read buffer to 4096 bytes and rejected malformed host/port input before connection. New socket-free model regressions failed on the old unbounded buffer and pass with the correction; invalid endpoints leave the socket unconnected and do not start the polling timer. Native Mac application build, 9 focused offline suites, production docs and all applicable stacked-branch CI checks pass. No peer socket or serial port was opened in these tests. TX Controls remains radio-side. Pre-tune bands stays in applet Settings and Clear all ATU memories stays in Advanced. Full main-target platform CI remains required after eventual retargeting; the PR is ready for Jeremy's code review, not for bypassing that review or the platform gates. 73, Ozy K6OZY · prepared with Codex |
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
1 similar comment
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
## Summary Tracking: Refs #4953 (KPA500 portion). #6452 delivers the KAT500 portion and carries the closing reference, so #4953 remains open until both device implementations land. Adds a complete KPA500 peripheral with local serial or raw-TCP network access, a resizable applet, and **Peripherals → Advanced** maintenance. The applet displays confirmed amplifier state, keeps stale measurements out of the UI and main power meter, and distinguishes amplifier RF output from the radio's TX flag. This PR delivers the **KPA500 only**. The KAT500 implementation is tracked separately in #6452. ### Credits **Bob Holzer / @WA8PAM** contributed the original KPA500 protocol/parser and command builders, serial connection and polling, first applet, Peripherals setup, main power-meter integration, initial tests, and subsequent fixes to the original review findings. His original commits remain in this branch. **@Ozy311** extended that foundation with the redesigned full/Mini applet, history and debug views, the radio-sourced DRV meter, confirmed power handling, Follow TX band selection, raw-TCP transport, Advanced maintenance, native-dialog crash fix, additional regression coverage, and Windows/Mac hardware validation. This is a continuation of WA8PAM's contribution, with explicit credit to both contributors. ### TX Controls and meter separation Following Jeremy’s direction recorded in #6477, the KPA500 affects the VU/PWR meter source and scale, not the radio-side TX Controls gauge. TX Controls continues to display exciter watts and SWR; RF Power and Tune Pwr values include **%**. Existing radio-integrated amplifier priority is retained. The dedicated KPA500 applet keeps amplifier readback and its Full-mode radio-sourced **DRV** row. ### Applet and connection behavior - **KP500** appears after a valid amplifier reply and remains available while that transport connection stays open, including when the amplifier is off. Full model names remain in titles/settings. - The power button reads **ON** when confirmed on and **OFF** when confirmed off. Standby/Operate, manual band selection, fan minimum, speaker alarm and fault clear follow amplifier readback. - **Follow TX flag** defaults on and follows the TX-assigned slice's transmit band. Disable it to park the amplifier on a manually selected band. It does not key the radio or amplifier. - **RF ACTIVE** derives from fresh amplifier forward-power readback, not AetherSDR's TX flag. The serial protocol does not report the RCA KEY IN/PTT contact. Radio TX separately interlocks power, mode and band changes. - Full uses compact 22 px controls, themed meter-readout pills and major/minor tick marks; SWR stays at 1–5 to match the amplifier face. Mini hides the lower drawers and mode controls, leaving power, band/status, power and SWR in a thin strip. Follow TX moves into the header pill. Full and Mini modes share confirmed state. Expanded history plots power, SWR, temperature, voltage and current; the popout resizes without scattering controls and scrolls when expanded content needs more room. - **DRV**, Full only, shows the connected radio's measured forward power on a 0–100 W scale, independently of shared AMP meter selection. It is before cable loss, not a KPA500 input-sensor measurement or the RF-power setting. Missing, disconnected or over-two-second-old FWDPWR clears the bar and shows a dash; a measured zero remains valid. Mini is unchanged. - Settings provides identity, reply age, transport details, history explanations, temperature units and an **Advanced…** shortcut. Debug supports pause/copy/save and opt-in verbose polling output. - Confirmed power-off stops normal telemetry polling; a five-second power probe detects power-on. Stale data is invalidated, commands are guarded, and serial/TCP reconnect does not replay writes. ### Remote access and aether-serial-bridge Network mode supports remote KPA500 access through a raw TCP serial bridge. The companion **aether-serial-bridge** project is **coming soon**: it runs on the computer connected to the equipment's serial port and makes that port accessible remotely to AetherSDR. Compatible raw-TCP **ser2net** endpoints also work. Local serial access remains available without the companion project. The bridge owns serial baud/framing settings. This PR does not implement Telnet/RFC2217 negotiation, Elecraft Remote Server framing, or a split GUI/AetherD peripheral connection. Peripherals labels local serial as **DIRECT** and TCP as **NETWORK**. ### Advanced maintenance **Peripherals → Elecraft KPA500 → Advanced** adds configuration backup/restore, stored fault history with CSV export, and power-on banner read/edit. Device information also appears in applet Settings. Maintenance is gated to the observed firmware **01.54**. Restore and banner writes require explicit confirmation, identity checks, fresh Standby/zero-RF readback, bounded transfers and matching readback before success. Polling and ordinary controls pause during maintenance; writes are not retried or resumed after reconnect. Cancellation, TX, malformed replies and transport failure abort the operation and report possible partial completion. The native file chooser is asynchronous, parent-owned and limited to one open chooser. This fixes the reproduced Windows COM/Qt crash caused by duplicate blocking file dialogs. ## Screenshots Full below shows the current DRV layout using explicitly replayed telemetry, not a hardware power measurement. Mini, History and Advanced retain earlier native Mac captures from the real KPA500 with zero RF; station identifiers are redacted. The History capture predates the added DRV row and remains a screenshot-refresh follow-up when the bridge is available. The images are included in the amplifier user guide. **Full applet** <img src="https://raw.githubusercontent.com/WA8PAM/AetherSDR/bc1457fc22a21c00545f33dd0ca9c219dfa9e7a6/docs/user/static/img/kpa500/kpa500-applet-drv.png" width="360" alt="KPA500 Full with DRV; replayed illustrative values" /> **Mini applet** <img src="https://raw.githubusercontent.com/WA8PAM/AetherSDR/453240e91d555f6a6e9dfb93663e093a5014aad4/docs/user/static/img/kpa500/kpa500-applet-mini.png" width="360" alt="KPA500 Mini applet" /> **Expanded history** <img src="https://raw.githubusercontent.com/WA8PAM/AetherSDR/453240e91d555f6a6e9dfb93663e093a5014aad4/docs/user/static/img/kpa500/kpa500-applet-history.png" width="420" alt="KPA500 telemetry history" /> **Peripherals and Advanced** <img src="https://raw.githubusercontent.com/WA8PAM/AetherSDR/453240e91d555f6a6e9dfb93663e093a5014aad4/docs/user/static/img/kpa500/kpa500-advanced-window.png" width="720" alt="KPA500 NETWORK connection and Advanced maintenance" /> Current radio-side TX Controls, captured on the Mac while disconnected with no RF: <img src="https://raw.githubusercontent.com/aethersdr/AetherSDR/808544493012bfe80861ca71c1fc6b85f67b3999/docs/user/static/img/kxpa100/tx-controls-radio-only.png" width="300" alt="Radio TX Controls with ATU/MEM, radio scales and percentage power labels" /> ## Constitution principles honored - **II — Radio-authoritative live state:** confirmed amplifier readback drives status, controls and power-meter selection; missing/stale values remain unknown. - **IV — Clean-room contributions:** standard commands use the public programmer's reference. Maintenance framing and file format were independently implemented from observed Utility I/O using an isolated virtual COM pair and artificial device; no proprietary code was disassembled or copied. The design note records the observations and firmware boundary. - **V — Configuration ownership:** connection/UI preferences use the existing nested Peripherals/Kpa500 settings; live hardware identity is not persisted as authoritative state. - **VI — Transmit on intent / VII — Boundary validation:** no automatic keying; bounded frame/file parsing, explicit maintenance-write intent and fail-closed guards. - **VIII — Evidence over assertion / XI — Fixes are demonstrated:** tests cover transport, stale-state, maintenance and UI behavior; the native-dialog regression failed before the fix and passed afterward. ## Validation Reviewed and rebased onto main `7864ef544`; current head is `874cf214e19aae344682b57996f8a77a76d1e2b1`. The rebase preserves the original contributor commits. Its generated-index conflict was resolved by regeneration, and the rebased tree matched a clean merge result before the landing fix. The landing review found and fixed an additional partial-telemetry failure: expired `^WS` measurements could leave the shared meters suppressing radio readings while other amplifier replies remained live. Shared meter routing, suppression and scale now use the same current power-on/Operate/forward-power predicate. Fresh zero watts remains valid. - **Local macOS / Qt 6.12:** all **11/11** selected headless CTests pass: six KPA500 suites, MeterModel, TX Controls power reconciliation, both HGauge suites and the Peripherals automatic-connection case. The connection target compiles without `HAVE_SERIALPORT` and uses only in-memory transport injection. - **Mutation:** removing the forward-power freshness guard causes the two new stale-selection assertions to fail; restoring the guard passes. - **Static/docs:** base/head engine findings are identical after ignoring source-line shifts (102 tracked warnings, zero blockers); capability and command-plane counts do not grow. Test registration, frozen CI gate, generated touchpoints/references, colour ratchet and focused accessibility checks pass. Log rules pass 707 checks; their existing moved-line advisory remains unrelated. `npm ci` and the user-doc production build pass. - **Demo bridge:** the clean `874cf214e` native bundle passed 10 assertions in an offscreen, isolated `DEMO-0001` session: unknown amplifier readings, no TX-band target, Network setup, and disabled backup/restore/banner writes without hardware. TX/MOX/Tune stayed off; the owned instance was disconnected and closed. External-amplifier telemetry remains socket-free injection/source-review evidence, not demo or hardware proof. - **Current-head CI:** [Linux, Windows and macOS](https://github.com/aethersdr/AetherSDR/actions/runs/39044294430), [Static checks and sanitizer configuration](https://github.com/aethersdr/AetherSDR/actions/runs/39044294388), and [Docs](https://github.com/aethersdr/AetherSDR/actions/runs/39044294336) all passed. These runs were created at 2026-10-17T15:16:10Z after current main 7864ef5; post-merge main verification remains separate. Earlier contributor hardware evidence covers firmware 01.54 identity/telemetry, configuration backup, banner reads and stored faults on Windows serial and Mac raw TCP. It is separate from this landing pass. No hardware was contacted or changed in this review. **Not validated on this final revision:** physical restore/banner-write persistence, higher-power RF accuracy, or end-to-end transmitted-power behavior. No transmission, Tune, MOX/PTT, restore or banner writes were performed for this pass. The demo backend cannot validate external amplifier firmware. ## Review scope KPA500 protocol/transport, its applet and Advanced panel, Peripherals setup, meter integration, source registration, tests and user documentation are included. KAT500 remains separate in #6452. Unrelated WSJT-X/a11y edits and the shared ACOM/SPE Standby recolouring from earlier revisions are absent. Shared clear-fault styling is factored without changing ACOM behavior. No CHANGELOG entry, dependency, workflow or gate weakening is included. The radio-only TX Controls direction and percentage labels follow #6477. Existing radio-integrated amplifier priority remains first. The operator requested the review, rebase, blocker repair and guarded merge of this proposed feature. Generated with OpenAI Codex (GPT-6 Astra) --------- Co-authored-by: Ozy311 <Ozy311@users.noreply.github.com> Co-authored-by: jensenpat <patjensen@gmail.com>
… II. Rebase the final KAT500-only delta from 0113445 onto current main, excluding the squash-landed KPA500 dependency. Preserve main KPA500 meter freshness admission and regenerate architecture inventories.
0113445 to
e83398b
Compare
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
jensenpat
left a comment
There was a problem hiding this comment.
Reviewed rebased head e83398b against main bccd4b7. No outstanding code blocker.
The dependency-only KPA500 history is excluded. The rebase preserves main’s hasLiveTxMeters() admission in the shared meter selector; existing KPA500 regression assertions reject stale/missing watts and accept a fresh measured zero. The prior TX Controls takeover/restyle findings are retired by the dedicated-app-only implementation documented in #6477; TxApplet remains main’s radio-side implementation. The numeric relay documentation matches round-down behavior.
Native macOS ARM64 app build passed, with 10/10 selected offscreen CTests passing: seven KAT500 targets, KPA500 connection/freshness, Radio Setup pair grid and TX power reconciliation. Reviewed parser bounds, request expiry/reply fencing, reconnect, maintenance confirmation/readback and captured pre-tune ownership. Architecture gates pass on main/head with unchanged normalized legacy findings; manifest/reference/log-rule/test-registration/frozen-gate checks pass. Hosted Docs and Static checks passed. Linux/Windows/macOS CI remains a separate merge prerequisite while those jobs finish.
Drove only an isolated TX-disabled DEMO-0001 instance: one slice/pan, transmitting=false, native ATU/MEM; added the disconnected KAT500 and opened Advanced, where read/restore/erase/reset were disabled with “Connect the KAT500 first.” The owned process exited. No live radio, RF or EEPROM write was performed; physical device convergence and cross-platform runtime are not proved by this pass. The optional automated code-review skill was unavailable; a manual lifecycle/failure-path review was performed. Existing review threads are answered and resolved.
|
Checked the accessibility advisory against the rebased change at |
jensenpat
left a comment
There was a problem hiding this comment.
Re-reviewed 2892e7d. The only change since my e83398b approval adds Kat500AdvancedWidget.cpp to radio_setup_operations_test, a target introduced on current main. Linux and macOS CI reproduced its undefined constructor; the repaired native target compiles and the offscreen CTest passes in 22.8 seconds. Audited all 14 test targets compiling RadioSetupDialog_Peripherals.cpp: each now includes the KAT500 widget source.
Production source is byte-identical to e83398b, whose native app build, 10/10 focused CTests and isolated TX-disabled DEMO-0001 bridge proof are recorded in my prior review. The four architecture gates and local reference/log/registration/frozen-gate checks passed; the new change does not modify production behavior or workflows. Prior review findings remain addressed and all threads are resolved. New-head hosted platform/Docs checks remain a merge prerequisite. No live hardware, RF or EEPROM write was used.
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
Closes #6512. Adds the compact Elecraft KX100 applet for the KXPA100 amplifier and optional KXAT100 tuner, built on the merged KPA500/KAT500 foundations (#5991, #6452). Supports local serial and raw TCP, confirmed amplifier telemetry, manual tuner controls, Full/Mini presentation, history, diagnostics and Advanced maintenance. - The applet provides Standby/Operate, tuner Auto/Manual/Bypass, antennas, tuner-only Tune/Cancel, Recall/Memorize, physical bypass/attenuator and C/L relays. Telemetry includes PA input DRV, forward/reflected power, SWR, current, voltage, dissipation and temperature. A missing optional tuner dims only tuner-specific controls. - Use with current radio binds the amplifier serial to the exact radio solely for VU/PWR metering and applet frequency context. Association sends no device commands; refused writes retain the previous durable association and display/log the failure. - Fresh associated PA Operate telemetry supplies VU/PWR after higher-priority radio-integrated amplifiers and KPA500. KAT500 KEY BLOCK retains the KPA500 radio fallback. Stale/standby/tuning KXPA100 telemetry falls back to the radio. Main’s KPA500 telemetry-freshness admission is preserved. - TX Controls remains radio-side, following the direction recorded in withdrawn RFC #6477. ATU/MEM keeps the radio’s behavior and availability reasons. - Advanced provides staged per-band antenna configuration/readback, validated atomic native `.KXPAConfig` backup/restore, stored faults/CSV, memory inspection/confirmed erase and separately confirmed reset. Firmware-dependent maintenance is restricted to the verified 01.39 layout. Connecting and opening Advanced perform no maintenance write. Transport/parser queues and receive buffers are bounded, endpoints are validated, and late/uncertain transactions do not replay writes. Basic SWR polling accepts documented `nn.n` decimal replies, including zero, alongside captured compact replies. No Telnet/RFC2217 support, remote power command, firmware updater or raw Command Tester is added. Protocol knowledge comes from public references and isolated Utility I/O observations, without copying proprietary implementation. Validation at rebased head `220f8726e6330ee3b7287cb8a51e76008c2c8a83`: - Native macOS ARM64 app build passed. All 10 selected headless CTests passed: six KXPA100 targets plus KAT500 meter-source selection, Radio Setup pair grid and TX power reconciliation, and the repaired Radio Setup operations target. - Disabling decimal SWR support failed both protocol and production-scheduler model regressions; restoring it passed both. The model cases include fragmented replies, request completion, advancing past the old deadline and continued polling without disconnect. - Isolated TX-disabled DEMO-0001 bridge session: one slice/pan, transmitting=false, native ATU/MEM; disconnected KXPA100 Advanced read/restore/erase/reset controls were disabled with accessible reasons. The owned app exited. - Four architecture checks passed with unchanged normalized legacy findings; generated references and Log Analyzer checks passed. The new Radio Setup operations target includes both peripheral Advanced widget sources; all 14 peripheral-page test targets were audited. Hosted Docs and main-target platform CI are recorded in this PR’s checks. - Peripheral tests use injected transport/time or invalid-endpoint refusal; no synthetic socket/serial firmware peer was run. Actual hardware communication, EEPROM effects, RF/TX and cross-platform runtime operation were not revalidated in this landing pass. The demo does not establish device convergence. The Full meter screenshot uses explicitly labeled replay telemetry and is not a live RF measurement:  Generated with OpenAI Codex (GPT-6 Astra) --------- Co-authored-by: jensenpat <patjensen@gmail.com>
Adds the Elecraft KAT500 tuner applet and Advanced configuration/maintenance tools over serial or raw TCP. Completes the KAT500 portion on the merged KPA500 foundation (#5991). Closes #4953.
.KAT500Configbackup/validated restore, stored fault history/CSV, memory inspection/confirmed erase, and separately confirmed reset. Firmware-dependent maintenance is restricted to the verified 02.16 schema. Opening the panel performs no maintenance write.Serial/raw TCP requests, receive buffering, endpoint validation, timeouts and readback are bounded. Reconnection discards queued mutations. Telnet/RFC2217 and Elecraft Remote authentication are unsupported. Protocol and maintenance implementations use public documentation and isolated Utility I/O observations; no proprietary implementation was copied. Settings use AppSettings and live state remains device-authoritative.
Validation:
The native app and 10 focused tests below were verified at
e83398b19b80f7fc4fcc2649282ce98b2d79d0c6. Current head2892e7d037092bfb68a4906fa278fa2bca5b2738changes only the existing operations-test source list; its production source is identical. The newly addedradio_setup_operations_testsource dependency compiles and its headless CTest passes (22.8 seconds), fixing the Linux/macOS CI link failure. All 14 peripheral-page test targets were audited for that dependency.Generated with OpenAI Codex (GPT-6 Astra)