- S-meter panel switches to TX mode when transmitting: single bar splits
into two sub-bars (power top, SWR bottom) at the same position/size
- Power bar: 0..PAMaxPower W, green; SWR bar: 1..10, amber/red past 2.5
- SWR > 2.5: red border, red label, "SWR High" warning text
- Remove PWR/SWR bars and labels from TX panel (left column)
- Remove FWD/SWR from status bar; remove frequency from RUNNING status
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>
gen1 (TXA PostGen) sits after all modulators and the bandpass filter,
so a positive frequency always produces a tone above the carrier.
For LSB and CWL the tone must appear below the carrier — negate the
frequency for those modes. Also re-apply the tone when mode is switched
while TUN is active.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Browser: AudioWorklet accumulates 960-sample frames, AudioEncoder encodes
to Opus, sends binary WS frame 'M' + raw Opus packet
- Server: opcode $02 handler decodes Opus via libopus, calls OnWebMic callback
- MainForm: WebOnMic converts PSingle→Double, pushes to PushTXMicSamplesD;
ApplyMOX switches mic source to txmsWeb when web client active, restores on TX-off
- WDSPEngine: added txmsWeb to TTXMicSource enum
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add 'dup' field to state JSON (FDisplayDuplex). In CTUN+non-DUP TX,
use vfo_a_hz as spectrum center so the TX signal renders at the VFO
position instead of the display center. In CTUN+DUP TX, keep center_hz
so the RX spectrum stays correctly positioned at the filter.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add freq_a command: sets VFO A regardless of active VFO; fixes A=B and
SWAP when VFO B is active (prev. sent freq_b which overwrote wrong VFO)
- Add pend("freq") to B=A / A=B / SWAP to guard local display during round-trip
- RX step attenuator: HPSDRNetwork.SetStepAtten → bytes 1442/1443 in HP packet;
WebServer attn command; MainForm FAtten + SyncWebAttn; attSel.onchange in JS
- TUN mode: set_tun WebSocket command wired to ApplyTUN(); toneBtn toggles
with on/off highlight and pending protection; tuning field in state JSON
- FNP button opens semi-transparent filter overlay (10 buttons, 5×2 grid)
with mode-appropriate widths (SSB/CW/AM/FM); active filter highlighted;
cur.filter_bw updated immediately on click so passband redraws within 50ms
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
В non-DUP при MOX/TUN на возврате слышен и виден на водопаде хвост
своего сигнала. Во время TX RX-IQ оседал в RXA pipeline / FFT-истории
RX-анализатора и «выстреливал» при открытии гейта.
- WDSPEngine: KeepRXDuringTX (DUP-флаг). В non-DUP TX PushIQItemToDSP
дропает пакеты на входе DSP-потока — RXA и анализатор не кормятся.
- Windows-only post-TX hold (250 мс): дропаем И входящие RX-IQ, И аудио
в окне после MOX-off, чтобы скрыть «хвост» от FPGA TX-buffer/PA
slew-down радио. На Linux код-путь не активируется (буфер OS мелкий).
- Network: ClearDUCIQQueue + IsTransmitting guard в DUC sender thread,
чтобы недопереданные TX-IQ пакеты не уходили после MOX-off.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Два источника искажений при передаче с микрофона компьютера:
1. SoundCard mic путь в TX-потоке (WDSPEngine.pas) при неполном блоке
подменял голосовой хвост в кольце тишиной. PortAudio отдаёт по 128
сэмплов, в кольце часто < FAudioBufSize → ProcessTXBlock уходил в
else-ветку и слал блок нулей, оставляя голос на следующий тик.
Результат: чередование «голос/тишина» = треск ≈50–100 Гц.
Теперь подмена тишиной только при Avail=0; иначе ждём следующий тик.
Добавлен PushTXMicSamplesD — батч-пуш с одной будилкой семафора.
2. DUC IQ packetizer (MainForm.pas/OnTXIQReady) добивал последний
неполный пакет нулями. FTXOutBufSize не кратен 240 (4096 mod 240=16),
из-за чего в каждом TX-блоке возникал провал ≈1.16 мс каждые ≈21 мс
≈ 47 Гц гул. Теперь хвост переносится в следующий вызов через
FDUCPendingI/Q/Count; шлются только полные пакеты по 240 пар.
Дополнительно: ScaleIQ24 клампит результат в [-2^23, 2^23-1] чтобы
пики от EQ/компрессора (V чуть >1.0) не оборачивались через 24-bit
wrap → щелчки на громких сэмплах.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- SWR: было 1+sqrt(Pr/Pf) — теперь (1+ρ)/(1-ρ). Считается только при
FwdW>=1W и HPS_PTT=1, иначе 1.0. Cap 9.9.
- На RX FwdW=0 (ADC даёт шум).
- EMA-сглаживание (α=0.30/0.20/0.05) в DoUpdateStatus на UI-потоке.
- LblFwdPwr/LblSWRVal и StatusBar Supply/FWD/SWR обновляются из
MeterTimerTick (10 Гц), а не на rate HP Status (~150/с).
- PbFwdPower/PbSWR теперь Invalidate в таймере — бары двигаются.
- 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>
FreqDisplay: добавлено свойство ColorHoverBG вместо хардкода $182838
AppTheme: FreqHover, FreqHoverBG, FreqDimDot для обеих тем
UpdateVfoDisplay: выставляет hover-цвета из текущей темы
Co-Authored-By: Claude Sonnet 4.6 <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>
Protocol Appendix A documents only the ADC reference voltage constant;
the PCB voltage divider must also be applied (from Thetis source):
- Boards 1-3 (Hermes/Angelia): ×(4.7+0.82)/0.82 ≈ 6.73, Vref=3.3V
- Board 5 (Orion MkII): ×(22+1)/1.1 ≈ 20.9, Vref=5.0V
For board type 5, supply voltage is measured via UserADC0 (bytes 57-58),
not the supply_volts field (bytes 49-50), matching Thetis behaviour.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The emulator (and real hardware) creates ddc_specific_thread (port 1025),
duc_specific_thread (port 1026), and rx_thread[0..3] (ports 1035+) only
after receiving HP Run=1. Previously ConfigureDDCs and SendDUCSpecific
were sent before SetRunAndFreq(True), so both packets arrived at closed
ports and were silently dropped — ddcenable remained -1, rx_thread slept
forever, no IQ data ever arrived, and the 5-second timeout fired Run=0.
Fix: reorder DoConnectDevice to send Run=1 first, then DDC/DUC Specific.
Also add keepalive resend of the cached DDC Specific packet every 500 ms
for the first 5 seconds after start, to handle thread-startup races on
real hardware over the network.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per Appendix A of openHPSDR Ethernet Protocol v4.3:
- Boards 1,2,3 (Hermes/Angelia): V = ADC/4095 * 3.3 (was always using this)
- Boards 4,5 (Orion/Orion MkII): V = ADC/4095 * 5.0 (was wrong, showing ~66% of real value)
- Board 0 (Atlas) has no supply voltage measurement per protocol spec
("not Atlas bus systems")
- Boards 6 (Hermes Lite), 10 (Saturn): constant not documented, hide Supply field
ADCToSupplyVolts now takes BoardType and returns -1.0 for unsupported boards.
DoUpdateStatus omits the Supply field when -1.0 is returned.
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>
- Remove FillDemoSpectrum from FormCreate: spectrum no longer shows
fake signal data before the transceiver is connected.
- Add SaveStartupPreview to STOP path: startup.vfo_a now matches the
device band cache so the display is consistent before and after
clicking START on a fresh launch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Save current band and global settings in the STOP path so the next
START restores the frequency the user was on, not the hardcoded default.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three params were never synced to FSpecView after FormCreate:
- WDSPReady: now set every timer tick so the draw branch (real
WDSP thresh/hang vs. static AGCTop) is always correct
- AGCThresh/AGCHangLevel: synced from FDSPEngine after
UpdateAGCLines in SpectrumTimerTick
- AGCTop: synced in TrkAGCChange, RestoreBand and DoConnectDevice
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New unit SpectrumView.pas (TSpectrumView): encapsulates all
spectrum, waterfall, ruler and S-meter rendering; ~900 lines
removed from MainForm.pas
- Fix marker bug: FMarkerActive/FMarkerX were duplicated in
MainForm and TSpectrumView — mouse handlers now write directly
to FSpecView, stale MainForm fields removed
- Fix filter band and ruler not following VFO: add SyncSpecViewFreq
helper called at every draw site and at top of SpectrumTimerTick
- Remove dead code: WFALL_PALETTE (34 lines), FSplitter, LblAGC,
LblVol, WinFirewall/IntfGraphics/FPImage from uses
- Fix pre-existing bug: SidetoneFreqLo := 600 overflowed Byte,
replaced with Hi(600)/Lo(600)
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>