QO-100 reuses the transverter path (VFO/band-stack/beacon-lock untouched)
with a split LO for the geostationary transponder:
- TXvtrEntry += FullDuplexTx + TxLOOffset (persisted). Reserve the last
XVTR slot (QO100_SLOT) for QO-100; migration restores the template when
the slot is unset or has empty offsets (fixes old configs showing zeros).
- XvtrTranslateTX: TX LO = visible - TxLOOffset (no LNB LOError) for QO-100;
identical to RX translate for normal transverters. Switched all TX-side
translate calls (controller + MainForm).
- SetDuplex wired (live KeepRXDuringTX); auto display-duplex on QO-100 entry.
- New "QO-100" settings page (LNB LO / transponder offset / downlink edges /
RX gain / TX ceiling + live RX-IF/TX readout); QO slot hidden from the
transverter table. Reuses OnXvtrChange — no MainForm wiring needed.
- doc/PLUTO_INTEGRATION_PLAN.md: phase 5 done.
Builds clean (headless + qt6 GUI). Not yet verified on hardware.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CTun position was lost on band restore / restart, and VHF/UHF bands were
never saved on Pluto.
- SaveCurrentBand: the >61 MHz guard (HPSDR HF-only) skipped saving native
Pluto VHF/UHF bands → 2m/70cm reverted to defaults. Now HPSDR-only; Pluto
saves VU-plan bands (FreqToBandIdx guards cross-band pollution).
- Persist DDC center for CTun: new TBandSettings.CenterHz (+ JSON center_hz)
and TXvtrEntry.LastCenterHz (+ last_center_hz). RestoreBand / ActivateXvtr
restore the saved center when CTun is on and the offset fits the span,
instead of forcing center := VFO (which re-centered the receiver).
- UpdateFreqDisplayMax: freq-display upper bound from active transverter's
FreqEnd (lets QO-100 show 10 GHz past the 6 GHz Pluto cap).
- QO-100 transverter template (slot 2, disabled): downlink 10489.5–10490.0,
LO 9750. MigrateXvtrTemplate injects it into the first free slot for
devices that already have a saved xvtr config (template was otherwise
masked by the loaded slots).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- RX hw-gain (Pluto/AD936x): backend SetRxGain(ModeIdx, GainDb) — gain_control_mode
(manual/fast_attack/slow_attack/hybrid) + hardwaregain. Controller FRxGainMode/
FRxGainDb, commands SetRxGainMode/SetRxGain/RxGainBy, rfRxGain event, snapshot,
applied in StartRunning. Persisted per-device (rx_gain_mode/rx_gain_db). HPSDR
unaffected (backend no-op).
- UI: separate "RF AGC" panel below the (unchanged) WDSP AGC block, shown only for
Pluto — FAST/SLOW/HYB/MAN mode buttons + manual GAIN slider. LayoutLeftPanel
reflows lower panels via RelayoutBelowBands; no layout shift on HPSDR.
- Cold-init fix: direct sampling_frequency write doesn't load the AD9361 FIR
decimation filter, so a cold-booted Pluto showed a wide centre spike that didn't
track tuning (worked only after SDR Console pre-initialised it). Add optional
libad9361 binding (dynamic) and call ad9361_set_bb_rate() in ApplySampleRate,
which configures BBPLL + loads the FIR; falls back to direct write if absent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Active-VFO desync (batch 15 follow-up): DoConnectDevice loaded
FActiveVfo from settings but never re-rendered the highlight (it was
drawn once in FormCreate while the field was still 0) nor re-tuned the
receiver. Result on reconnect with B last-active: the engine (spectrum,
filter band, mouse tuning) ran on VFO B while the UI showed A active.
Now the connect path brings both the receiver and the highlight to the
actually-active VFO via SetActiveVfo, and RestoreBand's final retune
follows the active VFO (A -> ApplyVfoA, B -> SetVfoB) instead of always A.
Bandstack on VFO switch: switching the active VFO to one parked on a
different ham band now restores that band's DSP state (mode/filter/AGC/
CTUN/FM). Extracted ApplyBandDSP (the DSP half of RestoreBand, without
the VFO frequencies) and reused it from both RestoreBand and SetActiveVfo.
VFO frequencies stay independent (each VFO carries its own band);
continuous tuning across a band edge still does not recall (band button
only) as on real radios. SaveCurrentBand no longer writes a VFO frequency
into a band's memory unless that VFO is actually within the band, so a
40m VFO-A no longer pollutes 30m's stored VfoA.
Per-transverter VFO-B: TXvtrEntry gains LastFreqB (load/save/default +
range clamp). ActivateXvtrBand restores VFO B, Deactivate/SaveAllAndExit
persist it, and SetVfoB clamps to the transverter range like SetVfoA.
The connect-time guard that forced VFO A in XVTR is dropped since B is now
a valid, in-range frequency there too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- FILT_* таблицы (BW/DEF/DEV/COUNT) перенесены MainForm -> Settings.pas
(нужны контроллеру для headless-выбора фильтра); UI-подписи FILT_*_NAMES
остались в MainForm (ссылки разрешаются через uses Settings).
- ApplyModeFilter (чистый DSP) перенесён в TRadioController; все вызовы в
MainForm -> FController.ApplyModeFilter.
- Контроллер: ApplyModeDefaults (дефолтный фильтр/девиация под FMode,
зеркало state-части UpdateFilterButtons) + наполнены команды SetMode
(FMode + дефолт + DSP SetMode/TUN + ApplyModeFilter + band cache) и
SetFilterIdx (FFilterBW по таблицам режима + ApplyModeFilter + band cache).
- BtnModeClick/BtnFilterClick — тонкие обёртки к командам; рендер
(StyleButton + UpdateFilterButtons + SyncSpecViewFreq) в
OnControllerState(rfMode/rfFilter).
OnModeFilterSelect (VFO overlay), SyncWebMode/SyncWebFilter (web/CAT),
RestoreBand пока зовут FController.ApplyModeFilter инлайн — мигрируют
в следующих батчах (как и в прошлых: web/overlay позже).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a "Send audio stream to radio (built-in speaker)" checkbox on the
Settings → Audio tab. It is a single switch for the radio's built-in
speaker:
- ON — RX audio is streamed to the radio via the DDC Audio packet
(port 1028, 64 L/R pairs per packet) and the speaker is unmuted
(High Priority byte 1400 bit1 = 0).
- OFF — speaker is muted (byte 1400 bit1 = 1, ANAN-8000DLE IO1 output)
and no audio stream is sent.
The float->int16 conversion and 64-pair packet accumulation live in
THPSDRNetwork (SendSpeakerAudio/SetSpeakerAudio), next to SendDDCAudio;
MainForm only reads the setting and forwards the audio. State is
persisted per-MAC (send_audio_to_radio, default off) and re-applied on
each radio start.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the existing Wideband gradient fill option for the main spectrum:
a "Spectrum gradient fill" checkbox on the Display settings tab gates the
under-curve gradient in TSpectrumView.DrawSpectrum. The curve, grid,
filter band and VFO markers always draw; only the gradient is optional.
Persisted as spectrum_fill in settings JSON (default True, preserving
current appearance). Wired through Settings -> SettingsForm visibility
event -> MainForm -> TSpectrumView.FillSpectrum, same path as
WidebandFill.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New ChannelStore.pas: TChannel record + JSON persistence (channels.json)
- New ChannelsForm.pas: full channel editor (list + fields, pre-fills from current radio state)
- CH button in RX block: left-click opens dropdown, right-click opens editor
- Active channel: button highlights with channel name, deactivates on VFO move
- ApplyChannel: auto-switches XVTR/HF band based on channel frequency; saves old
band state before switching to prevent cache corruption
- SaveCurrentBand: skip save when VFO > 61 MHz (prevents HF cache corruption
from VHF channel application without XVTR)
- DeactivateXvtr: save full XVTR state (was only saving LastFreq)
- CTCSS/RPT from channel marked as auto: cleared automatically when tuning away
from channel frequency; survives band switches via FXvtrSettings auto-flags
- TXvtrEntry: add LastCTCSSAutoActive, LastFMRptAutoActive fields
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LFM: Position = poDesigned (was poScreenCenter, overriding our restore)
- RestoreWindowBounds: remove position scaling (L,T invariant across DPI);
scale size correctly per backend: LCLWin32 uses CurDPI/SavedDPI (physical px),
Qt6/GTK/Cocoa use SavedDPI/CurDPI (logical px, inverse); fix min-size
inflation on Qt6 (was MulDiv(900,DPI,96) which exceeds logical screen width
at 150%+ scale, forcing window to 0,0)
- SaveWindowBounds moved from FormDestroy to FormClose while window is still
stable; add guard for bogus RestoredLeft/Top values on Qt6 when maximized
- RestoreWindowBounds stores FRestoreL/T and re-applies position in AfterShowTick
(200ms after show) when WM frame extents are already known to Qt, fixing the
systematic right/down drift caused by unknown frame extents at FormCreate time
- Save and restore WindowState (maximized flag); skip AfterShowTick position
re-apply when window is maximized
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove manual "Mic Source" (Radio/SoundCard) setting from UI and storage;
source is now determined automatically in ApplyMOX: HW PTT → txmsRadio,
MOX button/CAT → txmsSoundCard if audio device configured, else txmsRadio
- Replace "Line in" checkbox with "Mic jack" combo (Mic In / Line In);
when Line In is selected, Mic Boost is hidden and Line In Gain (-34.5..+12 dB,
step 1.5 dB) is shown and sent as DUC Specific byte 51
- Add Tip/Ring connector selector (Orion/OrionMkII boards 4/5 only, byte 50 bit 3);
control is hidden for other board types
- Fix byte 50 bit 1 (Mic Boost) suppressed when Line In is active
- Fix byte 51 comment polarity (was reversed: 0=+12dB; correct: 0=-34.5dB)
- New JSON fields: mic_line_in, mic_tip_ring, line_in_gain_db; mic_source removed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PA calibration per band (HF and VHF):
- Add VHFBandCal[0..7] in TGlobalSettings (saved as vhf_cal_0..7 in JSON)
- Default HF cal updated to Thetis HERMES defaults (38.8-41.3 dB per band)
- Default VHF cal = 56.2 dB (Thetis HERMES XVTR default)
- SettingsForm: second group in PA tab "VHF Calibration (Transverters)" with
8 spinboxes VHF0..VHF7; labels dynamically show XVTR slot ButtonText
when LoadXvtrSettings is called (e.g. "VHF0 (2m):")
TX drive formula (CalcDriveByte) rewritten to match Thetis exactly:
drive_byte = min(sqrt(10^((10*log10(pwr_W*1000) - Cal)/10) * 0.05) / 0.8, 1.0) * 1.02 * 255
Cal = PA gain in dB (not a percentage); matches Thetis verified output
(Cal=63.1 + slider=100% + PAMax=100W → drive_byte=16, verified against Thetis)
Old formula Pos/100*Cal/100*255 was wrong: treated dB gain as linear percent
Fix stale FDriveLevel not updated on band/XVTR switch:
- Startup: FCurrentBand must be set before CalcDriveByte (was reversed)
- RestoreBand: recalculate FDriveLevel before ApplyVfoA network update
- ActivateXvtrBand: recalculate after FCurrentXvtr changes
- ApplyVfoA: recalculate when VFO crosses HF band boundary
- FreqDispBChanged: same for VFO-B band changes
- ApplyMOX(True): safety recalculate at PTT engage time
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add LastCTun, LastAGCMode, LastAGCTop to TXvtrEntry; SaveCurrentBand
now captures all three in the XVTR branch; ActivateXvtrBand restores
them on slot switch/startup (previously CTun was always reset to False
and AGC was never saved/restored for XVTR)
- WebServer: expose fmstep_idx in state JSON so web clients sync the
FM step from desktop state
- WebPageHtml: swap stepSel options to FM steps (6.25k/12.5k/20k/25k)
when mode=FM, restore HF steps when leaving FM; syncs fmstep_idx
from server on connect and mode change
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix web spectrum scroll resetting freq to XVTR minimum (same root
cause as desktop: hardcoded 60 MHz clamp; now delegates to server)
- Save/restore FM mode, NFM/WFM filter, squelch, CTCSS per XVTR slot
and per HF band (CTCSSOn/ToneIdx added to TBandSettings; full state
added to TXvtrEntry; ActivateXvtrBand restores all on slot switch)
- Fix UpdateFilterButtons always resetting NFM/WFM to default on
mode restore; now preserves saved filter index
- Add FM Step tuning: panel with STEP toggle + 6.25/12.5/20/25 kHz
selector (popup); mouse wheel uses selected step in FM mode;
state saved per band and XVTR slot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FM modulation:
- Replace 10 generic FM filter buttons with NFM (2.5kHz dev/11kHz BW) and FM
(5.0kHz dev/16kHz BW); deviation applied to both RX and TX via WDSP
- Add FM squelch panel (SQL toggle + slider 0..100, per-band save/restore)
with WDSP SetRXAFMSQRun/SetRXAFMSQThreshold (threshold = level/200.0)
- Add CTCSS panel with on/off toggle and custom flat-button tone picker popup
(38 standard tones 67.0..250.3 Hz, default 67.0); tone selection and enable
state saved per band via SetTXACTCSS*
- FM panels (SQL, CTCSS) shown only in FM mode, layout via RelayoutBelowBands
- PanelFilter height shrinks in FM mode so panels sit tight below filters
- FlatButton: handle CMTextChanged to invalidate on Caption change
ADC settings (Dither/Random):
- Add DitherEnabled/RandomEnabled to TGlobalSettings (default true)
- Pass as bitmask to ConfigureDDCs DDC Specific packet bytes 5/6
- Exposed in Settings → Advanced tab with live apply via ApplyADCSettings
Web server settings:
- Add TWebSettings (enabled, port, bind_addr, user, pass) stored globally
- WebServer: configurable port/bind IP via ParseIPv4; Reconfigure method
- Exposed in Settings → Advanced tab with live apply via ApplyWebSettings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
XVTR RF (xvtr_rf in Thetis) is dead code — GetXVTRRF() is never called.
Remove the field, UI column, save/load from TXvtrEntry. UseDDCIn is now
always True when in XVTR mode (previous default behaviour, XVTRRF=False).
Byte 1400 bit 0 (XVTR Enable / VHF T/R relay output) now follows Thetis:
- XVTR mode: set only when DisablePA=True (external T/R relay needed)
- HF mode: set when Alex.EnableXvtrHf is checked (Antenna tab)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Settings > Display: new "VFO Display" control to select max frequency
range (999 MHz / 9.999 GHz / 99.999 GHz), persisted as freq_mhz_digits
- FreqDisplay: extend digit map [0..10] to support up to 11 digit positions;
keyboard navigation adapts to MinMhzDigits dynamically
- MainForm/LayoutTopVfoBlock: VFO panel width scales automatically with
selected digit count using dynamic text measurement
- Web VFO: fix broken HTML (class attr not closed before data-s — wheel and
hover were completely non-functional); rewrite fmtVfo to use variable MHz
digit count synced from server state; fix missing </span> for .vfo-hz
- WebServer: publish freq_mhz_digits in state JSON; updated from
ApplyFreqMhzDigits so desktop and web stay in sync
- Fix Int32 overflow on frequencies above ~2.15 GHz: CATEngine.Pad V param
Int64, FormatFreq/FormatFreqSV Mhz var Int64 (Mhz*1000000 overflowed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BtnTUN рядом с MOX. Включает SetTXAPostGenRun(TXA, 1) с тоном
(mode=0, mag=1.0, freq из настроек) и активирует MOX. PTT/HW
игнорируются во время tune.
- Drive в режиме TUN использует FTXSettings.TUNLevel вместо слайдера,
per-band калибровка FPABandCal применяется одинаково.
- Settings → Transmit → Tune (TUN): TUNLevel 0..100% (def 10),
TUNFreq 100..5000 Hz (def 750). Live-обновление во время tune.
- DUP: локальный режим отображения, при TX данные RX1, поверх рисуется
TX-фильтр. Кнопка на левой панели рядом с CTUN. Аппаратной командой
не является. Отдельный SetDisplaySourceTX в WDSPEngine, разделение
TXMode (data) и TXOverlay (filter band) в SpectrumView.
- ATT on TX: 0..31 dB, default 31 (Thetis-style). UI на вкладке Transmit
в группе Microphone. Передаётся в DUC Specific bytes 57/58/59
(StepAtten0/1/2). Перепосылается при изменении.
Декаплинг TX от RX sample rate (как в Thetis):
- WDSPEngine.pas: константа TX_SAMPLE_RATE=192000, поля FTXSampleRate/FTXOutBufSize.
TXA OpenChannel теперь использует FTXSampleRate как out_rate, TX-анализатор
через SetDisplaySampleRate(TX_DISP_ID, FTXSampleRate). ProcessTXBlock работает
с FTXOutBufSize вместо FBufSize. Public TXSampleRate для MainForm.
- MainForm.SendDUCSpecificFromSettings: DUC0Rate теперь берётся из
FDSPEngine.TXSampleRate, а не из RX-настроек — TX всегда 192k независимо от
выбранного RX-rate (48/96/192/384k).
DUC Specific cache + keepalive resend (HPSDRNetwork.pas):
- FCachedDUCSpec/FCachedDUCValid параллельно DDC-кэшу.
- KeepAlive thread в первые 5s после Run=1 переотправляет и DDC, и DUC
Specific — устраняет гонку, когда DUC настройки терялись железом.
- Disconnect сбрасывает оба valid-флага.
Per-device TX grid settings (Thetis-style):
- Settings.pas: TXSpecRefLevel/TXSpecRange/TXSpecGridStep в TTXSettings
(defaults 0.0/80.0/10.0). LoadTX/SaveTX — ключи tx_spec_ref_level/range/grid_step.
- SettingsForm.pas: новая группа \"TX Grid\" во вкладке Transmit
(RefLevel edit, Range edit, GridStep combo). CollectTXFromUI читает значения,
LoadTXSettings заполняет UI.
- MainForm.pas: поля FTXSpecRefLevel/Range/GridStep, ApplySpecViewGridFromState
пушит в FSpecView либо TX, либо RX параметры в зависимости от FTransmitting.
ApplyGridParams (RX-путь) уважает FTransmitting и не перебивает TX-grid.
OnTXSettingsChange при активном TX моментально применяет новый grid.
TX-aware spectrum/waterfall с CTUN-выравниванием (SpectrumView.pas):
- Поля FTXMode/FTXFreq/FTXSpanHz/FTXVfoIndex. TXSpanHz=192000 при создании.
- DrawSpectrum/DrawWaterfall в FTXMode мапят данные TX-анализатора (192k вокруг
FTXFreq) в RX-координаты FCenterFreq/FSpanHz. Формула:
freq = FCenterFreq - FSpanHz/2 + x*FSpanHz/W
idx = (freq-FTXFreq + TXSpanHz/2)/TXSpanHz * (SrcCount-1)
Вне диапазона — спектр уходит в -200 dB; водопад копирует пиксель из
предыдущей строки (FWfPixels[X+W]) — вместо чёрной полосы продолжает
скроллиться старая RX-картинка.
- Эффект: при CTUN ON TX-сигнал рисуется на полосе фильтра (на VFO), а не в
центре. При RX rate ≠ 192k частотная сетка совпадает между RX и TX
автоматически (ruler всегда в RX-координатах).
- MainForm.SyncSpecViewFreq/ApplyMOX обновляют TXFreq/TXVfoIndex/TXMode при
каждом PTT и смене VFO; учитывается split (FSplitTxB → TXVfoIndex=1).
Alpha-blended filter bands (визуальный feedback TX/RX):
- SpectrumView: PixelFormat:=pf32bit, процедура BlendBand через ScanLine[Y]
внутри BeginUpdate/EndUpdate. Альфа-смешение по формуле
(a*src + (255-a)*dst) / 255 для каждого канала BGR.
- В FTXMode полоса фильтра становится полупрозрачно красной. Если split
(FTXVfoIndex<>FActiveVfo): на RX-частоте полоса зелёная, на TX-частоте —
красная (CalcFilterBandX вычисляет границы для произвольной VFO-freq).
- При FTXMode=False — старое поведение (сплошная заливка SpecFilter).
Build: lazbuild --ws=qt6 ewsdr.lpi — clean, 0 errors.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Settings: TTXSettings record + LoadTX/SaveTX, JSON section "tx" per MAC
- WDSPEngine: separate TX_DISP_ID analyzer with own FFT/Window/Det/Avg
parameters, ApplyTXAnalyzerSettings + setters
- WDSPEngine: full TX chain setters (Filter/Compressor/Leveler/ALC/
PhaseRot/EQ/AMCarrier/FM/CTCSS) + ApplyTXChainSettings
- WDSPEngine: ProcessTXBlock now feeds TX analyzer in DISPLAY_BLOCK_SIZE
chunks (was passing whole FTXOut → analyzer ignored most of it)
- WDSPEngine: ActiveDisplayID switches RX/TX in UpdateSpectrum
- WDSPEngine: RX audio muted while TX active to avoid local feedback
- WDSPEngine: default EQ frequencies in Create (zero freqs broke TX chain)
- WDSPEngine: PushTXMicSampleD + OnPullMicSamples for sound-card path
- AudioInput: Available counter for non-blocking pull
- MainForm: PullSoundCardMic, BuildMicLineSelectByte, SendDUCSpecific
with mic bias/boost/line/PTT bits, OnTXSettingsChange
- MainForm: auto-switch MicSource on TX device select, TX diagnostic
in StatusBar panel [5]
- SettingsForm: new Transmit nav page with Microphone, Filter, Dynamic
processing, EQ, AM/FM/CTCSS, TX Display sections
NOT YET WORKING: sound-card mic produces ring activity but signal still
not visible on TX spectrum/waterfall — needs further investigation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- AppTheme.pas: новый файл, TAppTheme record + DarkTheme()/LightTheme()
- FlatSlider: цвета через SetThemeColors вместо хардкода
- SpectrumView: все цвета из TAppTheme (SetTheme)
- SettingsForm: ApplyTheme(T) с рекурсивным обходом контролов
- DeviceForm: SetTheme(T) — перекраска всех элементов включая TEdit
- Settings: SaveTheme/LoadTheme в корневой секции preferences (не привязано к устройству)
- MainForm: тема загружается при старте, сохраняется при изменении
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Settings.pas: add root-level SaveCATSettings/LoadCATSettings so CAT
config is stored independently of device MAC.
- MainForm.pas FormCreate: initialize CAT defaults, load from saved
'cat' JSON section, and apply immediately after InitCATEngine — so
SettingsForm shows correct values before the transceiver connects.
- MainForm.pas OnCATSettingsChange: always save to root 'cat' section
(not only when device is connected) so changes persist immediately.
- Default TCP CAT port changed from 4992 to 19090 everywhere.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New units: CATEngine (Kenwood + Thetis ZZ* commands), CATSerial
(up to 4 serial ports), CATTcp (multi-client TCP server on port 4992).
MainForm wired with TCATContext callbacks reusing existing WebOn* handlers.
SettingsForm gets a new CAT tab with 4 serial port groups and TCP section.
Settings.pas extended with CAT config fields (per-device, JSON-persisted).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>