mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
fix(dmr): normalize voice audio and lock bandwidth
This commit is contained in:
+10
-1
@@ -187,6 +187,9 @@ const
|
||||
DMR_SYNC_MASK = LongWord($00FFFFFF);
|
||||
DMR_SYNC_HOLD_MS = 500;
|
||||
DMR_SYNC_MAX_ERRORS = 2;
|
||||
// mbelib's floating-point API retains the signed 16-bit PCM scale. EWSDR's
|
||||
// audio outputs use normalized floating point, where full scale is +/-1.0.
|
||||
DMR_PCM_SCALE = 1.0 / 32768.0;
|
||||
DMR_SUPERFRAME_SAMPLES = DMR_INPUT_RATE * 360 div 1000;
|
||||
DMR_SYNC_CADENCE_TOLERANCE = DMR_SAMPLES_PER_SYM * 3;
|
||||
|
||||
@@ -771,7 +774,7 @@ var
|
||||
Frames: TDMRAMBEFrames;
|
||||
PCM: array[0..DMR_PCM_SAMPLES - 1] of Single;
|
||||
MbeResult: TDMRMbeResult;
|
||||
Slot, FrameIndex, RC: Integer;
|
||||
Slot, FrameIndex, SampleIndex, RC: Integer;
|
||||
NowTick: QWord;
|
||||
begin
|
||||
if (FMbe = nil) or (FLastSyncTick = 0) or
|
||||
@@ -818,6 +821,12 @@ begin
|
||||
end;
|
||||
Inc(FVoiceFrameCount);
|
||||
if MbeResult.ErrorsTotal > 0 then Inc(FVoiceErrorCount, MbeResult.ErrorsTotal);
|
||||
for SampleIndex := 0 to High(PCM) do
|
||||
begin
|
||||
PCM[SampleIndex] := PCM[SampleIndex] * DMR_PCM_SCALE;
|
||||
if PCM[SampleIndex] > 1.0 then PCM[SampleIndex] := 1.0
|
||||
else if PCM[SampleIndex] < -1.0 then PCM[SampleIndex] := -1.0;
|
||||
end;
|
||||
if Assigned(FOnAudio) then FOnAudio(PCM, Length(PCM));
|
||||
end;
|
||||
end;
|
||||
|
||||
+4
-8
@@ -4155,6 +4155,9 @@ var
|
||||
Names: array[0..FILT_COUNT-1] of string;
|
||||
DefIdx: Integer;
|
||||
begin
|
||||
// DMR has a mandatory 12.5 kHz channel. There is nothing for the operator
|
||||
// to choose, so hide the whole filter selector and restore it on exit.
|
||||
PanelFilter.Visible := FController.FMode <> MODE_DMR;
|
||||
if FController.FMode = MODE_FM then
|
||||
begin
|
||||
// FM: show only NFM and WFM buttons, resize them to fill the row
|
||||
@@ -4236,14 +4239,7 @@ begin
|
||||
FController.FFilter := 0;
|
||||
FController.FFilterBW := FILT_DMR_BW;
|
||||
FController.FFMDeviation := DMR_FM_DEV;
|
||||
BtnFilter[0].Caption := '12.5k';
|
||||
BtnFilter[0].Left := LeftPanelButtonLeft(PanelFilter.Width, 1, 0);
|
||||
BtnFilter[0].Width := LeftPanelButtonWidth(PanelFilter.Width, 1, 0);
|
||||
BtnFilter[0].Top := 16;
|
||||
BtnFilter[0].Visible := True;
|
||||
StyleButton(BtnFilter[0], True);
|
||||
for i := 1 to FILT_COUNT - 1 do BtnFilter[i].Visible := False;
|
||||
PanelFilter.Height := BtnFilter[0].Top + BtnFilter[0].Height + 4;
|
||||
for i := 0 to FILT_COUNT - 1 do BtnFilter[i].Visible := False;
|
||||
RelayoutBelowBands;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
@@ -2423,6 +2423,8 @@ end;
|
||||
procedure TRadioController.SetSliceModeBW(Id, Mode, BW: Integer);
|
||||
var Lo, Hi: Integer;
|
||||
begin
|
||||
// DMR has one fixed RF channel width; callers select only the mode.
|
||||
if Mode = MODE_DMR then BW := FILT_DMR_BW;
|
||||
FilterEdgesFor(Mode, BW, Lo, Hi);
|
||||
SetSliceMode(Id, Mode);
|
||||
SetSliceFilter(Id, Lo, Hi);
|
||||
|
||||
+7
-3
@@ -418,7 +418,7 @@ begin
|
||||
case ModeIdx of
|
||||
5: Result := 2; // FM: NFM/FM
|
||||
10: Result := 3; // WFM: 200K/160K/120K
|
||||
11: Result := 1; // DMR: fixed 12.5 kHz channel
|
||||
11: Result := 0; // DMR: fixed 12.5 kHz, no redundant selector
|
||||
else Result := 6;
|
||||
end;
|
||||
end;
|
||||
@@ -579,7 +579,10 @@ end;
|
||||
function TVfoOverlay.PanelContentH: Integer;
|
||||
begin
|
||||
case FPanel of
|
||||
fpMode: Result := OVL_MODE_ROWS*(ROW_H + IGAP) + ROW_H; // ряды режимов + ряд фильтров
|
||||
fpMode: begin
|
||||
Result := OVL_MODE_ROWS*(ROW_H + IGAP);
|
||||
if GetFilterCountForMode(FMode) > 0 then Inc(Result, ROW_H);
|
||||
end;
|
||||
fpDsp: begin
|
||||
Result := ROW_H + IGAP + ROW_H; // toggles + AGC
|
||||
if IsFM then Inc(Result, IGAP + ROW_H); // + ряд SQL (только FM)
|
||||
@@ -1221,7 +1224,8 @@ begin
|
||||
begin
|
||||
FModeFilter[FMode] := FFilterBW;
|
||||
FMode := NewMode;
|
||||
FFilterBW := FModeFilter[FMode];
|
||||
if FMode = 11 then FFilterBW := 12500
|
||||
else FFilterBW := FModeFilter[FMode];
|
||||
Height := ComputeHeight; // число фильтров могло смениться (FM=2, WFM=3)
|
||||
RequestInvalidate;
|
||||
if Assigned(FOnSelect) then FOnSelect(FMode, FFilterBW);
|
||||
|
||||
+4
-3
@@ -306,9 +306,9 @@ begin
|
||||
'function modGrp(){return q("modGrpSel").value||"ssb";}' +
|
||||
'function modTglTarget(){const grp=GRP_MAP[modGrp()];const m=cur.mode||0;return(m===grp[0])?grp[1]:grp[0];}' +
|
||||
'function updModTglLabel(){const b=q("modTglBtn");if(!b)return;const grp=GRP_MAP[modGrp()];const m=cur.mode||0;const show=(m===grp[0]||m===grp[1])?m:grp[0];b.textContent=MODE_N[show];}' +
|
||||
'function syncModGrp(m){const g=MODE_TO_GRP[m]||"ssb";const s=q("modGrpSel");if(s&&s.value!==g)s.value=g;updModTglLabel();}' +
|
||||
'q("modGrpSel").onchange=function(){const grp=GRP_MAP[modGrp()];const defaultMode=grp[0];cur.mode=defaultMode;updModTglLabel();pend("mode");ws2({cmd:"mode",mode:defaultMode});};' +
|
||||
'q("modTglBtn").onclick=function(){audioKick();const next=modTglTarget();cur.mode=next;updModTglLabel();pend("mode");ws2({cmd:"mode",mode:next});};' +
|
||||
'function syncModGrp(m){const g=MODE_TO_GRP[m]||"ssb";const s=q("modGrpSel");if(s&&s.value!==g)s.value=g;const f=q("fnpBtn");if(f)f.style.display=(m===11)?"none":"";updModTglLabel();}' +
|
||||
'q("modGrpSel").onchange=function(){const grp=GRP_MAP[modGrp()];const defaultMode=grp[0];cur.mode=defaultMode;syncModGrp(defaultMode);pend("mode");ws2({cmd:"mode",mode:defaultMode});};' +
|
||||
'q("modTglBtn").onclick=function(){audioKick();const next=modTglTarget();cur.mode=next;syncModGrp(next);pend("mode");ws2({cmd:"mode",mode:next});};' +
|
||||
'q("agcSel").onchange=function(e){pend("agc");ws2({cmd:"agc",mode:+e.target.value});};' +
|
||||
'q("attSel").onchange=function(e){pend("attn");ws2({cmd:"attn",idx:+e.target.value});};' +
|
||||
|
||||
@@ -334,6 +334,7 @@ begin
|
||||
'function closeFnp(){if(fnpOv){fnpOv.remove();fnpOv=null;}document.removeEventListener("click",fnpOutside);}' +
|
||||
'function fnpOutside(e){if(fnpOv&&!fnpOv.contains(e.target)&&e.target!==q("fnpBtn"))closeFnp();}' +
|
||||
'function showFnp(){' +
|
||||
'if((cur.mode|0)===11)return;' +
|
||||
'if(fnpOv){closeFnp();return;}' +
|
||||
'var g=fnpGrp(),ns=FNP_N[g],bws=FNP_BW[g],cb=cur.filter_bw||2700;' +
|
||||
'fnpOv=document.createElement("div");' +
|
||||
|
||||
Reference in New Issue
Block a user