mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
Phase 3 (batch 15): move band save/restore into the controller
Per the architecture decision, band state lifecycle belongs on the controller (it operates entirely on controller state + engines; persistence is already delegated to Settings.pas). A separate unit would need friend access to ~20 fields + engines — worse coupling. - MakeBandSettings, SaveCurrentBand: moved verbatim (pure over controller state; SaveCurrentBand handles the XVTR-slot vs HF band-cache split and delegates the write to FSettings). MainForm.SaveCurrentBand is now a thin delegate (22 call sites unchanged); MakeBandSettings removed from MainForm. - RestoreBand: state + DSP application moved to the controller; rendering is driven by per-field Changed events (rfBand/rfMode/rfFilter/rfAGC*/rfFM*/ rfCTun/rfVfoB). The FM filter-index sanitize (which UpdateFilterButtons did inline as a render side-effect) is extracted into a pure SanitizeFilterForMode so the DSP ApplyModeFilter still runs on the sanitized BW (preserves original ordering). Fixes the same AGC index-cast bug as earlier batches (now ApplyAGCToEngine). - MainForm.RestoreBand is a thin wrapper: ResetWfAvgBuf (band-switch-only, not per-tune) + controller.RestoreBand + ApplyVfoA(FVfoA) for the final tune with channel orchestration (auto-CTCSS / pre-channel drive). - Added rfFMStep render case (BtnFMStep on/off was styled ad-hoc). Builds clean, links. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a8a192a3a3
commit
24102343ea
+15
-135
@@ -412,7 +412,6 @@ type
|
||||
procedure RestoreWindowBounds;
|
||||
procedure SaveWindowBounds;
|
||||
function MakeGlobalSettings: TGlobalSettings;
|
||||
function MakeBandSettings: TBandSettings;
|
||||
procedure FreqDispBChanged(Sender: TObject; NewFreq: Int64);
|
||||
procedure BtnBandClick(Sender: TObject);
|
||||
procedure BtnModeClick(Sender: TObject);
|
||||
@@ -787,28 +786,6 @@ begin
|
||||
FController.FSettings.SaveWindowBounds(L, T, Wd, Ht, DPI, Maximized);
|
||||
end;
|
||||
|
||||
function TMainForm.MakeBandSettings: TBandSettings;
|
||||
begin
|
||||
Result.VfoA := FController.FVfoA;
|
||||
Result.VfoB := FController.FVfoB;
|
||||
Result.Mode := FController.FMode;
|
||||
Result.FilterIdx := FController.FFilter;
|
||||
Result.FilterBW := FController.FFilterBW;
|
||||
Result.AGCMode := FController.FAGCMode;
|
||||
Result.AGCTop := FController.FAGCTop;
|
||||
Result.CTun := FController.FCTun;
|
||||
Result.SpanHz := FController.FSpanHz;
|
||||
Result.FMSQOn := FController.FFMSQOn;
|
||||
Result.FMSQLevel := FController.FFMSQLevel;
|
||||
Result.CTCSSOn := FController.FFMCTCSSOn;
|
||||
Result.CTCSSToneIdx := FController.FFMCTCSSToneIdx;
|
||||
Result.FMStepOn := FController.FFMStepOn;
|
||||
Result.FMStepIdx := FController.FFMStepIdx;
|
||||
Result.FMRptDir := FController.FFMRptDir;
|
||||
Result.FMRptOffsetHz := FController.FFMRptOffsetHz;
|
||||
// Waterfall AGC/NF — глобальные (не диапазонные)
|
||||
end;
|
||||
|
||||
function TMainForm.MakeGlobalSettings: TGlobalSettings;
|
||||
var i: Integer;
|
||||
begin
|
||||
@@ -889,124 +866,21 @@ end;
|
||||
|
||||
procedure TMainForm.SaveCurrentBand;
|
||||
begin
|
||||
if not FController.FDevConnected then Exit;
|
||||
// В XVTR-режиме НЕ трогаем HF band-cache: FController.FBandCache[FController.FCurrentBand]
|
||||
// относится к HF-диапазону, на котором пользователь был ДО активации
|
||||
// трансвертера, и FController.FVfoA сейчас на XVTR-частоте (144.x, 432.x и т.д.).
|
||||
// Запись в FController.FBandCache[FController.FCurrentBand] стерла бы корректное HF-состояние.
|
||||
// Вместо этого сохраняем LastFreq в XVTR-настройках.
|
||||
if FController.FCurrentXvtr >= 0 then
|
||||
begin
|
||||
if (FController.FCurrentXvtr < CFG_XVTR_COUNT) then
|
||||
begin
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFreq := FController.FVfoA;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastMode := FController.FMode;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFilterIdx := FController.FFilter;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMSQOn := FController.FFMSQOn;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMSQLevel := FController.FFMSQLevel;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastCTCSSOn := FController.FFMCTCSSOn;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastCTCSSToneIdx := FController.FFMCTCSSToneIdx;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMStepOn := FController.FFMStepOn;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMStepIdx := FController.FFMStepIdx;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMRptDir := FController.FFMRptDir;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMRptOffsetHz := FController.FFMRptOffsetHz;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastCTun := FController.FCTun;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastAGCMode := FController.FAGCMode;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastAGCTop := FController.FAGCTop;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastCTCSSAutoActive := FController.FFMCTCSSAutoActive;
|
||||
FController.FXvtrSettings.Entries[FController.FCurrentXvtr].LastFMRptAutoActive := FController.FFMRptAutoActive;
|
||||
end;
|
||||
FController.FSettings.SaveXvtr(FController.FDevMAC, FController.FXvtrSettings);
|
||||
Exit;
|
||||
end;
|
||||
// Не сохраняем, если VFO ушёл в VHF/UHF область (например, после применения
|
||||
// канала на 2м без XVTR) — иначе HF-кэш диапазона будет перезаписан VHF-частотой.
|
||||
if FController.FVfoA > 61000000 then Exit;
|
||||
FController.FBandCache[FController.FCurrentBand] := MakeBandSettings;
|
||||
FController.FSettings.SaveBand(FController.FDevMAC, FController.FCurrentBand, FController.FBandCache[FController.FCurrentBand]);
|
||||
// Логика сохранения (band-cache/XVTR-слот + конфиг) — в контроллере.
|
||||
FController.SaveCurrentBand;
|
||||
end;
|
||||
|
||||
procedure TMainForm.RestoreBand(BandIdx: Integer);
|
||||
var
|
||||
B: TBandSettings;
|
||||
i: Integer;
|
||||
begin
|
||||
if (BandIdx < 0) or (BandIdx >= CFG_BAND_COUNT) then Exit;
|
||||
B := FController.FBandCache[BandIdx];
|
||||
|
||||
// --- Кнопки диапазонов ---
|
||||
for i := 0 to BAND_COUNT - 1 do
|
||||
StyleButton(BtnBand[i], i = BandIdx);
|
||||
// Сбрасываем waterfall thresholds при смене диапазона
|
||||
// Сброс порогов водопада — только при явной смене диапазона (не на каждом
|
||||
// пересечении границы при тюнинге, поэтому здесь, а не в rfBand-рендере).
|
||||
FSpecView.ResetWfAvgBuf;
|
||||
|
||||
// --- Режим: сначала устанавливаем FController.FMode, потом кнопки и WDSP ---
|
||||
FController.FMode := B.Mode;
|
||||
for i := 0 to MODE_COUNT - 1 do
|
||||
StyleButton(BtnMode[i], i = FController.FMode);
|
||||
if FController.FWDSPReady then
|
||||
FController.FDSPEngine.SetMode(FController.FMode);
|
||||
|
||||
// --- Фильтр ---
|
||||
FController.FFilter := B.FilterIdx;
|
||||
FController.FFilterBW := B.FilterBW;
|
||||
UpdateFilterButtons; // обновляет кнопки фильтра
|
||||
FController.ApplyModeFilter; // применяет Lo/Hi в WDSP с учётом нового FController.FMode
|
||||
|
||||
// --- AGC ---
|
||||
FController.FAGCMode := B.AGCMode;
|
||||
FController.FAGCTop := B.AGCTop;
|
||||
for i := 0 to 4 do
|
||||
StyleButton(BtnAGCMode[i], i = FController.FAGCMode);
|
||||
TrkAGC.Position := FController.FAGCTop;
|
||||
LblAGCTop.Caption := Format('%ddB', [FController.FAGCTop]);
|
||||
if FController.FWDSPReady then
|
||||
begin
|
||||
FController.FDSPEngine.SetAGCTop(FController.FAGCTop);
|
||||
FController.FDSPEngine.SetAGC(TWDSPAGCMode(FController.FAGCMode), 50.0);
|
||||
end;
|
||||
FSpecView.AGCTop := FController.FAGCTop;
|
||||
|
||||
// --- FM Squelch + CTCSS ---
|
||||
FController.FFMSQOn := B.FMSQOn;
|
||||
FController.FFMSQLevel := B.FMSQLevel;
|
||||
// UI update happens inside UpdateFilterButtons (called above) when mode=FM
|
||||
FController.FFMCTCSSOn := B.CTCSSOn;
|
||||
if BtnFMCTCSS <> nil then StyleButton(BtnFMCTCSS, FController.FFMCTCSSOn);
|
||||
SetFMCTCSSTone(B.CTCSSToneIdx);
|
||||
ApplyFMSquelch;
|
||||
FController.FFMStepOn := B.FMStepOn;
|
||||
SetFMStep(B.FMStepIdx);
|
||||
if BtnFMStep <> nil then StyleButton(BtnFMStep, FController.FFMStepOn);
|
||||
FController.FFMRptDir := B.FMRptDir;
|
||||
FController.FFMRptOffsetHz := B.FMRptOffsetHz;
|
||||
if FController.FFMRptOffsetHz <= 0 then
|
||||
FController.FFMRptOffsetHz := RptDefaultOffsetHz(B.VfoA);
|
||||
if BtnFMRptMinus <> nil then StyleButton(BtnFMRptMinus, FController.FFMRptDir = RPT_MINUS);
|
||||
if BtnFMRptPlus <> nil then StyleButton(BtnFMRptPlus, FController.FFMRptDir = RPT_PLUS);
|
||||
if EdFMRptOffset <> nil then EdFMRptOffset.Text := RptFormatOffset(FController.FFMRptOffsetHz);
|
||||
|
||||
// --- CTUN ---
|
||||
FController.FCTun := B.CTun;
|
||||
StyleButton(BtnCTun, FController.FCTun);
|
||||
if FController.FWDSPReady and not FController.FCTun then
|
||||
FController.FDSPEngine.SetShift(0.0);
|
||||
|
||||
// --- Sample Rate / Span ---
|
||||
// SampleRate — глобальный, не per-band. Не восстанавливаем из диапазона.
|
||||
FController.FSpanHz := FController.FSampleRate;
|
||||
if Assigned(FSampleRateOverlay) then
|
||||
FSampleRateOverlay.SetCurrentRate(FController.FSampleRate);
|
||||
|
||||
// --- VFO A: центрируем на новой частоте, сбрасываем shift ---
|
||||
FController.FCenterFreq := B.VfoA; // DDC = центр диапазона
|
||||
FController.FVfoB := B.VfoB;
|
||||
FreqDispB.Frequency := Round(FController.FVfoB);
|
||||
|
||||
// Пересчитываем drive byte для нового диапазона перед отправкой в сеть
|
||||
FController.FDriveLevel := CalcDriveByte;
|
||||
// ApplyVfoA обновит сеть, FreqDisp, shift, Ruler — перерисует.
|
||||
ApplyVfoA(Round(B.VfoA));
|
||||
// Состояние + DSP + события рендера — в контроллере.
|
||||
FController.RestoreBand(BandIdx);
|
||||
// Финальная перестройка VFO A с канальной оркестрацией (auto-CTCSS,
|
||||
// pre-channel drive) — через UI-путь ApplyVfoA.
|
||||
ApplyVfoA(Round(FController.FVfoA));
|
||||
end;
|
||||
|
||||
procedure TMainForm.SaveAllAndExit;
|
||||
@@ -1492,6 +1366,12 @@ begin
|
||||
if (FController.FMode = MODE_FM) and (FSpecView <> nil) then
|
||||
FSpecView.FMGridStepHz := FM_STEP_HZ[FController.FFMStepIdx];
|
||||
end;
|
||||
rfFMStep:
|
||||
if BtnFMStep <> nil then
|
||||
begin
|
||||
StyleButton(BtnFMStep, FController.FFMStepOn);
|
||||
BtnFMStep.Repaint;
|
||||
end;
|
||||
rfFMSQ:
|
||||
begin
|
||||
StyleButton(BtnFMSQ, FController.FFMSQOn);
|
||||
|
||||
@@ -110,6 +110,7 @@ type
|
||||
procedure Changed(Field: TRadioField);
|
||||
procedure ApplyAGCToEngine; // применяет FAGCMode к WDSP (единый маппинг)
|
||||
procedure ApplyModeDefaults; // дефолтный фильтр/девиация под текущий FMode
|
||||
procedure SanitizeFilterForMode; // FM: индекс фильтра 0/1 + BW/девиация (state)
|
||||
function ActiveVfoHz: Double; // частота активного VFO (A или B)
|
||||
procedure ApplyTuneCore(VfoHz: Double); // CTUN shift/center/scroll для актив. VFO
|
||||
public
|
||||
@@ -222,6 +223,15 @@ type
|
||||
// (band/xvtr cal, TUN/drive%, PA max). Чистая функция над состоянием.
|
||||
function CalcDriveByte: Byte;
|
||||
|
||||
// ---- Управление диапазонами (состояние + DSP; рендер через события) ----
|
||||
// MakeBandSettings — снимок текущего состояния в запись диапазона.
|
||||
// SaveCurrentBand — сохраняет текущее состояние в кэш+конфиг (или в XVTR-слот).
|
||||
// RestoreBand — загружает состояние диапазона, применяет к DSP, шлёт события
|
||||
// рендера. Финальную перестройку VFO A делает вызывающий (UI: ApplyVfoA).
|
||||
function MakeBandSettings: TBandSettings;
|
||||
procedure SaveCurrentBand;
|
||||
procedure RestoreBand(BandIdx: Integer);
|
||||
|
||||
// Полный HP-кадр в радио (RX/TX freq через трансвертор + drive + MOX).
|
||||
// Вызывается командами смены частоты/центра и UI после правки drive.
|
||||
procedure PushNetworkState;
|
||||
@@ -500,6 +510,151 @@ begin
|
||||
Result := Min(Round(audio_vol * 1.02 * 255.0), 255);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SanitizeFilterForMode;
|
||||
// Для FM индексы фильтров только 0/1 (NFM/WFM); приводим FFilter и
|
||||
// синхронизируем FFilterBW/FFMDeviation. Чистая (state) часть UpdateFilterButtons.
|
||||
begin
|
||||
if FMode = MODE_FM then
|
||||
begin
|
||||
if FFilter > 1 then FFilter := FILT_FM_DEF;
|
||||
FFilterBW := FILT_FM_BW[FFilter];
|
||||
FFMDeviation := FILT_FM_DEV[FFilter];
|
||||
end;
|
||||
end;
|
||||
|
||||
function TRadioController.MakeBandSettings: TBandSettings;
|
||||
begin
|
||||
Result.VfoA := FVfoA;
|
||||
Result.VfoB := FVfoB;
|
||||
Result.Mode := FMode;
|
||||
Result.FilterIdx := FFilter;
|
||||
Result.FilterBW := FFilterBW;
|
||||
Result.AGCMode := FAGCMode;
|
||||
Result.AGCTop := FAGCTop;
|
||||
Result.CTun := FCTun;
|
||||
Result.SpanHz := FSpanHz;
|
||||
Result.FMSQOn := FFMSQOn;
|
||||
Result.FMSQLevel := FFMSQLevel;
|
||||
Result.CTCSSOn := FFMCTCSSOn;
|
||||
Result.CTCSSToneIdx := FFMCTCSSToneIdx;
|
||||
Result.FMStepOn := FFMStepOn;
|
||||
Result.FMStepIdx := FFMStepIdx;
|
||||
Result.FMRptDir := FFMRptDir;
|
||||
Result.FMRptOffsetHz := FFMRptOffsetHz;
|
||||
// Waterfall AGC/NF — глобальные (не диапазонные)
|
||||
end;
|
||||
|
||||
procedure TRadioController.SaveCurrentBand;
|
||||
// Сохраняет текущее состояние диапазона. В XVTR-режиме пишем в XVTR-слот
|
||||
// (HF band-cache не трогаем — там сохранён HF-диапазон до активации XVTR).
|
||||
var i: Integer;
|
||||
begin
|
||||
if not FDevConnected then Exit;
|
||||
if FCurrentXvtr >= 0 then
|
||||
begin
|
||||
if (FCurrentXvtr < CFG_XVTR_COUNT) then
|
||||
begin
|
||||
i := FCurrentXvtr;
|
||||
FXvtrSettings.Entries[i].LastFreq := FVfoA;
|
||||
FXvtrSettings.Entries[i].LastMode := FMode;
|
||||
FXvtrSettings.Entries[i].LastFilterIdx := FFilter;
|
||||
FXvtrSettings.Entries[i].LastFMSQOn := FFMSQOn;
|
||||
FXvtrSettings.Entries[i].LastFMSQLevel := FFMSQLevel;
|
||||
FXvtrSettings.Entries[i].LastCTCSSOn := FFMCTCSSOn;
|
||||
FXvtrSettings.Entries[i].LastCTCSSToneIdx := FFMCTCSSToneIdx;
|
||||
FXvtrSettings.Entries[i].LastFMStepOn := FFMStepOn;
|
||||
FXvtrSettings.Entries[i].LastFMStepIdx := FFMStepIdx;
|
||||
FXvtrSettings.Entries[i].LastFMRptDir := FFMRptDir;
|
||||
FXvtrSettings.Entries[i].LastFMRptOffsetHz := FFMRptOffsetHz;
|
||||
FXvtrSettings.Entries[i].LastCTun := FCTun;
|
||||
FXvtrSettings.Entries[i].LastAGCMode := FAGCMode;
|
||||
FXvtrSettings.Entries[i].LastAGCTop := FAGCTop;
|
||||
FXvtrSettings.Entries[i].LastCTCSSAutoActive := FFMCTCSSAutoActive;
|
||||
FXvtrSettings.Entries[i].LastFMRptAutoActive := FFMRptAutoActive;
|
||||
end;
|
||||
FSettings.SaveXvtr(FDevMAC, FXvtrSettings);
|
||||
Exit;
|
||||
end;
|
||||
// Не сохраняем, если VFO ушёл в VHF/UHF (например, после канала на 2м без
|
||||
// XVTR) — иначе HF-кэш диапазона перезапишется VHF-частотой.
|
||||
if FVfoA > 61000000 then Exit;
|
||||
FBandCache[FCurrentBand] := MakeBandSettings;
|
||||
FSettings.SaveBand(FDevMAC, FCurrentBand, FBandCache[FCurrentBand]);
|
||||
end;
|
||||
|
||||
procedure TRadioController.RestoreBand(BandIdx: Integer);
|
||||
// Загружает состояние диапазона из кэша, применяет к DSP и шлёт события рендера.
|
||||
// Финальную перестройку VFO A (с канальной оркестрацией) делает вызывающий
|
||||
// UI-путь через ApplyVfoA(FVfoA); здесь FVfoA лишь выставляется.
|
||||
var B: TBandSettings;
|
||||
begin
|
||||
if (BandIdx < 0) or (BandIdx >= CFG_BAND_COUNT) then Exit;
|
||||
B := FBandCache[BandIdx];
|
||||
FCurrentBand := BandIdx;
|
||||
|
||||
// --- Режим ---
|
||||
FMode := B.Mode;
|
||||
if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetMode(FMode);
|
||||
|
||||
// --- Фильтр (санитизация FM до применения к DSP — как в оригинале) ---
|
||||
FFilter := B.FilterIdx;
|
||||
FFilterBW := B.FilterBW;
|
||||
SanitizeFilterForMode;
|
||||
ApplyModeFilter;
|
||||
|
||||
// --- AGC (единый маппинг — исправляет каст-баг RestoreBand) ---
|
||||
FAGCMode := B.AGCMode;
|
||||
FAGCTop := B.AGCTop;
|
||||
if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetAGCTop(FAGCTop);
|
||||
ApplyAGCToEngine;
|
||||
|
||||
// --- FM squelch / CTCSS / step / repeater ---
|
||||
FFMSQOn := B.FMSQOn;
|
||||
FFMSQLevel := B.FMSQLevel;
|
||||
FFMCTCSSOn := B.CTCSSOn;
|
||||
FFMCTCSSToneIdx := B.CTCSSToneIdx;
|
||||
if FWDSPReady and Assigned(FDSPEngine) then
|
||||
begin
|
||||
FDSPEngine.SetTXCTCSS(FFMCTCSSOn, CTCSS_TONES[FFMCTCSSToneIdx]);
|
||||
FDSPEngine.SetFMSquelch(FFMSQOn and (FMode = MODE_FM), FFMSQLevel);
|
||||
end;
|
||||
FFMStepOn := B.FMStepOn;
|
||||
FFMStepIdx := EnsureRange(B.FMStepIdx, 0, 3);
|
||||
FFMRptDir := B.FMRptDir;
|
||||
FFMRptOffsetHz := B.FMRptOffsetHz;
|
||||
if FFMRptOffsetHz <= 0 then FFMRptOffsetHz := RptDefaultOffsetHz(B.VfoA);
|
||||
|
||||
// --- CTUN ---
|
||||
FCTun := B.CTun;
|
||||
if FWDSPReady and Assigned(FDSPEngine) and not FCTun then FDSPEngine.SetShift(0.0);
|
||||
|
||||
// --- Span (sample rate глобальный, не per-band — лишь ресинк) ---
|
||||
FSpanHz := FSampleRate;
|
||||
|
||||
// --- VFO / drive ---
|
||||
FCenterFreq := B.VfoA;
|
||||
FVfoB := B.VfoB;
|
||||
FVfoA := B.VfoA;
|
||||
FDriveLevel := CalcDriveByte;
|
||||
|
||||
// --- Рендер: единичные события (UI рисует через OnControllerState) ---
|
||||
Changed(rfBand);
|
||||
Changed(rfMode); // rfMode-рендер зовёт UpdateFilterButtons (FM-панели)
|
||||
Changed(rfFilter);
|
||||
Changed(rfAGCMode);
|
||||
Changed(rfAGCTop);
|
||||
Changed(rfFMSQ);
|
||||
Changed(rfFMSQLevel);
|
||||
Changed(rfFMCTCSS);
|
||||
Changed(rfFMCTCSSTone);
|
||||
Changed(rfFMStep); // кнопка вкл/выкл STEP
|
||||
Changed(rfFMStepIdx); // дропдаун шага + web + grid
|
||||
Changed(rfFMRpt);
|
||||
Changed(rfCTun);
|
||||
Changed(rfVfoB);
|
||||
// rfVfoA НЕ шлём: финальную перестройку делает ApplyVfoA в UI-обёртке.
|
||||
end;
|
||||
|
||||
function TRadioController.ActiveVfoHz: Double;
|
||||
begin
|
||||
if FActiveVfo = 0 then Result := FVfoA else Result := FVfoB;
|
||||
|
||||
Reference in New Issue
Block a user