mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#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:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user