mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
Phase 5: extract channel memory logic into ChannelController.pas
Channels are recall-of-saved-config — a client of the radio core, not part of DSP/network. Move ApplyChannel/CheckChannelActive + active-channel state (FActiveChannelIdx, FPreChannelPower) out of MainForm into TChannelController, which orchestrates controller commands and emits OnActiveChanged for the Channel-button render. Removes the last UI-held state blocking headless mode. Also completes TRadioController.SetDrive (was a skeleton): clamp + CalcDriveByte + SetDriveLevel + PushNetworkState + Changed(rfDrive), with an rfDrive render case; routes slider/web/channel drive through it. Adds SetCurrentBandIdx for the lightweight HF band switch. Behavior note: channel recall now applies the mode-default filter via SetMode (was: kept current filter — a latent quirk that left FM on an SSB bandwidth). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
06fd730ff4
commit
03f7359184
+33
-203
@@ -42,7 +42,7 @@ uses
|
||||
ChannelStore, ChannelsForm,
|
||||
PowerInhibit,
|
||||
DeviceStore,
|
||||
RadioController;
|
||||
RadioController, ChannelController;
|
||||
|
||||
const
|
||||
CLR_BG = TColor($00101010);
|
||||
@@ -151,7 +151,6 @@ type
|
||||
FDisplayFPS: Integer;
|
||||
FWaterfallDirty: Boolean;
|
||||
FWidebandDirty: Boolean;
|
||||
FPreChannelPower: Integer; // мощность до применения канала (-1 = не сохранена)
|
||||
// Display state (FShow*/FSpec*/FWf*/FTXSpec*/FController.FWidebandFill/FController.FSpectrumFill)
|
||||
// переехало в TRadioController (нужно headless-load в DoConnectDevice).
|
||||
// DSP data buffers (copy of last DSP output, for WebServer.PushSpectrum)
|
||||
@@ -171,6 +170,7 @@ type
|
||||
// --- Settings ---
|
||||
FWebServer: TWebServer;
|
||||
FWebAdapter: TWebAdapter; // мост web-сервер ↔ контроллер (вынесенные WebOnXxx)
|
||||
FChannelController: TChannelController; // адаптер каналов памяти поверх контроллера
|
||||
FWebEnabled: Boolean;
|
||||
FWebPort: Integer;
|
||||
FWebBindAddr: string;
|
||||
@@ -284,7 +284,6 @@ type
|
||||
BtnChannels: TFlatButton;
|
||||
FChannelsDropDown: TFlatDropDown;
|
||||
FChannelsForm: TObject; // TChannelsForm (cast при использовании)
|
||||
FActiveChannelIdx: Integer; // -1 = нет активного канала
|
||||
|
||||
// ---- Right panel ----
|
||||
PanelRight: TPanel;
|
||||
@@ -402,8 +401,7 @@ type
|
||||
procedure OnChannelsDropDownSelect(Sender: TObject; Idx: Integer);
|
||||
procedure RefreshChannelsDropDown;
|
||||
procedure OnChannelStoreChanged(Sender: TObject);
|
||||
procedure ApplyChannel(const Ch: TChannel);
|
||||
procedure CheckChannelActive;
|
||||
procedure OnChannelActiveChanged(Idx: Integer; const Name: string; Active: Boolean);
|
||||
procedure PbSpectrumMouseDown(Sender: TObject; Button: TMouseButton;
|
||||
Shift: TShiftState; X, Y: Integer);
|
||||
procedure PbSpectrumMouseMove(Sender: TObject; Shift: TShiftState;
|
||||
@@ -847,6 +845,13 @@ begin
|
||||
FController := TRadioController.Create;
|
||||
FController.OnInvoke := DoInvoke;
|
||||
FController.OnStateChanged := OnControllerState;
|
||||
// Адаптер каналов поверх контроллера: оркеструет команды, держит активный
|
||||
// канал/pre-channel-drive. Рендер кнопки Channel — через OnActiveChanged;
|
||||
// XVTR-вход/выход — через UI-обёртки (wideband/web живут в них).
|
||||
FChannelController := TChannelController.Create(FController);
|
||||
FChannelController.OnActiveChanged := OnChannelActiveChanged;
|
||||
FChannelController.OnActivateXvtr := ActivateXvtrBand;
|
||||
FChannelController.OnDeactivateXvtr := DeactivateXvtr;
|
||||
FController.FVfoA := 14200000;
|
||||
FController.FVfoB := 7100000;
|
||||
FController.FActiveVfo := 0;
|
||||
@@ -892,7 +897,6 @@ begin
|
||||
FController.FFMRptOffsetHz := RPT_DEFAULT_2M;
|
||||
FController.FFMRptAutoActive := False;
|
||||
FController.FFMCTCSSAutoActive := False;
|
||||
FPreChannelPower := -1;
|
||||
FSpectrumBufCount := 1024;
|
||||
FWaterfallBufCount := 1024;
|
||||
FWaterfallFrameInterval := 2;
|
||||
@@ -908,7 +912,6 @@ begin
|
||||
FController.FTXSpecGridStep := 10.0;
|
||||
FSettingsForm := nil;
|
||||
FChannelsForm := nil;
|
||||
FActiveChannelIdx := -1;
|
||||
FillChar(FController.FDevMAC, SizeOf(FController.FDevMAC), 0);
|
||||
|
||||
// Инициализируем кэш диапазонов умолчаниями
|
||||
@@ -1173,6 +1176,7 @@ begin
|
||||
// Размер окна сохраняем всегда (не зависит от подключения)
|
||||
FController.FSettings.SaveStartupPreview(FController.FVfoA, FController.FVfoB, FController.FSampleRate);
|
||||
FController.FSettings.Save;
|
||||
FreeAndNil(FChannelController); // адаптер каналов (не владеет store/контроллером)
|
||||
FreeAndNil(FController.FChannelStore);
|
||||
FWebServer.Stop;
|
||||
FreeAndNil(FWebAdapter); // адаптер не владеет сервером — освобождаем после Stop
|
||||
@@ -1346,6 +1350,8 @@ begin
|
||||
for i := 0 to 4 do StyleButton(BtnAGCMode[i], i = FController.FAGCMode);
|
||||
rfVolume:
|
||||
TrkVolume.Position := FController.FVolume;
|
||||
rfDrive:
|
||||
TrkDrive.Position := FController.FDrivePercent; // слайдер ← drive%
|
||||
rfAGCTop:
|
||||
begin
|
||||
TrkAGC.Position := FController.FAGCTop;
|
||||
@@ -3915,30 +3921,9 @@ begin
|
||||
// OnControllerState(rfVfoA/rfBand).
|
||||
FController.SetVfoA(NewFreq);
|
||||
|
||||
// Канальная оркестрация (UI). Только при отсутствии активного канала.
|
||||
// Авто-RPT сбрасывает сам SetVfoA через UpdateRptAutoState; здесь — авто-CTCSS
|
||||
// и восстановление pre-channel drive (виджет TrkDrive).
|
||||
if FActiveChannelIdx < 0 then
|
||||
begin
|
||||
if FController.FFMCTCSSAutoActive then
|
||||
begin
|
||||
FController.FFMCTCSSAutoActive := False;
|
||||
FController.FFMCTCSSOn := False;
|
||||
if BtnFMCTCSS <> nil then StyleButton(BtnFMCTCSS, False);
|
||||
if FController.FWDSPReady then FController.FDSPEngine.SetTXCTCSS(False, CTCSS_TONES[FController.FFMCTCSSToneIdx]);
|
||||
end;
|
||||
if FPreChannelPower >= 0 then
|
||||
begin
|
||||
TrkDrive.Position := FPreChannelPower;
|
||||
FPreChannelPower := -1;
|
||||
FController.FDriveLevel := CalcDriveByte;
|
||||
if FController.FWDSPReady then
|
||||
FController.FDSPEngine.SetDriveLevel(FController.FDrivePercent / 100.0);
|
||||
FController.PushNetworkState; // протолкнуть восстановленный drive
|
||||
end;
|
||||
end;
|
||||
|
||||
CheckChannelActive;
|
||||
// Канальная оркестрация (авто-CTCSS/pre-channel-drive + деактивация при уходе
|
||||
// с частоты канала) — в адаптере каналов.
|
||||
FChannelController.OnVfoTuned;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -4452,8 +4437,8 @@ begin
|
||||
for i := 0 to FController.FChannelStore.Count - 1 do
|
||||
names[i] := FController.FChannelStore.GetChannel(i).Name;
|
||||
FChannelsDropDown.SetItems(names);
|
||||
if (FActiveChannelIdx >= 0) and (FActiveChannelIdx < FController.FChannelStore.Count) then
|
||||
FChannelsDropDown.SetItemIndex(FActiveChannelIdx)
|
||||
if (FChannelController.ActiveIdx >= 0) and (FChannelController.ActiveIdx < FController.FChannelStore.Count) then
|
||||
FChannelsDropDown.SetItemIndex(FChannelController.ActiveIdx)
|
||||
else if BtnChannels <> nil then
|
||||
BtnChannels.Caption := 'Channel';
|
||||
end;
|
||||
@@ -4497,168 +4482,20 @@ begin
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnChannelsDropDownSelect(Sender: TObject; Idx: Integer);
|
||||
var Ch: TChannel;
|
||||
begin
|
||||
if (FController.FChannelStore = nil) or (Idx < 0) or (Idx >= FController.FChannelStore.Count) then Exit;
|
||||
Ch := FController.FChannelStore.GetChannel(Idx);
|
||||
FActiveChannelIdx := Idx;
|
||||
StyleButton(BtnChannels, True);
|
||||
BtnChannels.Caption := Ch.Name;
|
||||
// Логика применения канала (band/XVTR/mode/FM/drive/freq) — в адаптере каналов;
|
||||
// рендер кнопки Channel — в OnChannelActiveChanged.
|
||||
FChannelController.Select(Idx);
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnChannelActiveChanged(Idx: Integer; const Name: string; Active: Boolean);
|
||||
begin
|
||||
// Рендер кнопки Channel по событию адаптера каналов (Active=True → подсветка+имя).
|
||||
if BtnChannels = nil then Exit;
|
||||
StyleButton(BtnChannels, Active);
|
||||
if Active then BtnChannels.Caption := Name
|
||||
else BtnChannels.Caption := 'Channel';
|
||||
BtnChannels.Repaint;
|
||||
ApplyChannel(Ch);
|
||||
end;
|
||||
|
||||
procedure TMainForm.ApplyChannel(const Ch: TChannel);
|
||||
var
|
||||
i, NewBand, XvtrIdx: Integer;
|
||||
begin
|
||||
// Сохраняем текущее состояние до любых изменений
|
||||
SaveCurrentBand;
|
||||
|
||||
// Ищем подходящий XVTR-слот по частоте канала
|
||||
XvtrIdx := -1;
|
||||
for i := 0 to CFG_XVTR_COUNT - 1 do
|
||||
if FController.FXvtrSettings.Entries[i].Enabled and
|
||||
(Ch.RXFreq >= FController.FXvtrSettings.Entries[i].FreqBegin) and
|
||||
(Ch.RXFreq <= FController.FXvtrSettings.Entries[i].FreqEnd) then
|
||||
begin
|
||||
XvtrIdx := i;
|
||||
Break;
|
||||
end;
|
||||
|
||||
NewBand := -1;
|
||||
|
||||
if XvtrIdx >= 0 then
|
||||
begin
|
||||
// Канал на трансвертерном диапазоне — активируем XVTR если не активен
|
||||
if FController.FCurrentXvtr <> XvtrIdx then
|
||||
ActivateXvtrBand(XvtrIdx);
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Канал на HF (или неизвестная частота)
|
||||
// Если были на XVTR — выходим из него, иначе ApplyVfoA заклэмпит частоту
|
||||
if FController.FCurrentXvtr >= 0 then
|
||||
DeactivateXvtr;
|
||||
NewBand := FreqToBandIdx(Ch.RXFreq);
|
||||
if (NewBand >= 0) and (NewBand <> FController.FCurrentBand) then
|
||||
begin
|
||||
StyleButton(BtnBand[FController.FCurrentBand], False);
|
||||
FController.FCurrentBand := NewBand;
|
||||
StyleButton(BtnBand[FController.FCurrentBand], True);
|
||||
FController.FDriveLevel := CalcDriveByte;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Режим (после ActivateXvtrBand/DeactivateXvtr FController.FMode обновился — сравниваем снова)
|
||||
if FController.FMode <> Ch.Mode then
|
||||
begin
|
||||
FController.FMode := Ch.Mode;
|
||||
for i := 0 to MODE_COUNT - 1 do StyleButton(BtnMode[i], i = FController.FMode);
|
||||
if FController.FWDSPReady then
|
||||
begin
|
||||
FController.FDSPEngine.SetMode(FController.FMode);
|
||||
if FController.FTuning then FController.FDSPEngine.SetTXTone(True, FController.FTXSettings.TUNFreq, 1.0);
|
||||
end;
|
||||
UpdateFilterButtons;
|
||||
FController.ApplyModeFilter;
|
||||
SyncSpecViewFreq;
|
||||
end;
|
||||
|
||||
// FM-параметры
|
||||
if FController.FMode = MODE_FM then
|
||||
begin
|
||||
FController.FFMCTCSSOn := Ch.CTCSSOn;
|
||||
FController.FFMCTCSSAutoActive := Ch.CTCSSOn; // CTCSS из канала — авто, сбросится при уходе с частоты
|
||||
SetFMCTCSSTone(Ch.CTCSSToneIdx);
|
||||
if BtnFMCTCSS <> nil then
|
||||
begin
|
||||
StyleButton(BtnFMCTCSS, FController.FFMCTCSSOn);
|
||||
BtnFMCTCSS.Repaint;
|
||||
end;
|
||||
if FController.FWDSPReady then
|
||||
FController.FDSPEngine.SetTXCTCSS(FController.FFMCTCSSOn, CTCSS_TONES[FController.FFMCTCSSToneIdx]);
|
||||
FController.FFMRptDir := Ch.RptDir;
|
||||
FController.FFMRptOffsetHz := Ch.RptOffsetHz;
|
||||
FController.FFMRptAutoActive := Ch.RptDir <> RPT_NONE; // RPT из канала — авто, сбросится при уходе
|
||||
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);
|
||||
end;
|
||||
|
||||
// Мощность из канала.
|
||||
// TrkDrive.Position := ... синхронно вызывает TrkDriveChange → FPreChannelPower := -1,
|
||||
// поэтому сохраняем оригинал в локальную переменную и восстанавливаем после.
|
||||
if Ch.Power >= 0 then
|
||||
begin
|
||||
i := FController.FDrivePercent; // запомнить до срабатывания OnChange
|
||||
TrkDrive.Position := EnsureRange(Ch.Power, 0, 100); // TrkDriveChange обновит FController.FDriveLevel и WDSP
|
||||
if FPreChannelPower < 0 then
|
||||
FPreChannelPower := i; // установить после (TrkDriveChange уже сбросил в -1)
|
||||
end;
|
||||
|
||||
// Частота VFO A
|
||||
ApplyVfoA(Round(Ch.RXFreq));
|
||||
|
||||
// Сохраняем состояние
|
||||
if XvtrIdx >= 0 then
|
||||
SaveCurrentBand // сохраняем в XVTR-слот (CTCSS/RPT/Mode из канала)
|
||||
else if NewBand >= 0 then
|
||||
SaveCurrentBand; // сохраняем в HF band-кэш
|
||||
end;
|
||||
|
||||
procedure TMainForm.CheckChannelActive;
|
||||
var Ch: TChannel;
|
||||
begin
|
||||
if FActiveChannelIdx < 0 then Exit;
|
||||
if (FController.FChannelStore = nil) or (FActiveChannelIdx >= FController.FChannelStore.Count) then
|
||||
begin
|
||||
FActiveChannelIdx := -1;
|
||||
Exit;
|
||||
end;
|
||||
Ch := FController.FChannelStore.GetChannel(FActiveChannelIdx);
|
||||
if Round(FController.FVfoA) <> Round(Ch.RXFreq) then
|
||||
begin
|
||||
FActiveChannelIdx := -1;
|
||||
StyleButton(BtnChannels, False);
|
||||
BtnChannels.Caption := 'Channel';
|
||||
BtnChannels.Repaint;
|
||||
|
||||
// Сбрасываем CTCSS если он был включён каналом (не вручную)
|
||||
if FController.FFMCTCSSAutoActive then
|
||||
begin
|
||||
FController.FFMCTCSSAutoActive := False;
|
||||
FController.FFMCTCSSOn := False;
|
||||
if BtnFMCTCSS <> nil then
|
||||
begin
|
||||
StyleButton(BtnFMCTCSS, False);
|
||||
BtnFMCTCSS.Repaint;
|
||||
end;
|
||||
if FController.FWDSPReady then
|
||||
FController.FDSPEngine.SetTXCTCSS(False, CTCSS_TONES[FController.FFMCTCSSToneIdx]);
|
||||
end;
|
||||
|
||||
// Сбрасываем RPT если он был включён каналом (не вручную), затем
|
||||
// даём UpdateRptAutoState переоценить авто-MINUS для текущей частоты
|
||||
if FController.FFMRptAutoActive then
|
||||
begin
|
||||
FController.FFMRptDir := RPT_NONE;
|
||||
FController.FFMRptAutoActive := False;
|
||||
if BtnFMRptMinus <> nil then StyleButton(BtnFMRptMinus, False);
|
||||
if BtnFMRptPlus <> nil then StyleButton(BtnFMRptPlus, False);
|
||||
UpdateRptAutoState;
|
||||
end;
|
||||
|
||||
// Восстанавливаем мощность которая была до применения канала
|
||||
if FPreChannelPower >= 0 then
|
||||
begin
|
||||
TrkDrive.Position := FPreChannelPower;
|
||||
FPreChannelPower := -1;
|
||||
FController.FDriveLevel := CalcDriveByte;
|
||||
if FController.FWDSPReady then
|
||||
FController.FDSPEngine.SetDriveLevel(FController.FDrivePercent / 100.0);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.SetFMCTCSSTone(Idx: Integer);
|
||||
@@ -5253,16 +5090,9 @@ end;
|
||||
|
||||
procedure TMainForm.TrkDriveChange(Sender: TObject);
|
||||
begin
|
||||
FController.FDrivePercent := TrkDrive.Position; // слайдер — ввод; контроллер — истина
|
||||
FPreChannelPower := -1; // ручное изменение снимает сохранённую мощность канала
|
||||
FController.FDriveLevel := CalcDriveByte;
|
||||
if FController.FWDSPReady then
|
||||
FController.FDSPEngine.SetDriveLevel(FController.FDrivePercent / 100.0);
|
||||
if FController.FNetwork.Connected and FController.FNetwork.Running then
|
||||
begin
|
||||
FController.FNetwork.UpdateState(XvtrTranslate(FController.FCenterFreq), XvtrTranslate(ActiveTXFreqHz), FController.FDriveLevel, FController.FTransmitting, True, True);
|
||||
FController.FNetwork.SendFullHP;
|
||||
end;
|
||||
if FSyncingFromController then Exit; // программная установка из OnControllerState — не команда
|
||||
FChannelController.ClearPreChannelPower; // ручное изменение снимает сохранённую мощность канала
|
||||
FController.SetDrive(TrkDrive.Position);
|
||||
end;
|
||||
|
||||
function TMainForm.CalcDriveByte: Byte;
|
||||
|
||||
Reference in New Issue
Block a user