diff --git a/SpectrumView.pas b/SpectrumView.pas index 165cbba..4cc1d76 100644 --- a/SpectrumView.pas +++ b/SpectrumView.pas @@ -37,8 +37,8 @@ type end; // Маркер 2TON/IMD-измерения на спектре: кружок на пике продукта + подпись. - // Тона подписываются уровнем шкалы (dB), IMD-продукты — в dBc от среднего - // уровня тонов. + // Все маркеры подписываются абсолютным уровнем шкалы (dBm); относительная + // величина IMD3 в dBc — в сводке слева от полосы фильтра. TIMDMarker = record FreqHz: Double; // абсолютная частота продукта LevelDB: Double; // измеренный пик (шкала дисплея) @@ -741,11 +741,10 @@ begin M[i].LevelDB := PeakDB(M[i].FreqHz); ToneAvg := (M[0].LevelDB + M[1].LevelDB) * 0.5; - M[0].Caption := Format('%.0f', [M[0].LevelDB]); - M[1].Caption := Format('%.0f', [M[1].LevelDB]); - // IMD-продукты — в dBc относительно среднего уровня тонов - M[2].Caption := Format('%.0fc', [M[2].LevelDB - ToneAvg]); - M[3].Caption := Format('%.0fc', [M[3].LevelDB - ToneAvg]); + // Все маркеры — абсолютный уровень шкалы (dBm, как в Thetis); относительная + // величина в dBc есть в сводке FIMDSummary, чтобы не путать единицы. + for i := 0 to 3 do + M[i].Caption := Format('%.0f', [M[i].LevelDB]); // Сводка только когда оба тона и хотя бы один продукт в окне буфера if (M[0].LevelDB > -500) and (M[1].LevelDB > -500) and (Max(M[2].LevelDB, M[3].LevelDB) > -500) then @@ -782,7 +781,7 @@ const CLR_IMD = TColor($000A8CFF); // оранжевый — продукты IMD3 var M: array[0..3] of TIMDMarker; - N, i, X, Y, TY: Integer; + N, i, X, Y, TY, TW, BX1, BX2, BVX: Integer; Col: TColor; begin if FSpanHz <= 0 then Exit; @@ -794,20 +793,27 @@ begin 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; + // Все маркеры зелёные (dBm-замеры); оранжевый — только вычисленная + // сводка dBc, чтобы цвета не путались. + Col := CLR_TONE; RawCircle(X, Y, 4, Col); - // Подпись правее-выше кружка; у верхней кромки — вниз, у правой — влево. - TY := Y - 14; - if TY < 2 then TY := Y + 6; - if X + 6 + RawTextWidth(M[i].Caption, 7, True) < W then - RawText(X + 6, TY, M[i].Caption, Col, 7, True, clNone) - else - RawText(X - 6 - RawTextWidth(M[i].Caption, 7, True), TY, - M[i].Caption, Col, 7, True, clNone); + // Подпись по центру над своим кружком; у верхней кромки — под ним. + TW := RawTextWidth(M[i].Caption, 7, True); + TY := Y - 16; + if TY < 2 then TY := Y + 8; + RawText(Max(2, Min(W - TW - 2, X - TW div 2)), TY, + M[i].Caption, Col, 7, True, clNone); end; - // Сводка в левом верхнем углу + // Сводка слева от полосы фильтра, чтобы не прятаться за SampleRateOverlay + // в левом верхнем углу и сразу бросаться в глаза рядом с сигналом. if FIMDSummary <> '' then - RawText(6, 6, FIMDSummary, CLR_IMD, 9, True, clNone); + begin + TW := RawTextWidth(FIMDSummary, 11, True); + if FActiveVfo = 0 then CalcFilterBandX(FVfoA, W, BX1, BX2, BVX) + else CalcFilterBandX(FVfoB, W, BX1, BX2, BVX); + X := Max(2, Min(W - TW - 2, BX1 - TW - 8)); + RawText(X, 6, FIMDSummary, CLR_IMD, 11, True, clNone); + end; end; // ════════════════════════════════════════════════════════════════════════════ diff --git a/SpectrumViewOpengl.pas b/SpectrumViewOpengl.pas index 4b7d605..cdfb03c 100644 --- a/SpectrumViewOpengl.pas +++ b/SpectrumViewOpengl.pas @@ -842,7 +842,7 @@ const CLR_IMD = TColor($000A8CFF); // оранжевый — продукты IMD3 var M: array[0..3] of TIMDMarker; - N, i, X, Y, TX, TY: Integer; + N, i, X, Y, TX, TY, BX1, BX2, BVX: Integer; Col: TColor; begin if FSpanHz <= 0 then Exit; @@ -854,28 +854,35 @@ begin 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; + // Все маркеры зелёные (dBm-замеры); оранжевый — только вычисленная + // сводка dBc, чтобы цвета не путались. + Col := CLR_TONE; 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; + // Подпись по центру над своим кружком; у верхней кромки — под ним. + TY := Y - 16; + if TY < 2 then TY := Y + 8; + TX := Max(2, Min(W - FIMDLabelTex[i].W - 2, X - FIMDLabelTex[i].W div 2)); DrawTexture(FIMDLabelTex[i], TX, TY); end; - // Сводка в левом верхнем углу + // Сводка слева от полосы фильтра, чтобы не прятаться за SampleRateOverlay + // в левом верхнем углу и сразу бросаться в глаза рядом с сигналом. if FIMDSummary <> '' then begin if (FIMDSummary <> FIMDLastText[4]) or FIMDLabelTex[4].Dirty then begin - UploadText(FIMDLabelTex[4], FIMDSummary, CLR_IMD, 9, True); + UploadText(FIMDLabelTex[4], FIMDSummary, CLR_IMD, 11, True); FIMDLastText[4] := FIMDSummary; end; - DrawTexture(FIMDLabelTex[4], 6, 6); + if FActiveVfo = 0 then CalcFilterBandX(FVfoA, W, BX1, BX2, BVX) + else CalcFilterBandX(FVfoB, W, BX1, BX2, BVX); + TX := Max(2, Min(W - FIMDLabelTex[4].W - 2, + BX1 - FIMDLabelTex[4].W - 8)); + DrawTexture(FIMDLabelTex[4], TX, 6); end; end;