vix vfo-a/vfo-b

This commit is contained in:
2026-03-11 21:43:37 +03:00
parent 99463fa22f
commit 443da44e40
+310 -34
View File
@@ -410,6 +410,10 @@ type
procedure BtnStartStopClick(Sender: TObject); procedure BtnStartStopClick(Sender: TObject);
procedure FreqDispAChanged(Sender: TObject; NewFreq: Int64); procedure FreqDispAChanged(Sender: TObject; NewFreq: Int64);
procedure ApplyVfoA(NewFreq: Int64); procedure ApplyVfoA(NewFreq: Int64);
procedure FreqDispAClick(Sender: TObject);
procedure FreqDispBClick(Sender: TObject);
procedure ActivateVfo(Idx: Integer);
function ActiveVfoFreq: Int64;
// Настройки // Настройки
procedure SaveCurrentBand; procedure SaveCurrentBand;
procedure RestoreBand(BandIdx: Integer); procedure RestoreBand(BandIdx: Integer);
@@ -1068,6 +1072,7 @@ begin
FreqDispA.ColorHover := TColor($0040DDFF); FreqDispA.ColorHover := TColor($0040DDFF);
FreqDispA.ColorDim := CLR_TEXTDIM; FreqDispA.ColorDim := CLR_TEXTDIM;
FreqDispA.OnChange := FreqDispAChanged; FreqDispA.OnChange := FreqDispAChanged;
FreqDispA.OnClick := FreqDispAClick;
Inc(Y, 58); Inc(Y, 58);
@@ -1094,6 +1099,7 @@ begin
FreqDispB.ColorHover := TColor($0040DDFF); FreqDispB.ColorHover := TColor($0040DDFF);
FreqDispB.ColorDim := CLR_TEXTDIM; FreqDispB.ColorDim := CLR_TEXTDIM;
FreqDispB.OnChange := FreqDispBChanged; FreqDispB.OnChange := FreqDispBChanged;
FreqDispB.OnClick := FreqDispBClick;
Inc(Y, 50); Inc(Y, 50);
@@ -1727,27 +1733,26 @@ begin
FreqDispB.Frequency := Round(FVfoB); FreqDispB.Frequency := Round(FVfoB);
if FActiveVfo = 0 then if FActiveVfo = 0 then
begin begin
// VFO-A активен: яркий зелёный, крупный
FreqDispA.ColorNormal := CLR_FREQ; FreqDispA.FontSize := 20; FreqDispA.ColorNormal := CLR_FREQ; FreqDispA.FontSize := 20;
FreqDispB.ColorNormal := CLR_FREQ_DIM; FreqDispB.FontSize := 14; FreqDispB.ColorNormal := CLR_FREQ_DIM; FreqDispB.FontSize := 14;
FreqDispA.Height := 36; FreqDispA.Height := 36;
FreqDispB.Height := 28; FreqDispB.Height := 28;
LblVfoALabel.Font.Color := CLR_FREQ;
LblVfoBLabel.Font.Color := CLR_TEXTDIM;
end else begin end else begin
// VFO-B активен: яркий зелёный, крупный
FreqDispA.ColorNormal := CLR_FREQ_DIM; FreqDispA.FontSize := 14; FreqDispA.ColorNormal := CLR_FREQ_DIM; FreqDispA.FontSize := 14;
FreqDispB.ColorNormal := CLR_FREQ; FreqDispB.FontSize := 20; FreqDispB.ColorNormal := CLR_FREQ; FreqDispB.FontSize := 20;
FreqDispA.Height := 28; FreqDispA.Height := 28;
FreqDispB.Height := 36; FreqDispB.Height := 36;
LblVfoALabel.Font.Color := CLR_TEXTDIM;
LblVfoBLabel.Font.Color := CLR_FREQ;
end; end;
// FCenterFreq обновляется через ApplyVfoA, здесь не трогаем FreqDispA.Invalidate;
FreqDispB.Invalidate;
// При изменении частоты — обновляем HP пакет с новыми частотами и ALEX LblVfoALabel.Invalidate;
if FNetwork.Connected and FNetwork.Running then LblVfoBLabel.Invalidate;
begin
FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel,
FTransmitting, True, True);
FNetwork.SendFullHP;
end;
// WDSP: частота NCO задаётся через HPSDR железо (DDC), не через WDSP напрямую
end; end;
// =========================================================================== // ===========================================================================
@@ -2063,7 +2068,11 @@ begin
// ── 2. Полоса фильтра — заливка ДО спектра ───────────────────────────── // ── 2. Полоса фильтра — заливка ДО спектра ─────────────────────────────
Half := FFilterBW / 2; Half := FFilterBW / 2;
VfoX := Round((FVfoA - FCenterFreq + FSpanHz / 2) / FSpanHz * W); // Используем частоту активного VFO (A или B) для позиции маркера и фильтра
if FActiveVfo = 0 then
VfoX := Round((FVfoA - FCenterFreq + FSpanHz / 2) / FSpanHz * W)
else
VfoX := Round((FVfoB - FCenterFreq + FSpanHz / 2) / FSpanHz * W);
case FMode of case FMode of
0: begin Lo_Hz := -FFilterBW; Hi_Hz := -100; end; 0: begin Lo_Hz := -FFilterBW; Hi_Hz := -100; end;
1: begin Lo_Hz := 100; Hi_Hz := FFilterBW; end; 1: begin Lo_Hz := 100; Hi_Hz := FFilterBW; end;
@@ -2652,11 +2661,11 @@ begin
H := PbRuler.Height; H := PbRuler.Height;
if (W <= 0) or (H <= 0) then Exit; if (W <= 0) or (H <= 0) then Exit;
// Пропускаем перерисовку если ни частота центра, ни VFO не изменились // Пропускаем перерисовку если ни частота центра, ни активный VFO не изменились
if (Abs(FCenterFreq - FRulerLastFreq) < 0.5) and if (Abs(FCenterFreq - FRulerLastFreq) < 0.5) and
(Abs(FVfoA - FRulerLastVfo) < 0.5) then Exit; (Abs(ActiveVfoFreq - FRulerLastVfo) < 0.5) then Exit;
FRulerLastFreq := FCenterFreq; FRulerLastFreq := FCenterFreq;
FRulerLastVfo := FVfoA; FRulerLastVfo := ActiveVfoFreq;
if (FRulerBitmap.Width <> W) or (FRulerBitmap.Height <> H) then if (FRulerBitmap.Width <> W) or (FRulerBitmap.Height <> H) then
FRulerBitmap.SetSize(W, H); FRulerBitmap.SetSize(W, H);
@@ -2682,7 +2691,7 @@ begin
C.Font.Color := TColor($00C8D8E2); C.Font.Color := TColor($00C8D8E2);
C.TextOut(X - TW div 2, H div 2 - 1, Lbl); C.TextOut(X - TW div 2, H div 2 - 1, Lbl);
end; end;
VfoX := Round((FVfoA - FCenterFreq + FSpanHz / 2) / FSpanHz * W); VfoX := Round((ActiveVfoFreq - FCenterFreq + FSpanHz / 2) / FSpanHz * W);
if (VfoX >= 0) and (VfoX < W) then if (VfoX >= 0) and (VfoX < W) then
begin begin
C.Pen.Color := TColor($0000FF80); C.Pen.Color := TColor($0000FF80);
@@ -3381,23 +3390,178 @@ begin
end; end;
// ---------------------------------------------------------------------------
// ActivateVfo — переключаем активный VFO и перестраиваем приёмник.
// Idx=0 → VFO-A активен, Idx=1 → VFO-B активен.
// Перестройка всегда происходит на частоту активного VFO (не на оффсет).
// CTUN при этом сбрасывается чтобы не было путаницы с оффсетом.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// FreqToBandIdx — возвращает индекс диапазона для частоты Hz, или -1
// ---------------------------------------------------------------------------
function FreqToBandIdx(Hz: Double): Integer;
const
// Границы диапазонов [Low, High] в Гц (приблизительные)
BAND_LO: array[0..10] of Double = (
1800000, 3500000, 5330000, 7000000, 10100000,
14000000, 18068000, 21000000, 24890000, 28000000, 50000000);
BAND_HI: array[0..10] of Double = (
2000000, 4000000, 5410000, 7300000, 10150000,
14350000, 18168000, 21450000, 24990000, 29700000, 54000000);
var
i: Integer;
begin
Result := -1;
for i := 0 to 10 do
if (Hz >= BAND_LO[i]) and (Hz <= BAND_HI[i]) then
begin
Result := i;
Exit;
end;
end;
// ---------------------------------------------------------------------------
// ActiveVfoFreq — частота активного VFO
// ---------------------------------------------------------------------------
function TMainForm.ActiveVfoFreq: Int64;
begin
if FActiveVfo = 0 then Result := Round(FVfoA)
else Result := Round(FVfoB);
end;
// ---------------------------------------------------------------------------
// ActivateVfo — переключаем активный VFO и перестраиваем приёмник.
// Idx=0 → VFO-A активен, Idx=1 → VFO-B активен.
// Перестройка на частоту выбранного VFO. CTUN не трогаем.
// ---------------------------------------------------------------------------
procedure TMainForm.ActivateVfo(Idx: Integer);
var
ActiveFreq: Int64;
NewBand: Integer;
Offset: Double;
HalfSpan: Double;
FreqVisible: Boolean;
begin
FActiveVfo := Idx;
// Частота нового активного VFO
if FActiveVfo = 0 then
ActiveFreq := Round(FVfoA)
else
ActiveFreq := Round(FVfoB);
// Обновляем визуальный дисплей
UpdateVfoDisplay;
if FCTun then
begin
// CTUN включён: проверяем, видна ли новая частота на текущем спектре
HalfSpan := FSpanHz / 2;
Offset := ActiveFreq - FCenterFreq;
FreqVisible := (Offset > -HalfSpan) and (Offset < HalfSpan);
if FreqVisible then
begin
// Частота видна в окне — просто меняем shift, DDC не трогаем
if FWDSPReady then FDSPEngine.SetShift(Offset);
// Сеть не трогаем — DDC (FCenterFreq) остаётся прежним
end
else
begin
// Частота за пределами спектра — центрируем окно и сбрасываем CTUN
FCTun := False;
StyleButton(BtnCTun, False);
FBandCache[FCurrentBand].CTun := False;
FCenterFreq := ActiveFreq;
if FWDSPReady then FDSPEngine.SetShift(0.0);
if FNetwork.Connected and FNetwork.Running then
begin
FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel, FTransmitting, True, True);
FNetwork.SendFullHP;
end;
end;
end
else
begin
// CTUN выключен: перестраиваем DDC на новую частоту
FCenterFreq := ActiveFreq;
if FWDSPReady then FDSPEngine.SetShift(0.0);
if FNetwork.Connected and FNetwork.Running then
begin
FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel, FTransmitting, True, True);
FNetwork.SendFullHP;
end;
end;
// Переключаем кнопку диапазона по новой частоте
NewBand := FreqToBandIdx(ActiveFreq);
if (NewBand >= 0) and (NewBand <> FCurrentBand) then
begin
StyleButton(BtnBand[FCurrentBand], False);
FCurrentBand := NewBand;
StyleButton(BtnBand[FCurrentBand], True);
end;
// Инвалидируем кэш линейки
FRulerLastFreq := -1.0;
FRulerLastVfo := -1.0;
DrawSpectrum;
PbSpectrum.Invalidate;
DrawWaterfall;
PbWaterfall.Invalidate;
if PbRuler <> nil then PbRuler.Invalidate;
end;
procedure TMainForm.FreqDispAClick(Sender: TObject);
begin
// Клик на VFO-A → активируем A (перестройка на FVfoA)
if FActiveVfo <> 0 then
ActivateVfo(0);
end;
procedure TMainForm.FreqDispBClick(Sender: TObject);
begin
// Клик на VFO-B → активируем B (перестройка на FVfoB)
if FActiveVfo <> 1 then
ActivateVfo(1);
end;
procedure TMainForm.FreqDispAChanged(Sender: TObject; NewFreq: Int64); procedure TMainForm.FreqDispAChanged(Sender: TObject; NewFreq: Int64);
begin begin
ApplyVfoA(NewFreq); if FActiveVfo = 0 then
ApplyVfoA(NewFreq)
else
FVfoA := NewFreq; // просто сохраняем, приёмник не перестраиваем
end; end;
procedure TMainForm.FreqDispBChanged(Sender: TObject; NewFreq: Int64); procedure TMainForm.FreqDispBChanged(Sender: TObject; NewFreq: Int64);
var
BandIdx: Integer;
begin begin
FVfoB := NewFreq; FVfoB := NewFreq;
if FActiveVfo = 1 then if FActiveVfo = 1 then
begin begin
FCenterFreq := FVfoB; // VFO-B активен: перестраиваем приёмник
FCenterFreq := FVfoB; FCenterFreq := FVfoB;
if FWDSPReady then FDSPEngine.SetShift(0.0);
if FNetwork.Connected and FNetwork.Running then if FNetwork.Connected and FNetwork.Running then
begin begin
FNetwork.UpdateState(FVfoB, FVfoB, FDriveLevel, FTransmitting, True, True); FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel, FTransmitting, True, True);
FNetwork.SendFullHP; FNetwork.SendFullHP;
end; end;
// Обновляем диапазон
BandIdx := FreqToBandIdx(FVfoB);
if (BandIdx >= 0) and (BandIdx <> FCurrentBand) then
begin
StyleButton(BtnBand[FCurrentBand], False);
FCurrentBand := BandIdx;
StyleButton(BtnBand[FCurrentBand], True);
end;
FRulerLastFreq := -1.0; FRulerLastVfo := -1.0;
DrawSpectrum;
PbSpectrum.Invalidate;
if PbRuler <> nil then PbRuler.Invalidate;
end; end;
end; end;
@@ -3464,6 +3628,7 @@ var
FiltHi: Double; FiltHi: Double;
HalfSpan: Double; HalfSpan: Double;
Scrolled: Boolean; Scrolled: Boolean;
BandIdx: Integer;
begin begin
FVfoA := NewFreq; FVfoA := NewFreq;
Scrolled := False; Scrolled := False;
@@ -3513,6 +3678,15 @@ begin
// Обновляем VFO дисплей // Обновляем VFO дисплей
FreqDispA.Frequency := Round(FVfoA); FreqDispA.Frequency := Round(FVfoA);
// Обновляем кнопку диапазона если частота перешла в другой диапазон
BandIdx := FreqToBandIdx(FVfoA);
if (BandIdx >= 0) and (BandIdx <> FCurrentBand) then
begin
StyleButton(BtnBand[FCurrentBand], False);
FCurrentBand := BandIdx;
StyleButton(BtnBand[FCurrentBand], True);
end;
// DDC: передаём FCenterFreq (при CTUN OFF = FVfoA, при CTUN ON = фиксирован) // DDC: передаём FCenterFreq (при CTUN OFF = FVfoA, при CTUN ON = фиксирован)
if FNetwork.Connected and FNetwork.Running then if FNetwork.Connected and FNetwork.Running then
begin begin
@@ -3582,13 +3756,37 @@ procedure TMainForm.DoSpectrumClick(PixelX: Integer; PanelWidth: Integer);
var var
ClickFreq: Int64; ClickFreq: Int64;
StepHz: Int64; StepHz: Int64;
BandIdx: Integer;
begin begin
if PanelWidth <= 0 then Exit; if PanelWidth <= 0 then Exit;
StepHz := 100; StepHz := 100;
// Пиксель → частота через текущий центр дисплея (FCenterFreq)
ClickFreq := Round(FCenterFreq + (PixelX / PanelWidth - 0.5) * FSpanHz); ClickFreq := Round(FCenterFreq + (PixelX / PanelWidth - 0.5) * FSpanHz);
ClickFreq := (ClickFreq div StepHz) * StepHz; ClickFreq := (ClickFreq div StepHz) * StepHz;
ApplyVfoA(ClickFreq); if FActiveVfo = 0 then
ApplyVfoA(ClickFreq)
else
begin
FVfoB := ClickFreq;
FreqDispB.Frequency := Round(FVfoB);
FCenterFreq := ClickFreq;
if FWDSPReady then FDSPEngine.SetShift(0.0);
if FNetwork.Connected and FNetwork.Running then
begin
FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel, FTransmitting, True, True);
FNetwork.SendFullHP;
end;
BandIdx := FreqToBandIdx(FVfoB);
if (BandIdx >= 0) and (BandIdx <> FCurrentBand) then
begin
StyleButton(BtnBand[FCurrentBand], False);
FCurrentBand := BandIdx;
StyleButton(BtnBand[FCurrentBand], True);
end;
FRulerLastFreq := -1.0; FRulerLastVfo := -1.0;
DrawSpectrum;
PbSpectrum.Invalidate;
if PbRuler <> nil then PbRuler.Invalidate;
end;
end; end;
procedure TMainForm.DoSpectrumDrag(PixelX: Integer; PanelWidth: Integer); procedure TMainForm.DoSpectrumDrag(PixelX: Integer; PanelWidth: Integer);
@@ -3602,21 +3800,40 @@ begin
if FCTun then if FCTun then
begin begin
// CTUN ON: drag двигает окно просмотра (FCenterFreq/DDC) // CTUN ON: drag двигает окно просмотра (FCenterFreq/DDC)
// VFO остаётся, shift обновляется = FVfoA - новый FCenterFreq // VFO остаётся, shift обновляется = VFO - новый FCenterFreq
FCenterFreq := FSpecDragFreq - dFreq; FCenterFreq := FSpecDragFreq - dFreq;
if FWDSPReady then if FWDSPReady then
FDSPEngine.SetShift(FVfoA - FCenterFreq); begin
if FActiveVfo = 0 then
FDSPEngine.SetShift(FVfoA - FCenterFreq)
else
FDSPEngine.SetShift(FVfoB - FCenterFreq);
end;
if FNetwork.Connected and FNetwork.Running then if FNetwork.Connected and FNetwork.Running then
begin begin
FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel, FTransmitting, True, True); FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel, FTransmitting, True, True);
FNetwork.SendFullHP; FNetwork.SendFullHP;
end; end;
FSpectrumDirty := True; // таймер перерисует в ближайшем тике FSpectrumDirty := True;
end end
else else
begin begin
// CTUN OFF: drag двигает VFO, FCenterFreq следует через ApplyVfoA // CTUN OFF: drag двигает VFO, FCenterFreq следует через Apply
ApplyVfoA(Round(FSpecDragFreq - dFreq)); if FActiveVfo = 0 then
ApplyVfoA(Round(FSpecDragFreq - dFreq))
else
begin
FVfoB := FSpecDragFreq - dFreq;
FreqDispB.Frequency := Round(FVfoB);
FCenterFreq := FVfoB;
if FWDSPReady then FDSPEngine.SetShift(0.0);
if FNetwork.Connected and FNetwork.Running then
begin
FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel, FTransmitting, True, True);
FNetwork.SendFullHP;
end;
FSpectrumDirty := True;
end;
end; end;
end; end;
@@ -3636,7 +3853,10 @@ begin
FSpecDrag := True; FSpecDrag := True;
FSpecDragX0 := X; FSpecDragX0 := X;
if FCTun then FSpecDragFreq := FCenterFreq if FCTun then FSpecDragFreq := FCenterFreq
else FSpecDragFreq := FVfoA; else begin
if FActiveVfo = 0 then FSpecDragFreq := FVfoA
else FSpecDragFreq := FVfoB;
end;
PbSpectrum.Cursor := crSizeWE; PbSpectrum.Cursor := crSizeWE;
end end
else if Button = mbRight then else if Button = mbRight then
@@ -3698,7 +3918,10 @@ begin
FSpecDrag := True; FSpecDrag := True;
FSpecDragX0 := X; FSpecDragX0 := X;
if FCTun then FSpecDragFreq := FCenterFreq if FCTun then FSpecDragFreq := FCenterFreq
else FSpecDragFreq := FVfoA; else begin
if FActiveVfo = 0 then FSpecDragFreq := FVfoA
else FSpecDragFreq := FVfoB;
end;
PbWaterfall.Cursor := crSizeWE; PbWaterfall.Cursor := crSizeWE;
end end
else if Button = mbRight then else if Button = mbRight then
@@ -3812,23 +4035,76 @@ begin
end; end;
procedure TMainForm.BtnVfoSwapClick(Sender: TObject); procedure TMainForm.BtnVfoSwapClick(Sender: TObject);
var Tmp: Double; var
Tmp: Double;
BandIdx: Integer;
ActiveFreq: Int64;
begin begin
// Меняем частоты местами
Tmp := FVfoA; FVfoA := FVfoB; FVfoB := Tmp; Tmp := FVfoA; FVfoA := FVfoB; FVfoB := Tmp;
// Определяем новую частоту активного VFO
if FActiveVfo = 0 then ActiveFreq := Round(FVfoA)
else ActiveFreq := Round(FVfoB);
// Перестраиваем приёмник
FCenterFreq := ActiveFreq;
if FWDSPReady then FDSPEngine.SetShift(0.0);
if FNetwork.Connected and FNetwork.Running then
begin
FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel, FTransmitting, True, True);
FNetwork.SendFullHP;
end;
// Обновляем кнопку диапазона
BandIdx := FreqToBandIdx(ActiveFreq);
if (BandIdx >= 0) and (BandIdx <> FCurrentBand) then
begin
StyleButton(BtnBand[FCurrentBand], False);
FCurrentBand := BandIdx;
StyleButton(BtnBand[FCurrentBand], True);
end;
FRulerLastFreq := -1.0; FRulerLastVfo := -1.0;
UpdateVfoDisplay; UpdateVfoDisplay;
UpdateFilterButtons; DrawSpectrum;
if FRunning then FNetwork.SetRunAndFreq(True, FCenterFreq, FCenterFreq, FDriveLevel); PbSpectrum.Invalidate;
if PbRuler <> nil then PbRuler.Invalidate;
end; end;
procedure TMainForm.BtnVfoACopyBClick(Sender: TObject); procedure TMainForm.BtnVfoACopyBClick(Sender: TObject);
begin begin
FVfoB := FVfoA; UpdateVfoDisplay; // A>B: копируем частоту A в B (VFO-B получает частоту VFO-A)
FVfoB := FVfoA;
if FActiveVfo = 1 then
begin
// B активен — перестраиваем приёмник на новую частоту B
FCenterFreq := Round(FVfoB);
if FWDSPReady then FDSPEngine.SetShift(0.0);
if FNetwork.Connected and FNetwork.Running then
begin
FNetwork.UpdateState(FCenterFreq, FCenterFreq, FDriveLevel, FTransmitting, True, True);
FNetwork.SendFullHP;
end;
FRulerLastFreq := -1.0; FRulerLastVfo := -1.0;
DrawSpectrum; PbSpectrum.Invalidate;
if PbRuler <> nil then PbRuler.Invalidate;
end;
FreqDispB.Frequency := Round(FVfoB);
UpdateVfoDisplay;
end; end;
procedure TMainForm.BtnVfoBCopyAClick(Sender: TObject); procedure TMainForm.BtnVfoBCopyAClick(Sender: TObject);
begin begin
FVfoA := FVfoB; UpdateVfoDisplay; // B>A: копируем частоту B в A (VFO-A получает частоту VFO-B)
if FRunning then FNetwork.SetRunAndFreq(True, FCenterFreq, FCenterFreq, FDriveLevel); FVfoA := FVfoB;
if FActiveVfo = 0 then
ApplyVfoA(Round(FVfoA)) // A активен — перестраиваем приёмник
else
begin
FreqDispA.Frequency := Round(FVfoA);
UpdateVfoDisplay;
end;
end; end;
procedure TMainForm.BtnMOXClick(Sender: TObject); procedure TMainForm.BtnMOXClick(Sender: TObject);