feat(fm): add universal FM RAW decoder output

This commit is contained in:
2026-07-16 18:58:28 +03:00
parent 3acd569da5
commit cd816c54d6
16 changed files with 656 additions and 148 deletions
+79
View File
@@ -57,6 +57,9 @@ type
FLastAGCHangY: Integer;
FLastMarkerText: string;
FLastMarkerX: Integer;
// 2TON/IMD: текстуры подписей маркеров (0..3 = кружки, 4 = сводка IMD3)
FIMDLabelTex: array[0..4] of TGLTextureCache;
FIMDLastText: array[0..4] of string;
FLastADCVisible: Boolean;
FLastSampleX: Integer;
FLastSampleY: Integer;
@@ -90,6 +93,8 @@ type
procedure DrawSpectrumCurve(W, H: Integer; DBmax, InvRange: Double);
procedure DrawMarker(W, H: Integer);
procedure DrawBeaconMarkersGL(W, H: Integer);
procedure DrawCircleGL(CX, CY: Integer; R: Single; C: TColor);
procedure DrawIMDMarkersGL(W, H: Integer; DBmax, InvRange: Double);
procedure DrawADCOverlay(W, H: Integer);
procedure DrawCachedOverlays(W, H: Integer);
procedure DrawSliceOverlays(W, H: Integer);
@@ -170,6 +175,7 @@ begin
DeleteTexture(FBandOverlayTex);
for i := 0 to High(FSliceTex) do DeleteTexture(FSliceTex[i].Tex);
for i := 0 to High(FLetterTex) do DeleteTexture(FLetterTex[i]);
for i := 0 to High(FIMDLabelTex) do DeleteTexture(FIMDLabelTex[i]);
end;
SetLength(FSliceTex, 0);
inherited Destroy;
@@ -451,6 +457,11 @@ begin
Zap(FBandOverlayTex);
for i := 0 to High(FSliceTex) do Zap(FSliceTex[i].Tex);
for i := 0 to High(FLetterTex) do Zap(FLetterTex[i]);
for i := 0 to High(FIMDLabelTex) do
begin
Zap(FIMDLabelTex[i]);
FIMDLastText[i] := '';
end;
FLastAGCText := '';
FLastMarkerText := '';
FGridDirty := True;
@@ -803,6 +814,71 @@ begin
end;
end;
procedure TSpectrumViewOpenGL.DrawCircleGL(CX, CY: Integer; R: Single; C: TColor);
// Залитый кружок (triangle fan, 20 сегментов) — маркер пика 2TON/IMD.
var
CR, CG, CB: GLFloat;
a: Integer;
Ang: Single;
begin
ColorToRGB(C, CR, CG, CB);
glColor4f(CR, CG, CB, 1);
glBegin(GL_TRIANGLE_FAN);
glVertex2f(CX + 0.5, CY + 0.5);
for a := 0 to 20 do
begin
Ang := a * 2 * Pi / 20;
glVertex2f(CX + 0.5 + R * Cos(Ang), CY + 0.5 + R * Sin(Ang));
end;
glEnd;
end;
procedure TSpectrumViewOpenGL.DrawIMDMarkersGL(W, H: Integer;
DBmax, InvRange: Double);
// GL-вариант DrawIMDMarkersRaw: замер общий (ComputeIMDMarkers), подписи —
// текстуры с кэшем по строке (перезаливка только при смене значения).
const
CLR_TONE = TColor($0050FF50); // зелёный — основные тона
CLR_IMD = TColor($000A8CFF); // оранжевый — продукты IMD3
var
M: array[0..3] of TIMDMarker;
N, i, X, Y, TX, TY: Integer;
Col: TColor;
begin
if FSpanHz <= 0 then Exit;
N := ComputeIMDMarkers(M);
if N = 0 then Exit;
for i := 0 to N - 1 do
begin
if M[i].LevelDB < -500 then Continue; // частота вне окна буфера
X := Round((M[i].FreqHz - (FCenterFreq - FSpanHz / 2)) / FSpanHz * W);
if (X < 0) or (X >= W) then Continue;
Y := Max(0, Min(H - 1, Round((DBmax - M[i].LevelDB) * InvRange * H)));
if M[i].IsTone then Col := CLR_TONE else Col := CLR_IMD;
DrawCircleGL(X, Y, 4.0, Col);
if (M[i].Caption <> FIMDLastText[i]) or FIMDLabelTex[i].Dirty then
begin
UploadText(FIMDLabelTex[i], M[i].Caption, Col, 7, True);
FIMDLastText[i] := M[i].Caption;
end;
TY := Y - 14;
if TY < 2 then TY := Y + 6;
TX := X + 6;
if TX + FIMDLabelTex[i].W >= W then TX := X - 6 - FIMDLabelTex[i].W;
DrawTexture(FIMDLabelTex[i], TX, TY);
end;
// Сводка в левом верхнем углу
if FIMDSummary <> '' then
begin
if (FIMDSummary <> FIMDLastText[4]) or FIMDLabelTex[4].Dirty then
begin
UploadText(FIMDLabelTex[4], FIMDSummary, CLR_IMD, 9, True);
FIMDLastText[4] := FIMDSummary;
end;
DrawTexture(FIMDLabelTex[4], 6, 6);
end;
end;
procedure TSpectrumViewOpenGL.DrawADCOverlay(W, H: Integer);
var
B: TBitmap;
@@ -1016,6 +1092,9 @@ begin
DrawSpectrumCurve(W, H, DBmax, InvRange);
DrawMarker(W, H);
if FBeaconMarkActive or FBeaconDecActive then DrawBeaconMarkersGL(W, H);
// 2TON: кружки с уровнями на пиках тонов/IMD3 (активирует MainForm при
// 2TON+TX+DUP — реальный сигнал после PA виден на RX-спектре)
if FIMDActive then DrawIMDMarkersGL(W, H, DBmax, InvRange);
DrawADCOverlay(W, H);
DrawCachedOverlays(W, H);