#1351 Restores semi-accurate timestamps of sample buffers to ensure P25 & DMR decoded messages have accurate timestamps and MBE recording voice frames have accurate timestamps. (#1357)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2022-12-17 06:39:35 -05:00
committed by GitHub
parent 047dec6da1
commit bc462b9652
79 changed files with 607 additions and 733 deletions

View File

@@ -21,9 +21,8 @@ package io.github.dsheirer.sample;
import io.github.dsheirer.sample.complex.Complex;
import io.github.dsheirer.sample.complex.ComplexSamples;
import org.apache.commons.math3.util.FastMath;
import java.util.Random;
import org.apache.commons.math3.util.FastMath;
public class SampleUtils
{
@@ -33,9 +32,10 @@ public class SampleUtils
* Converts from an interleaved complex sample array to a sample record with the
* I and Q in separate arrays.
* @param samples that are interleaved complex samples
* @param timestamp of the samples
* @return deinterleaved complex samples instance
*/
public static ComplexSamples deinterleave(float[] samples)
public static ComplexSamples deinterleave(float[] samples, long timestamp)
{
float[] i = new float[samples.length / 2];
float[] q = new float[samples.length / 2];
@@ -49,7 +49,7 @@ public class SampleUtils
q[x] = samples[offset + 1];
}
return new ComplexSamples(i, q);
return new ComplexSamples(i, q, timestamp);
}
/**
@@ -94,7 +94,7 @@ public class SampleUtils
q[x] = (float)Math.sin(angle);
}
return new ComplexSamples(i, q);
return new ComplexSamples(i, q, System.currentTimeMillis());
}
/**
@@ -119,7 +119,7 @@ public class SampleUtils
q[x] *= gain;
}
return new ComplexSamples(i, q);
return new ComplexSamples(i, q, System.currentTimeMillis());
}
public static Complex multiply(Complex a, Complex b)

View File

@@ -20,11 +20,10 @@ package io.github.dsheirer.sample.adapter;
import io.github.dsheirer.buffer.FloatNativeBuffer;
import io.github.dsheirer.buffer.INativeBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Converts 16-bit/2-byte little endian byte data into a reusable buffer of complex float sample data
@@ -62,7 +61,7 @@ public class ComplexShortAdapter implements ISampleAdapter<INativeBuffer>
long now = System.currentTimeMillis();
return new FloatNativeBuffer(convertedSamples, now);
return new FloatNativeBuffer(convertedSamples, now, 192.0f);
}
/**

View File

@@ -22,7 +22,7 @@ package io.github.dsheirer.sample.complex;
/**
* Wrapper for a complex sample array where I and Q samples are in separate arrays.
*/
public record ComplexSamples(float[] i, float[] q)
public record ComplexSamples(float[] i, float[] q, long timestamp)
{
/**
* Converts this non-interleaved complex samples to interleaved.

View File

@@ -28,7 +28,6 @@ import java.util.Iterator;
public class ComplexSamplesNativeBufferAdapter implements INativeBuffer
{
private ComplexSamples mComplexSamples;
private long mTimestamp = System.currentTimeMillis();
/**
* Constructs an instance
@@ -60,7 +59,7 @@ public class ComplexSamplesNativeBufferAdapter implements INativeBuffer
@Override
public long getTimestamp()
{
return mTimestamp;
return mComplexSamples.timestamp();
}
/**

View File

@@ -1,95 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.complex;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Processes I & Q sample arrays into uniform length complex samples buffers.
*/
public class ComplexSamplesRepackager
{
private int mSize;
private float[] mIResidual = new float[0];
private float[] mQResidual = new float[0];
/**
* Constructs an instance.
* @param size or length of the output complex samples.
*/
public ComplexSamplesRepackager(int size)
{
mSize = size;
}
/**
* Combines the I & Q sample arrays with any residual leftover from the previous invocation and
* chunks the arrays up into uniform length complex sample buffers, storing any leftover samples
* as residual to use in the next processing invocation. If the length of the sample arguments
* combined with any residual is not enough to create at least one buffer, those samples will be
* added to the residual and used in the next operation, and no complex sample buffer will be
* returned.
*
* @param i samples array to repackage
* @param q samples array to repackage
* @return zero or more complex samples of uniform length.
*/
public List<ComplexSamples> process(float[] i, float[] q)
{
float[] iCombined = new float[mIResidual.length + i.length];
float[] qCombined = new float[mQResidual.length + q.length];
if(mIResidual.length > 0)
{
System.arraycopy(mIResidual, 0, iCombined, 0, mIResidual.length);
System.arraycopy(mQResidual, 0, qCombined, 0, mQResidual.length);
}
System.arraycopy(i, 0, iCombined, mIResidual.length, i.length);
System.arraycopy(q, 0, qCombined, mQResidual.length, q.length);
int processed = 0;
List<ComplexSamples> samples = new ArrayList<>();
while((processed + mSize) < iCombined.length)
{
float[] iChunk = Arrays.copyOfRange(iCombined, processed, processed + mSize);
float[] qChunk = Arrays.copyOfRange(qCombined, processed, processed + mSize);
samples.add(new ComplexSamples(iChunk, qChunk));
processed += mSize;
}
if(processed < iCombined.length)
{
mIResidual = Arrays.copyOfRange(iCombined, processed, iCombined.length);
mQResidual = Arrays.copyOfRange(qCombined, processed, qCombined.length);
}
else
{
mIResidual = new float[0];
mQResidual = new float[0];
}
return samples;
}
}

View File

@@ -41,6 +41,6 @@ public record InterleavedComplexSamples(float[] samples, long timestamp) impleme
q[x] = samples()[x / 2 + 1];
}
return new ComplexSamples(i, q);
return new ComplexSamples(i, q, timestamp());
}
}