Replaces TProgressBar in TWisdomProgressDialog with TFlatProgressBar,
drawn entirely via Canvas (plain rectangle, no native widget styling).
Dialog now receives the active TAppTheme so both dark and light themes
are applied consistently to background, label, and progress bar.
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 ResizeSMeter (Pango font measurement + multiple SetBounds) from 60 Hz timer tick;
it is still called on actual resize events
- Throttle UpdateAGCLines WDSP API calls from 60 Hz to ~10 Hz (every 6 ticks)
- Optimize spectrum alpha-fix loop: write-only byte access instead of read-modify-write DWORD
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SpectrumView: rewrite DrawSpectrumGradient to blend directly into
FSpectrumBitmap via ScanLine instead of TLazIntfImage+BitBlt (stale DC
on win32). Add alpha=$FF fixup pass so pf32bit Canvas.Draw renders via
StretchBlt not AlphaBlend. Remove FSpecGradImg/FSpecGradBmp fields.
MainForm: mask exZeroDivide/exInvalidOp in FormCreate so WDSP DLL
divide-by-zero does not crash the DSP thread on win32 (Qt6 already
masks FPU exceptions globally).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add SampleRateOverlay.pas: TCustomControl overlay (top-left of spectrum)
with hide-panel button and 6 span buttons (48k–1536k), same hit-test
pattern as VfoOverlay
- Consolidate toolbar: move PanelTopVfoGroup and PanelSMeterRight into
PanelToolbar (height 72) instead of floating on the form
- Remove PanelSpanButtons (alTop spacer) that caused the ~1cm black strip
at the top of the spectrum
- Add separator line inside PanelToolbar anchored to its bottom edge,
with 16px left/right margins
- Fix left panel blocks starting Y offset (was 38, now 0)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AGC button in DSP row shows current mode (FAST/MED/SLOW/LONG/OFF).
Click toggles a picker row below; selecting a mode closes it, fires
OnAGCChange, and syncs MainForm BtnAGCMode buttons and DSP engine.
Overlay height expands 136→159 px while picker is open.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves CTCSS tone picker and FM step picker from inline TPanel+TFlatButton
arrays into a standalone FlatDropDown.pas module. TFlatDropDown owns the
popup panel, handles positioning and item selection, and fires OnSelect.
ApplyStyle syncs button colors on theme change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Third button row mirrors MainForm NR (cycle 0-4), NB (cycle 0-2),
SNB and ANF toggles. OnDSPChange event syncs state back to MainForm
buttons and DSP engine. Overlay height tightened to match top/bottom
padding.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- FM filter row now shows only NFM (11 kHz) and FM (16 kHz) matching MainForm
- OnModeFilterSelect updates FFMDeviation and FFilter index for FM mode
- Mode and filter button widths distributed evenly (proportional, not fixed BtnW)
- S-meter inset by extra 4 px on each side
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TSpectrumView remains the public facade for MainForm; waterfall, S-meter,
and ruler rendering are now in isolated units. Dead code removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gives 44ms jitter margin for WiFi connections. Sound card mic path
is unaffected — StallCount never increments when FOnPullMic fills
the ring every tick.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pre-roll bug: when MIC_PREROLL_SAMP is an exact multiple of OPUS_FRAME_SAMP
(1920/960=2), the N<=Want branch filled the buffer but never flushed it,
losing the first 40ms of audio and leaving the ring empty long enough for
silence blocks to be injected into WDSP.
StallCount threshold raised from 2 to 4 (~43ms) to cover OS scheduler
jitter of up to 22ms, preventing false silence injection when an Opus
frame arrives slightly late relative to the DSP tick.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Opus decodes 960 samples per frame; the TX DSP thread consumes
512-sample blocks. 960 mod 512 = 448, so the ring always kept a
sub-threshold tail, causing TTXDSPThread to skip ticks and starve
the DUC hardware every ~20 ms.
FlushMicToCallback accumulates samples in FMicStageBuf and calls
FOnWebMic only when a full 512-sample block is ready. The remainder
stays in the buffer until the next Opus frame arrives. This ensures
FTXMicRing always receives data in multiples of 512, eliminating
the 0 < Avail < 512 dead-zone entirely.
FMicStageLen is reset together with FMicPreRollPos on new TX session
to prevent stale samples from a previous session mixing in.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
WebOnMic: drop intermediate Singles[] array, read PSingle directly.
PushAudio: replace per-frame SetLength/array of Byte with pre-allocated
FWsAudioBuf field — eliminates heap allocation from the DSP thread.
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>
WebPageHtml.pas:
- Add MIC gain slider (0..300%) with localStorage persistence; inserts
Web Audio GainNode between MediaStreamSource and Opus encoder.
- Disable autoGainControl in getUserMedia so the manual gain actually works.
- Pre-create+resume AudioContext synchronously inside the MOX click handler
(before any await) so user-gesture context isn't lost during getUserMedia
on macOS/Android.
- Call startTXMic() from the click handler too — the call from applyState
runs in a WS-message context which mobile browsers reject.
- Re-resume txMicACtx on every MOX→TX (Android can suspend it between
cycles) and reuse the existing context instead of recreating per session.
- stopTXMic now a no-op: the previous flag reset created a race that spun
up duplicate pipelines on rapid RX→TX toggles.
WebServer.pas:
- Pre-roll cushion: hold first 40ms of decoded mic samples before flushing
to FTXMicRing, raising steady-state ring depth from ~9ms to ~50ms so
WiFi jitter no longer drains the buffer into silence ("robotic" voice).
- Wall-clock-based Opus PLC: synthesize up to 5 frames via
opus_decode_float(NULL) when packet gap exceeds expected 20ms cadence.
- A gap > 200ms is treated as a new TX session: pre-roll resets, PLC
is skipped (no useful decoder state).
- Local (non-web) mic paths are untouched — they bypass WebOnMic entirely.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
UserADC1 (bytes 55-56) carries current sensor data on the ANAN-8000DLE.
Formula from Thetis: amps = ((adc/4095 * 5000) - 360) / 120 (Voff=360mV,
Sens=120mV/A). Status bar now shows "Supply: 13.8V / 2.3A | PLL OK"
when current is available.
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>
Web UI: stepSel.onchange now sends fm_step command to server when in FM
mode; option values stay in Hz so tuneStep works unchanged.
WebServer: add fm_step command handler and OnFMStep callback.
MainForm: wire WebOnFMStep -> SyncWebFMStep -> SetFMStep + SaveCurrentBand.
Web spectrum/ruler: vertical FM-step grid lines in drawSpec; FM-aligned
ticks and labels in drawRuler with auto label-density (short ticks between
labeled lines, measureText-based labelMult to prevent overlap).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When FM mode is active, vertical grid lines and frequency labels snap to
FM channel boundaries (FM_STEP_HZ) instead of the fixed 8-division layout.
Grid and label density auto-adapt: lines thinner than 4px are thinned,
labels are spaced so they never overlap (short tick shown between labeled
lines). Grid cache invalidates on center-freq, step, or span changes.
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>
- LSB/USB: 5.0k–1.0k (unchanged)
- DSB: split into own group 16k–2.4k (was sharing AM filters)
- CWL/CWU: 1.0k 800 600 500 400 250 150 100 50 25 (removed 750, added 150)
- AM/SAM: 20k–5.0k (new wider range)
- Web FNP FM: now shows NFM/FM only (11k/16k), matching desktop
- Fix SyncWebFilter: FM array out-of-bounds; FFMDeviation now set on web filter select
- VfoOverlay: DSB separated from SSB with own quick-select values
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>
CalcAlex0: Alex.RxOnly[band]=3 (XVTR checkbox in Antenna tab) now sets
XVTR DDC In only when XvtrActive=True (FXvtrEnable). Matches Thetis
Alex.cs behaviour: "do not use XVTR ant port if not using transverter".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Separate FPTTActive from FIsTransmitting so PTT bit in HP packet
can be sent as a second packet after Alex bit27 (T/R relay) is set,
matching Thetis behaviour: relay switches first, then PTT=1
- On TX stop: PTT=0 first, then Alex switches back to RX
- Add SendPTT(Active) method to THPSDRNetwork
- Reset FSeqHP to 0 in SetRunAndFreq(Run=True) to eliminate HP SEQ
ERROR caused by intermediate SendFullHP calls before Run=1 packet
- RebuildXvtrButtons: use dynamic BtnH/RowH/MaxBottom instead of
hardcoded px values so XVTR buttons scale correctly on HiDPI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>