Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion BUILD-OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ alone. MSVC flags and `Release` builds are unaffected.
|---|---|---|
| `ENABLE_DEEPFIST_EXPERIMENT` | OFF | Build the experimental DeepFist CW receive decoder. Requires ONNX Runtime at configure time (configuration fails without it) and a separate verified model bundle at runtime; see below. |
| `ENABLE_RTL` | ON | Build the experimental receive-only RTL-SDR USB backend when both `librtlsdr` and single-precision FFTW (`fftw3f`) are found. Missing either disables the backend unless `REQUIRE_RTL=ON`. |
| `AETHER_HL2_TX_TXA` | ON | Select WDSP's TXA chain for the Hermes-Lite 2 SSB transmit modulator. OFF builds the in-tree phasing modulator. This choice has no runtime toggle. |
| `AETHER_HL2_TX_TXA` | ON | Select WDSP's TXA chain as the Hermes-Lite 2 transmit modulator: SSB, AM, DSB and FM. OFF builds the in-tree phasing modulator, which is SSB only; AM, DSB and FM then stay receive-only. This choice has no runtime toggle. |

**DeepFist:** enabling `-DENABLE_DEEPFIST_EXPERIMENT=ON` alone does not supply its
model. The configured model-download URL defaults to empty. For developer
Expand Down
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ option(REQUIRE_RTL "Fail configuration unless the RTL backend and float FFTW are
# signed-16-bit wire (MetisProtocol.cpp, ep2WriteTxIq), so TXA's
# advantage THERE is below the wire and unusable.
# OFF — the in-tree phasing modulator, unchanged. The fallback for a machine or
# a report where TXA misbehaves.
# a report where TXA misbehaves. SSB and digital only: AM, DSB and FM
# stay receive-only in this build.
#
# THIS LINE USED TO SAY "kept building and kept tested" AND THE FLIP
# BELOW MAKES THAT FALSE. `grep AETHER_HL2_TX_TXA .github/workflows/*.yml`
Expand Down Expand Up @@ -234,7 +235,7 @@ option(REQUIRE_RTL "Fail configuration unless the RTL backend and float FFTW are
# radio was keyed. No antenna port was observed." -- now exists, and it is named
# rather than asserted.
option(AETHER_HL2_TX_TXA
"Hermes-Lite 2 SSB transmit modulator: ON (default) uses WDSP's TXA chain, OFF the in-tree phasing modulator"
"Hermes-Lite 2 transmit modulator: ON (default) uses WDSP's TXA chain, OFF the in-tree phasing modulator (SSB only)"
ON)
# Lets AETHER_FIRMWARE_CATALOG and AETHER_FIRMWARE_TRUST_KEYS replace the
# firmware catalog and add firmware-signing keys, for testing a publisher's own
Expand Down
9 changes: 9 additions & 0 deletions docs/HERMES.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,15 @@ in `third_party/wdsp/upstream/` and were **not** confirmed on the air.
TXA's bandpass both reach it through `CalcBandpassFilter`/`fir_bandpass` with
`rtype = 1`, so this one function is the mechanism behind both the RX bullet
above and the TX correction here.
- **TX AM, DSB and FM (TXA build only; the phasing build refuses to key them)
take a SYMMETRIC passband, and that is not a sideband rule.** For `TXA_AM`, `TXA_DSB`, `TXA_SAM` and `TXA_FM`, `TXASetupBPFilters`
runs `bp0` *before* `xammod`/`xfmmod`, and both modulators read only the I
output — so the band is an audio low-pass. `Hl2TxDsp::txaPassband()` maps the
positive `{100, 3000}` to `[-3000, +3000]` (piHPSDR's `tx_set_filter` uses the
same `(-high, high)` for AM and DSB); a one-sided band there halves the
modulation. `WdspChannel::open()` also turns off the CTCSS encoder
`create_txa` builds switched **on** (100 Hz, level 0.10), and pushes the AM
carrier (0.5) and FM deviation (5000 Hz) from `Hl2TxLevelPolicy.h`.

The trap: RXA and the phasing modulator use **opposite conventions**, and both look
plausible. A table written for one and reused for the other is silently wrong on
Expand Down
19 changes: 11 additions & 8 deletions docs/architecture/radio-capabilities-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ the production dialog and checks its displayed peak level and frequency.
| `tuningMinHz` / `tuningMaxHz` | — (0/0) | 0.1–38.4 MHz | — (0/0) | `MainWindow_Wiring.cpp`, `applyTuningRangeToOverlayMenu` | Refuses band buttons the receiver cannot reach. 0/0 means unconstrained |
| `declaredBandRanges` | — (empty) | — (empty) | — (empty) | `SpectrumOverlayMenu::setDeclaredBands` | Optional canonical-name + native coverage list used for backend-authoritative band labels. Icom publishes the IC-9700's three discontinuous deck ranges; empty keeps canonical labels and existing Flex/HL2/Sim presentation |
| `canTransmit` | ✅ | `m_txAllowed` | ❌ | `RadioModel::setTransmit`, MOX/TUNE key guards | **TX safety gate.** Fail-closed: false denies any keying intent |
| `receiveOnlyModes` | — (empty) | AM · SAM · DSB · FM · NFM · WBFM · WFM · DRM | — (empty) | `RadioModel::refuseKeyInReceiveOnlyMode` (MOX / TUNE / CW-key / `setTransmit`) | Modes the radio **demodulates but will not transmit in**, in the neutral vocabulary. Empty = transmits in everything it receives. Refusing here (not in the backend) is what rolls back `TransmitModel`'s optimistic MOX/TUNE state — a backend cannot reach `TransmitModel`, so a refusal made down there leaves the TX indicator lit and TUNE latched. Icom: `["WFM"]` on the IC-705, which receives 76–108 MHz broadcast and does not transmit there (#5040). HL2: `Hl2TxDsp` is a phasing SSB modulator whose stored mode is read only by `isLowerSideband()`, so every mode above takes the USB branch and would go on the air as SSB announcing nothing — both spellings of each alias are listed because the guard is a membership test on the string the slice holds, not alias normalisation — `Hl2Backend::setSliceMode` and `applyRestoredState` both canonicalise now, so an HL2 slice should only ever hold the offered spelling, and the alias entries are kept because a list that is only correct while every writer stays canonical is the wrong kind of correct. **Note what a non-empty list costs:** `beginLocalTxActivity` runs this guard for EVERY `TxActivity`, so it withdraws TUNE in those modes as well as MOX and CW keying, even where the tune carrier itself would be clean |
| `receiveOnlyModes` | — (empty) | SAM · WBFM · WFM · DRM (default TXA build); plus AM · DSB · FM · NFM with `-DAETHER_HL2_TX_TXA=OFF` | — (empty) | `RadioModel::refuseKeyInReceiveOnlyMode` (MOX / TUNE / CW-key / `setTransmit`) | Modes the radio **demodulates but will not transmit in**, in the neutral vocabulary. Empty = transmits in everything it receives. Refusing here (not in the backend) is what rolls back `TransmitModel`'s optimistic MOX/TUNE state — a backend cannot reach `TransmitModel`, so a refusal made down there leaves the TX indicator lit and TUNE latched. Icom: `["WFM"]` on the IC-705, which receives 76–108 MHz broadcast and does not transmit there (#5040). HL2: the list depends on the modulator `AETHER_HL2_TX_TXA` selects at build time. The default WDSP TXA build modulates AM, DSB, FM and NFM — a symmetric `bp0` (`Hl2TxDsp::txaPassband`), CTCSS forced off, explicit AM carrier 0.5 and FM deviation 5000 Hz — and `hl2_txdsp_test` reads carrier-plus-sidebands, suppressed carrier and constant-envelope FM off the emitted IQ. SAM stays (a receive detector; TXA would send plain AM), WBFM/WFM stay (WDSP TX defines no WBFM mode, `WdspChannel::validateConfig`), DRM stays (no encoder). The phasing build (`OFF`) reads the stored mode only through `isLowerSideband()`, so every mode on the longer list would go on the air as USB announcing nothing, and that build keeps all eight. Both spellings of each alias are listed because the guard is a membership test on the string the slice holds, not alias normalisation — `Hl2Backend::setSliceMode` and `applyRestoredState` both canonicalise now, so an HL2 slice should only ever hold the offered spelling, and the alias entries are kept because a list that is only correct while every writer stays canonical is the wrong kind of correct. **Note what a non-empty list costs:** `beginLocalTxActivity` runs this guard for EVERY `TxActivity`, so it withdraws TUNE in those modes as well as MOX and CW keying, even where the tune carrier itself would be clean |
| `hostModulates` | — (❌) | ✅ | — (❌) | `TciServer`, `MainWindow_Session` | Mic source collapses to PC; PC-audio lock. **Not the same question as `takesTxAudioOverSeam`** — see below |
| `takesTxAudioOverSeam` | ❌ | ✅ | ❌ | `MainWindow_Session` (capture, TX stream, PC-audio lock), `AudioEngine::setHostModulation`, `RadioModel::ensureDaxTxStream` | Whether transmit audio leaves through `submitTxAudio` rather than a DAX/VITA-49 stream. Icom: ✅ |
| `hasRadioPttReadback` | ❌ | ❌ | ❌ | `RadioModel::publishCommandedBackendTransmitEdge`, `Ax25HfPacketDecodeDialog::beginTransmitWhenReady` | The backend's `transmitChanged` / `keyingStateConfirmed` carry the **radio's own** PTT readback and `setKeying()` is intent only. True suppresses RadioModel's command-edge fallback, and makes AetherModem wait for `radioTransmittingChanged` / `radioTransmitConfirmed` (or an already-keyed radio) before releasing sample zero. Flex: ❌ because its interlock edge is decoded inside RadioModel, not through the seam. Icom: ✅ (decoded CI-V `1C 00`, #5311) |
Expand Down Expand Up @@ -247,7 +247,7 @@ the production dialog and checks its displayed peak level and frequency.
| `lineoutControl` | ✅ | absent | absent | `RadioModel::lineoutThroughSeam`, `lineoutControlsAvailable` (title-bar speaker, Radio Setup Line Out row) | Optional record: the radio has its own line out. Flex by its command plane (`mixer lineout`); otherwise `IRadioBackend::setLineoutGain` / `setLineoutMute` must apply it, and RadioModel sends them typed instead of the text. ANAN: present (speaker silence and host-side scaling). Absent (HL2, Icom, RTL): the title-bar speaker mutes only this computer's output (refused with the unsupported-control notice when PC Audio is off too), Radio Setup's Line Out row is dimmed with its reason, and any other `mixer lineout` text keeps its drop notice (#4665). Sim: absent, but its synthetic command plane takes the text, so it behaves as a Flex here |
| `txWaterfallClientFlag` (record) | absent | **engaged**, `clientPersistsFlag` ✅ | absent | `RadioModel::requestLocalShowTxInWaterfall` and `RadioModel::restoreClientShowTxInWaterfall` → `RadioCapabilities::clientPersistsShowTxInWaterfall()` | Who owns "Show TX in Waterfall" (`TransmitModel::showTxInWaterfall()`) on a radio that neither stores nor echoes it. Engaged with `clientPersistsFlag` true makes the client the owner: Radio Setup and the bridge's `txwaterfall` set the transmit model directly instead of sending `transmit set show_tx_in_waterfall=`, the value is written per radio to `ClientDisplay.showTxInWaterfall`, and it is restored on connect, all three through the one accessor. Absent keeps a family's existing behavior. **HL2 only** (no register, no echo, stores nothing itself). Flex: absent, the radio stores and echoes the flag. Icom, ANAN, RTL and Sim: absent, no owner declared; the wire text is sent, nothing answers it and the client stores nothing. Engaging another family is that family's declaration to make |
| `hasTransmitFrequencyCheck` | ❌ | ❌ | ❌ | `VfoWidget` and `RxApplet` through `RadioModel` | Replaces persistent REV with momentary XFC on verified radios. Icom: ✅ for IC-705 and IC-9700 (`1C 02`); radio readback/polling owns the visual state, and release always sends OFF |
| `fmTonePresentation` | Legacy | **Hidden** | Legacy | `VfoWidget`, `RxApplet` | Icom is model-profile driven: IC-705 and IC-9700 expose their documented extended CTCSS/DTCS surface; basic repeater models retain Legacy; unattested models hide tone controls. **HL2 is Hidden**: Legacy fills the tone-mode combo from `legacyFmToneModes()`, i.e. it OFFERS `ctcss_tx`, and this radio has no CTCSS encoder — nothing under `src/core/backends/hl2/` mentions CTCSS at all, and `Hl2Backend` overrides none of `setSliceFmToneMode`/`setSliceFmToneValue`/`setSliceFmToneRxValue`/`setSliceFmDtcs`, which are empty virtuals in `IRadioBackend`. The ground that holds in BOTH build configurations is that FM and NFM are on `receiveOnlyModes`, so `RadioModel::refuseKeyInReceiveOnlyMode` refuses every `TxActivity` in them — not an argument from the modulator, which `AETHER_HL2_TX_TXA` selects at build time and which defaults to WDSP's TXA. Receive is untouched; what is withdrawn is a transmit-side control |
| `fmTonePresentation` | Legacy | **Hidden** | Legacy | `VfoWidget`, `RxApplet` | Icom is model-profile driven: IC-705 and IC-9700 expose their documented extended CTCSS/DTCS surface; basic repeater models retain Legacy; unattested models hide tone controls. **HL2 is Hidden**: Legacy fills the tone-mode combo from `legacyFmToneModes()`, i.e. it OFFERS `ctcss_tx`, and this backend exposes no tone-encode verb — `Hl2Backend` overrides none of `setSliceFmToneMode`/`setSliceFmToneValue`/`setSliceFmToneRxValue`/`setSliceFmDtcs`, which are empty virtuals in `IRadioBackend`. The ground that holds in BOTH build configurations is that no verb is behind the control: in the default TXA build FM keys, and TXA's own CTCSS encoder (on by default in `create_txa`) is forced off by `WdspChannel::open()` rather than wired to these controls; in the phasing build (`-DAETHER_HL2_TX_TXA=OFF`) FM and NFM are on `receiveOnlyModes` and cannot key at all. Receive is untouched; what is withdrawn is a transmit-side control |
| `fmToneModes` | empty | empty | empty | `VfoWidget`, `RxApplet` | Authoritative selectable access-mode vocabulary. IC-705 and IC-9700 publish all eight states documented for each model at `16 5D`; IC-7300MK2 retains its narrower activated path |
| `fmDtcsCodes` | empty | empty | empty | `VfoWidget`, `RxApplet` | Authoritative operator-intent vocabulary for the DTCS selector. Every backend declares empty explicitly; the activated IC-705 and IC-9700 extended profiles publish the standard 104-code set. Empty means no DTCS control, never an invented default |
| `hasHostNoiseBlanker` | ❌ | ✅ | ❌ | `RadioModel::hasHostNoiseBlanker()` → `VfoWidget::setHasHostNoiseBlanker` | **THIS HOST** blanks impulse noise in the radio's IQ (WDSP ANB, ahead of the demodulator). OR'd with `hasRadioSideDsp` at the NB button, so a direct-sampling radio gets NB without claiming firmware DSP it does not have — the same exception the manual notch makes. Requires an IQ path this host demodulates: a backend fed finished audio has nothing to blank. Icom: ❌ (the radio's own blanker, under `hasRadioSideDsp`). ANAN: ✅ — the same WDSP ANB, in `AnanRxDsp`. **Not** permissive on disconnect — it can only ADD the button |
Expand Down Expand Up @@ -925,9 +925,11 @@ predicates the readouts ask before printing. See [`HERMES.md`](../HERMES.md)
- **Hl2Backend** reports `canTransmit` from its own TX gate (`m_txAllowed`) so a
build with transmit disabled looks RX-only from above the seam. Its
`receiveOnlyModes` is hand-listed rather than derived, and the evidence for the
list is a test, not the list: `tests/hl2_txdsp_test.cpp` asserts each declared
mode produces IQ **bit-identical** to USB, and `tests/hl2_family_transition_test.cpp`
asserts the live backend still declares every one of them.
list is a test, not the list. In the default TXA build `tests/hl2_txdsp_test.cpp`
measures AM, DSB and FM off a real TXA channel; in the phasing build it asserts
each declared mode produces IQ **bit-identical** to USB. In both,
`tests/hl2_family_transition_test.cpp` asserts the live backend declares exactly
the list for that build.
- **IcomCivBackend** derives `receiveOnlyModes` from `modeListFor()` filtered by
`modeIsReceiveOnly()` rather than listing it a third time, so a mode cannot be
offered in the combo without a consistent transmit answer for it. It also
Expand Down Expand Up @@ -963,9 +965,10 @@ prevent.
pins the two FM-control declarations this radio must NOT make —
`hasFmRepeaterOffset` (whose struct default is the opposite value, so the
default is asserted separately and the two facts fail apart) and
`fmTonePresentation`. The reason for both is read from production —
`receiveOnlyModes` and `legacyFmToneModes()` — rather than re-typed, so if the
HL2 ever gains an FM transmit chain that target is the one that fails first.
`fmTonePresentation`. In the default TXA build the HL2 has an FM transmit chain,
and that target asserts it (FM and NFM are off `receiveOnlyModes`) beside the two
withdrawals, which stand because no verb is behind either. In the phasing build
it asserts FM and NFM are still on the list.

[`tests/show_tx_in_waterfall_client_flag_test.cpp`](../../tests/show_tx_in_waterfall_client_flag_test.cpp)
pins `txWaterfallClientFlag` on the real backends: the HL2 stores and restores
Expand Down
7 changes: 4 additions & 3 deletions docs/user/docs/hermes-lite-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ RIT and XIT work per receiver, ±9999 Hz. When transmit passes to another receiv
- **Show TX in Waterfall:** **Settings → Radio Setup... → Transmit → Show TX in Waterfall** lets the waterfall draw spectrum rows over the TX passband while transmitting. It starts off, and AetherSDR remembers your choice separately for each HL2 across reconnects and restarts. The control does not key the radio.
- **Max Power:** **Settings → Radio Setup... → Transmit → Max Power** shows the HL2's rated watts, read-only. The power gauge uses a 5 W scale, and forward power reads near PEP on speech.
- **RF power slider:** the radio has 16 drive steps, so several slider positions do nothing and then one position adds about 1.25 dB.
- **Modes that refuse to transmit:** AM, SAM, DSB, FM, NFM, WBFM and DRM refuse MOX and TUNE, because the HL2 modulator is SSB only. A mode the HL2 cannot demodulate is refused everywhere.
- **AM, DSB and FM** transmit through the same TXA modulator as SSB. FM is simplex and carries no CTCSS tone.
- **Modes that refuse to transmit:** SAM, WBFM and DRM refuse MOX and TUNE. SAM is a receive detector choice; wide FM has no transmit modulator, and DRM has no encoder. A mode the HL2 cannot demodulate is refused everywhere.
- **TX drive and LNA gain** are remembered per band.

> **Upgrading from an older version:** CW keys at the same carrier level as TUNE, about 6 dB (four times the power) above earlier versions at the same RF power setting. Check your RF power before you key CW into an amplifier or a low-power load.
Expand Down Expand Up @@ -197,9 +198,9 @@ The receivers share one sample rate, and only one pane controls it.

### MOX or TUNE is refused

The HL2 modulator is SSB only, so AM, SAM, DSB, FM, NFM, WBFM and DRM refuse to transmit.
SAM is receive-only. WBFM has no transmit modulator, and DRM has no encoder, so those modes also refuse to transmit.

1. Switch the slice to USB, LSB, CW or a data mode before keying.
1. Switch the slice to USB, LSB, AM, DSB, FM, CW or a data mode before keying.

### The Calibration controls are disabled

Expand Down
2 changes: 1 addition & 1 deletion docs/user/src/components/LogAnalyzer/rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@
"source": [
{
"file": "src/core/backends/hl2/Hl2Backend.cpp",
"line": 2844,
"line": 2848,
"text": "HL2: could not open the UDP socket"
}
]
Expand Down
Loading
Loading