mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
87 lines
2.7 KiB
ObjectPascal
87 lines
2.7 KiB
ObjectPascal
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.
|