mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feat(dmr): add diagnostic capture probes
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
program dmr_replay;
|
||||
|
||||
{$MODE Delphi}
|
||||
|
||||
uses
|
||||
{$IFDEF UNIX}cthreads,{$ENDIF}
|
||||
Classes, SysUtils, DMRDecoder;
|
||||
|
||||
type
|
||||
TBurstCounter = class
|
||||
public
|
||||
Count: QWord;
|
||||
procedure BurstReady(const Dibits: array of Byte; DibitCount: Integer);
|
||||
end;
|
||||
|
||||
procedure TBurstCounter.BurstReady(const Dibits: array of Byte;
|
||||
DibitCount: Integer);
|
||||
begin
|
||||
if DibitCount = DMR_BURST_DIBITS then Inc(Count);
|
||||
end;
|
||||
|
||||
procedure Usage;
|
||||
begin
|
||||
WriteLn('Usage: dmr_replay <capture.demod_f32le> [--invert]');
|
||||
Halt(2);
|
||||
end;
|
||||
|
||||
var
|
||||
Stream: TFileStream;
|
||||
Decoder: TDMRDecoder;
|
||||
Counter: TBurstCounter;
|
||||
Samples, Right: array[0..2047] of Single;
|
||||
Status: TDMRStatus;
|
||||
BytesRead, N, i: Integer;
|
||||
TotalSamples: QWord;
|
||||
Invert: Boolean;
|
||||
begin
|
||||
if ParamCount < 1 then Usage;
|
||||
Invert := (ParamCount >= 2) and (ParamStr(2) = '--invert');
|
||||
Stream := TFileStream.Create(ParamStr(1), fmOpenRead or fmShareDenyNone);
|
||||
Counter := TBurstCounter.Create;
|
||||
Decoder := TDMRDecoder.Create;
|
||||
try
|
||||
Decoder.OnBurst := Counter.BurstReady;
|
||||
Decoder.SetInverted(Invert);
|
||||
Decoder.SetEnabled(True);
|
||||
TotalSamples := 0;
|
||||
repeat
|
||||
BytesRead := Stream.Read(Samples[0], SizeOf(Samples));
|
||||
N := BytesRead div SizeOf(Single);
|
||||
if N <= 0 then Break;
|
||||
for i := 0 to N - 1 do Right[i] := Samples[i];
|
||||
Decoder.FeedAudio(Samples, Right, N);
|
||||
Inc(TotalSamples, N);
|
||||
// Keep the bounded realtime ring from becoming the replay bottleneck.
|
||||
Sleep(2);
|
||||
until BytesRead < SizeOf(Samples);
|
||||
Sleep(500);
|
||||
Decoder.GetStatus(Status);
|
||||
WriteLn('file=', ParamStr(1));
|
||||
WriteLn('seconds=', FormatFloat('0.000', TotalSamples / DMR_INPUT_RATE));
|
||||
WriteLn('invert=', Invert);
|
||||
WriteLn('rms=', FormatFloat('0.000000', Status.SignalRMS));
|
||||
WriteLn('sync_count=', Status.SyncCount);
|
||||
WriteLn('best_sync_distance=', Status.BestSyncDistance);
|
||||
WriteLn('best_sync_phase=', Status.BestSyncPhase);
|
||||
WriteLn('best_candidate=',
|
||||
TDMRDecoder.SyncKindName(Status.BestCandidateKind));
|
||||
WriteLn('symbol_outer_level=',
|
||||
FormatFloat('0.000000', Status.SymbolOuterLevel));
|
||||
WriteLn('dc_mean=', FormatFloat('0.000000', Status.SignalMean));
|
||||
WriteLn('last_sync=', TDMRDecoder.SyncKindName(Status.SyncKind));
|
||||
WriteLn('bursts=', Counter.Count);
|
||||
WriteLn('dropped_samples=', Status.DroppedSamples);
|
||||
if Status.CACHValid then WriteLn('timeslot=', Status.Slot + 1);
|
||||
if Status.SlotTypeValid then
|
||||
WriteLn('color_code=', Status.ColorCode, ' data_type=', Status.DataType);
|
||||
if Status.LinkControlValid then
|
||||
WriteLn('lc_slot=', Status.LCSlot + 1, ' target=', Status.TargetID,
|
||||
' source=', Status.SourceID, ' service_options=', Status.ServiceOptions);
|
||||
finally
|
||||
Decoder.Free;
|
||||
Counter.Free;
|
||||
Stream.Free;
|
||||
end;
|
||||
end.
|
||||
@@ -0,0 +1,58 @@
|
||||
program iq24be_to_cf32;
|
||||
|
||||
{$MODE Delphi}
|
||||
|
||||
uses
|
||||
Classes, SysUtils;
|
||||
|
||||
const
|
||||
PAIRS_PER_BLOCK = 8192;
|
||||
SCALE = 1.0 / 8388608.0;
|
||||
|
||||
var
|
||||
Input, Output: TFileStream;
|
||||
Raw: array[0..PAIRS_PER_BLOCK * 6 - 1] of Byte;
|
||||
IQ: array[0..PAIRS_PER_BLOCK * 2 - 1] of Single;
|
||||
InputName, OutputName: string;
|
||||
BytesRead, Pairs, i, p: Integer;
|
||||
I24, Q24: LongInt;
|
||||
TotalPairs: QWord;
|
||||
begin
|
||||
if ParamCount < 1 then
|
||||
begin
|
||||
WriteLn('Usage: iq24be_to_cf32 <capture.iq24be> [output.cf32le]');
|
||||
Halt(2);
|
||||
end;
|
||||
InputName := ParamStr(1);
|
||||
if ParamCount >= 2 then OutputName := ParamStr(2)
|
||||
else OutputName := ChangeFileExt(InputName, '.cf32le');
|
||||
Input := TFileStream.Create(InputName, fmOpenRead or fmShareDenyNone);
|
||||
Output := TFileStream.Create(OutputName, fmCreate);
|
||||
try
|
||||
TotalPairs := 0;
|
||||
repeat
|
||||
BytesRead := Input.Read(Raw[0], SizeOf(Raw));
|
||||
Pairs := BytesRead div 6;
|
||||
p := 0;
|
||||
for i := 0 to Pairs - 1 do
|
||||
begin
|
||||
I24 := (LongInt(Raw[p]) shl 16) or (LongInt(Raw[p + 1]) shl 8) or
|
||||
LongInt(Raw[p + 2]);
|
||||
Q24 := (LongInt(Raw[p + 3]) shl 16) or (LongInt(Raw[p + 4]) shl 8) or
|
||||
LongInt(Raw[p + 5]);
|
||||
if (I24 and $800000) <> 0 then I24 := I24 or LongInt($FF000000);
|
||||
if (Q24 and $800000) <> 0 then Q24 := Q24 or LongInt($FF000000);
|
||||
IQ[i * 2] := I24 * SCALE;
|
||||
IQ[i * 2 + 1] := Q24 * SCALE;
|
||||
Inc(p, 6);
|
||||
end;
|
||||
if Pairs > 0 then Output.WriteBuffer(IQ[0], Pairs * 2 * SizeOf(Single));
|
||||
Inc(TotalPairs, Pairs);
|
||||
until BytesRead < SizeOf(Raw);
|
||||
WriteLn('output=', OutputName);
|
||||
WriteLn('iq_pairs=', TotalPairs);
|
||||
finally
|
||||
Output.Free;
|
||||
Input.Free;
|
||||
end;
|
||||
end.
|
||||
Reference in New Issue
Block a user