diff --git a/MainForm.pas b/MainForm.pas index e89e5c6..6d0347f 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -665,6 +665,7 @@ type procedure PositionVfoOverlay; procedure OnModeFilterSelect(Mode: Integer; FilterBW: Integer); procedure OnVfoOverlayDSPChange(NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean); + procedure OnVfoOverlayAGCChange(AGCMode: Integer); procedure PbSpectrumDblClick(Sender: TObject); procedure TrkDriveChange(Sender: TObject); procedure TrkVolumeChange(Sender: TObject); @@ -1330,6 +1331,7 @@ begin FVfoOverlay := TVfoOverlay.Create(Self); FVfoOverlay.OnSelect := OnModeFilterSelect; FVfoOverlay.OnDSPChange := OnVfoOverlayDSPChange; + FVfoOverlay.OnAGCChange := OnVfoOverlayAGCChange; FVfoOverlay.Visible := False; FPanelHidden := False; FShowSpectrum := True; @@ -5397,9 +5399,9 @@ begin BtnHidePanel.Caption := '▶'; FVfoOverlay.Parent := PanelRight; FVfoOverlay.Width := 260; - FVfoOverlay.Height := 136; + FVfoOverlay.Height := 136; // OVL_H_NORM — расширяется сам при открытии AGC-пикера FVfoOverlay.SetState(FMode, FFilterBW, FVfoA, FLastSMeter); - FVfoOverlay.SetDSPState(BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0); + FVfoOverlay.SetDSPState(BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0, FAGCMode); FVfoOverlay.Visible := True; FVfoOverlay.BringToFront; PositionVfoOverlay; @@ -5463,7 +5465,7 @@ begin if Assigned(FVfoOverlay) and FVfoOverlay.Visible then begin FVfoOverlay.SetState(FMode, FFilterBW, FVfoA, FLastSMeter); - FVfoOverlay.SetDSPState(BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0); + FVfoOverlay.SetDSPState(BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0, FAGCMode); PositionVfoOverlay; // перепозиционируем при смене LSB↔USB end; end; @@ -5477,6 +5479,19 @@ begin ApplyNoiseFilterButtonsToDSP; end; +procedure TMainForm.OnVfoOverlayAGCChange(AGCMode: Integer); +const + AGCModes: array[0..4] of TWDSPAGCMode = (agcFast, agcMedium, agcSlow, agcLong, agcOff); +var + I: Integer; +begin + FAGCMode := AGCMode; + for I := 0 to 4 do StyleButton(BtnAGCMode[I], I = AGCMode); + if FWDSPReady then + FDSPEngine.SetAGC(AGCModes[AGCMode], 50.0); + FBandCache[FCurrentBand].AGCMode := FAGCMode; +end; + procedure TMainForm.BtnSpanClick(Sender: TObject); var NewRate: Integer; diff --git a/VfoOverlay.pas b/VfoOverlay.pas index a69351e..74f5e82 100644 --- a/VfoOverlay.pas +++ b/VfoOverlay.pas @@ -10,6 +10,7 @@ uses type TModeFilterEvent = procedure(Mode: Integer; FilterBW: Integer) of object; TDSPChangeEvent = procedure(NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean) of object; + TAGCChangeEvent = procedure(AGCMode: Integer) of object; TVfoOverlay = class(TCustomControl) private @@ -17,17 +18,20 @@ type FFilterBW: Integer; FVfoHz: Double; FSMeterDB: Double; - FOnSelect: TModeFilterEvent; + FOnSelect: TModeFilterEvent; FOnDSPChange: TDSPChangeEvent; + FOnAGCChange: TAGCChangeEvent; FModeFilter: array[0..7] of Integer; FNRMode: Integer; // 0=off, 1..4=NR/NR2/NR3/NR4 FNBMode: Integer; // 0=off, 1=NB, 2=NB2 FSNBOn: Boolean; FANFOn: Boolean; + FAGCMode: Integer; // 0=FAST 1=MED 2=SLOW 3=LONG 4=OFF + FAGCPickerOpen: Boolean; - FHitRects: array[0..19] of TRect; - FHitActions: array[0..19] of Integer; // <0 = режим/DSP, >0 = BW фильтра + FHitRects: array[0..24] of TRect; + FHitActions: array[0..24] of Integer; FHitCount: Integer; FHotIdx: Integer; @@ -36,6 +40,7 @@ type procedure DrawModeRow(C: TCanvas; X, Y, RowW, RowH: Integer); procedure DrawFilterRow(C: TCanvas; X, Y, RowW, RowH: Integer); procedure DrawDSPRow(C: TCanvas; X, Y, RowW, RowH: Integer); + procedure DrawAGCPickerRow(C: TCanvas; X, Y, RowW, RowH: Integer); procedure RegisterHit(HR: TRect; HV: Integer); function CalcSLabel: string; function FormatFreq(Hz: Double): string; @@ -53,11 +58,13 @@ type public constructor Create(AOwner: TComponent); override; procedure SetState(AMode, ABW: Integer; AVfoHz, ASMeterDB: Double); - procedure SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean); + procedure SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean; + AAGCMode: Integer); procedure UpdateSMeter(DB: Double); procedure UpdateVfo(AVfoHz: Double); property OnSelect: TModeFilterEvent read FOnSelect write FOnSelect; property OnDSPChange: TDSPChangeEvent read FOnDSPChange write FOnDSPChange; + property OnAGCChange: TAGCChangeEvent read FOnAGCChange write FOnAGCChange; end; implementation @@ -67,6 +74,14 @@ const HIT_NB = -10; HIT_SNB = -11; HIT_ANF = -12; + HIT_AGC = -13; + + HIT_AGC_PICK_BASE = 100000; // 100000+N — выбор режима AGC (>всех BW фильтров) + + AGC_NAMES: array[0..4] of string = ('FAST', 'MED', 'SLOW', 'LONG', 'OFF'); + + OVL_H_NORM = 136; // высота без пикера AGC + OVL_H_AGC = 159; // высота с открытым пикером (136 + GAP(3) + ROW_H(20)) CLR_PANEL_BG = TColor($00141414); CLR_BORDER = TColor($00505050); @@ -185,12 +200,14 @@ begin FModeFilter[5] := 11000; // FM (NFM по умолчанию) FModeFilter[6] := 8000; // AM FModeFilter[7] := 8000; // SAM - FNRMode := 0; - FNBMode := 0; - FSNBOn := False; - FANFOn := False; - Width := 260; - Height := 136; + FNRMode := 0; + FNBMode := 0; + FSNBOn := False; + FANFOn := False; + FAGCMode := 0; + FAGCPickerOpen := False; + Width := 260; + Height := OVL_H_NORM; Cursor := crHandPoint; end; @@ -356,8 +373,8 @@ end; procedure TVfoOverlay.DrawDSPRow(C: TCanvas; X, Y, RowW, RowH: Integer); const - DSP_COUNT = 4; - DSP_HITS: array[0..3] of Integer = (HIT_NR, HIT_NB, HIT_SNB, HIT_ANF); + DSP_COUNT = 5; + DSP_HITS: array[0..4] of Integer = (HIT_NR, HIT_NB, HIT_SNB, HIT_ANF, HIT_AGC); var I, BX, BRight: Integer; HR: TRect; @@ -386,6 +403,7 @@ begin end; 2: begin Lbl := 'SNB'; IsAct := FSNBOn; end; 3: begin Lbl := 'ANF'; IsAct := FANFOn; end; + 4: begin Lbl := AGC_NAMES[FAGCMode]; IsAct := FAGCPickerOpen; end; else Lbl := ''; IsAct := False; end; IsHot := (FHotIdx >= 0) and (FHitActions[FHotIdx] = DSP_HITS[I]); @@ -410,6 +428,43 @@ begin end; end; +procedure TVfoOverlay.DrawAGCPickerRow(C: TCanvas; X, Y, RowW, RowH: Integer); +const + AGC_COUNT = 5; +var + I, BX, BRight: Integer; + HR: TRect; + IsAct, IsHot: Boolean; +begin + BX := X; + for I := 0 to AGC_COUNT - 1 do + begin + BRight := X + ((I + 1) * RowW) div AGC_COUNT; + HR := Rect(BX, Y, BRight, Y + RowH); + IsAct := (I = FAGCMode); + IsHot := (FHotIdx >= 0) and + (FHitActions[FHotIdx] = HIT_AGC_PICK_BASE + I); + + if IsAct then C.Brush.Color := CLR_BTN_ACT + else if IsHot then C.Brush.Color := CLR_BTN_HOT + else C.Brush.Color := CLR_BTN_NORM; + C.Brush.Style := bsSolid; C.Pen.Style := psClear; + C.FillRect(HR); + C.Pen.Style := psSolid; C.Pen.Color := CLR_BORDER; + C.Brush.Style := bsClear; C.Rectangle(HR); + + if IsAct then C.Font.Color := CLR_ACCENT + else C.Font.Color := CLR_TEXT; + C.Font.Size := 7; C.Font.Name := 'Courier New'; + C.Brush.Style := bsClear; + C.TextOut(BX + (BRight - BX - C.TextWidth(AGC_NAMES[I])) div 2, + Y + (RowH - C.TextHeight('A')) div 2, AGC_NAMES[I]); + + RegisterHit(HR, HIT_AGC_PICK_BASE + I); + BX := BRight + 1; + end; +end; + procedure TVfoOverlay.DrawSelf(C: TCanvas; W, H: Integer); const PAD = 4; @@ -463,8 +518,15 @@ begin DrawFilterRow(C, PAD, CurY, W - PAD * 2, ROW_H); Inc(CurY, ROW_H + GAP); - // Кнопки DSP (NR, NB, SNB, ANF) + // Кнопки DSP (NR, NB, SNB, ANF, AGC) DrawDSPRow(C, PAD, CurY, W - PAD * 2, ROW_H); + + // Пикер AGC (открывается кнопкой AGC) + if FAGCPickerOpen then + begin + Inc(CurY, ROW_H + GAP); + DrawAGCPickerRow(C, PAD, CurY, W - PAD * 2, ROW_H); + end; end; procedure TVfoOverlay.Paint; @@ -501,17 +563,36 @@ begin else begin // Кнопка DSP - case HV of - HIT_NR: FNRMode := (FNRMode + 1) mod 5; - HIT_NB: FNBMode := (FNBMode + 1) mod 3; - HIT_SNB: FSNBOn := not FSNBOn; - HIT_ANF: FANFOn := not FANFOn; + if HV = HIT_AGC then + begin + FAGCPickerOpen := not FAGCPickerOpen; + if FAGCPickerOpen then Height := OVL_H_AGC + else Height := OVL_H_NORM; + Invalidate; + end + else + begin + case HV of + HIT_NR: FNRMode := (FNRMode + 1) mod 5; + HIT_NB: FNBMode := (FNBMode + 1) mod 3; + HIT_SNB: FSNBOn := not FSNBOn; + HIT_ANF: FANFOn := not FANFOn; + end; + Invalidate; + if Assigned(FOnDSPChange) then + FOnDSPChange(FNRMode, FNBMode, FSNBOn, FANFOn); end; - Invalidate; - if Assigned(FOnDSPChange) then - FOnDSPChange(FNRMode, FNBMode, FSNBOn, FANFOn); end; end + else if HV >= HIT_AGC_PICK_BASE then + begin + // Выбор режима AGC из пикера + FAGCMode := HV - HIT_AGC_PICK_BASE; + FAGCPickerOpen := False; + Height := OVL_H_NORM; + Invalidate; + if Assigned(FOnAGCChange) then FOnAGCChange(FAGCMode); + end else if HV > 0 then begin NewBW := HV; @@ -556,12 +637,14 @@ begin Invalidate; end; -procedure TVfoOverlay.SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean); +procedure TVfoOverlay.SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean; + AAGCMode: Integer); begin - FNRMode := ANRMode; - FNBMode := ANBMode; - FSNBOn := ASNBOn; - FANFOn := AANFOn; + FNRMode := ANRMode; + FNBMode := ANBMode; + FSNBOn := ASNBOn; + FANFOn := AANFOn; + FAGCMode := AAGCMode; Invalidate; end;