mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -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:
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.buffer;
|
||||
|
||||
/**
|
||||
* Base native buffer class.
|
||||
*/
|
||||
public abstract class AbstractNativeBuffer implements INativeBuffer
|
||||
{
|
||||
private long mTimestamp;
|
||||
private float mSamplesPerMillisecond;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param timestamp for the start of this buffer
|
||||
* @param samplesPerMillisecond to calculate the time offset of sub-buffers extracted from this native buffer.
|
||||
*/
|
||||
public AbstractNativeBuffer(long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
mTimestamp = timestamp;
|
||||
mSamplesPerMillisecond = samplesPerMillisecond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamp for the start of this buffer
|
||||
* @return timestamp in milliseconds
|
||||
*/
|
||||
@Override
|
||||
public long getTimestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quantity of samples representing one millisecond of sample data, used for calculating fragment timestamp offsets.
|
||||
* @return samples per millisecond count.
|
||||
*/
|
||||
public float getSamplesPerMillisecond()
|
||||
{
|
||||
return mSamplesPerMillisecond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the timestamp for a samples buffer fragment based on where it is extracted from relative to the
|
||||
* native buffer starting timestamp.
|
||||
* @param samplesPointer for the start of the fragment. Note: this value will be divided by 2 to account for I/Q sample pairs.
|
||||
* @return timestamp adjusted to the fragment or sub-buffer start sample.
|
||||
*/
|
||||
protected long getFragmentTimestamp(int samplesPointer)
|
||||
{
|
||||
return getTimestamp() + (long)(samplesPointer / 2 / getSamplesPerMillisecond());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.buffer;
|
||||
|
||||
/**
|
||||
* Base class for native buffer factories.
|
||||
*/
|
||||
public abstract class AbstractNativeBufferFactory implements INativeBufferFactory
|
||||
{
|
||||
private float mSamplesPerMillisecond = 0.0f;
|
||||
|
||||
@Override
|
||||
public void setSamplesPerMillisecond(float samplesPerMillisecond)
|
||||
{
|
||||
mSamplesPerMillisecond = samplesPerMillisecond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quantity of I/Q sample pairs per milli-second at the current sample rate to use in calculating an accurate
|
||||
* timestamp for sub-buffer that are generated from the native buffer.
|
||||
* @return samples per millisecond.
|
||||
*/
|
||||
public float getSamplesPerMillisecond()
|
||||
{
|
||||
return mSamplesPerMillisecond;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import java.util.Iterator;
|
||||
* Native buffer sample array wrapper class that provides access to a stream of either interleaved or
|
||||
* non-interleaved complex sample buffers converted from the raw byte sample array.
|
||||
*/
|
||||
public class ByteNativeBuffer implements INativeBuffer
|
||||
public class ByteNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
private static final int FRAGMENT_SIZE = 2048;
|
||||
private final static float[] LOOKUP_VALUES;
|
||||
@@ -46,16 +46,17 @@ public class ByteNativeBuffer implements INativeBuffer
|
||||
}
|
||||
|
||||
private byte[] mSamples;
|
||||
private long mTimestamp;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param samples to process
|
||||
* @param timestamp of the samples
|
||||
* @param averageDc measured from sample stream
|
||||
* @param samplesPerMillisecond to calculate derivative timestamps for sub-buffers.
|
||||
*/
|
||||
public ByteNativeBuffer(byte[] samples, long timestamp, float averageDc)
|
||||
public ByteNativeBuffer(byte[] samples, long timestamp, float averageDc, float samplesPerMillisecond)
|
||||
{
|
||||
super(timestamp, samplesPerMillisecond);
|
||||
//Ensure we're an even multiple of the fragment size. Typically, this will be 64k or 128k
|
||||
if(samples.length % FRAGMENT_SIZE != 0)
|
||||
{
|
||||
@@ -63,7 +64,6 @@ public class ByteNativeBuffer implements INativeBuffer
|
||||
}
|
||||
|
||||
mSamples = samples;
|
||||
mTimestamp = timestamp;
|
||||
mAverageDc = averageDc;
|
||||
}
|
||||
|
||||
@@ -73,12 +73,6 @@ public class ByteNativeBuffer implements INativeBuffer
|
||||
return mSamples.length / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ComplexSamples> iterator()
|
||||
{
|
||||
@@ -104,6 +98,8 @@ public class ByteNativeBuffer implements INativeBuffer
|
||||
@Override
|
||||
public ComplexSamples next()
|
||||
{
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
|
||||
float[] i = new float[FRAGMENT_SIZE];
|
||||
float[] q = new float[FRAGMENT_SIZE];
|
||||
int samplesOffset = mSamplesPointer;
|
||||
@@ -115,8 +111,7 @@ public class ByteNativeBuffer implements INativeBuffer
|
||||
}
|
||||
|
||||
mSamplesPointer = samplesOffset;
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +131,8 @@ public class ByteNativeBuffer implements INativeBuffer
|
||||
@Override
|
||||
public InterleavedComplexSamples next()
|
||||
{
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
|
||||
float[] converted = new float[FRAGMENT_SIZE * 2];
|
||||
|
||||
int samplesPointer = mSamplesPointer;
|
||||
@@ -147,7 +144,7 @@ public class ByteNativeBuffer implements INativeBuffer
|
||||
|
||||
mSamplesPointer = samplesPointer;
|
||||
|
||||
return new InterleavedComplexSamples(converted, mTimestamp);
|
||||
return new InterleavedComplexSamples(converted, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.nio.ByteBuffer;
|
||||
/**
|
||||
* Implements a factory for creating ByteNativeBuffer instances
|
||||
*/
|
||||
public class ByteNativeBufferFactory implements INativeBufferFactory
|
||||
public class ByteNativeBufferFactory extends AbstractNativeBufferFactory
|
||||
{
|
||||
private DcCorrectionManager mDcCorrectionManager = new DcCorrectionManager();
|
||||
|
||||
@@ -39,7 +39,7 @@ public class ByteNativeBufferFactory implements INativeBufferFactory
|
||||
calculateDc(copy);
|
||||
}
|
||||
|
||||
return new ByteNativeBuffer(copy, timestamp, mDcCorrectionManager.getAverageDc());
|
||||
return new ByteNativeBuffer(copy, timestamp, mDcCorrectionManager.getAverageDc(), getSamplesPerMillisecond());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,35 +22,24 @@ package io.github.dsheirer.buffer;
|
||||
import io.github.dsheirer.sample.SampleUtils;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Simple native buffer implementation that simply wraps a single, existing, non-native sample buffer.
|
||||
*/
|
||||
public class FloatNativeBuffer implements INativeBuffer
|
||||
public class FloatNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
private float[] mInterleavedComplexSamples;
|
||||
private long mTimestamp;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param complexSamples to wrap
|
||||
* @param complexSamples interleaved to wrap
|
||||
* @param timestamp for the buffer
|
||||
*/
|
||||
public FloatNativeBuffer(float[] complexSamples, long timestamp)
|
||||
public FloatNativeBuffer(float[] complexSamples, long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(timestamp, samplesPerMillisecond);
|
||||
mInterleavedComplexSamples = complexSamples;
|
||||
mTimestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param complexSamples to wrap
|
||||
*/
|
||||
public FloatNativeBuffer(float[] complexSamples)
|
||||
{
|
||||
this(complexSamples, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,9 +47,9 @@ public class FloatNativeBuffer implements INativeBuffer
|
||||
* @param samples to wrap
|
||||
* @param timestamp for the buffer
|
||||
*/
|
||||
public FloatNativeBuffer(ComplexSamples samples, long timestamp)
|
||||
public FloatNativeBuffer(ComplexSamples samples)
|
||||
{
|
||||
this(SampleUtils.interleave(samples), timestamp);
|
||||
this(SampleUtils.interleave(samples), samples.timestamp(), 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +58,7 @@ public class FloatNativeBuffer implements INativeBuffer
|
||||
*/
|
||||
public FloatNativeBuffer(InterleavedComplexSamples samples)
|
||||
{
|
||||
this(samples.samples(), samples.timestamp());
|
||||
this(samples.samples(), samples.timestamp(), 0.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,12 +79,6 @@ public class FloatNativeBuffer implements INativeBuffer
|
||||
return mInterleavedComplexSamples.length / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
private class ComplexSamplesIterator implements Iterator<ComplexSamples>
|
||||
{
|
||||
private boolean mEmpty;
|
||||
@@ -115,7 +98,7 @@ public class FloatNativeBuffer implements INativeBuffer
|
||||
}
|
||||
|
||||
mEmpty = true;
|
||||
return SampleUtils.deinterleave(mInterleavedComplexSamples);
|
||||
return SampleUtils.deinterleave(mInterleavedComplexSamples, getTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +122,7 @@ public class FloatNativeBuffer implements INativeBuffer
|
||||
}
|
||||
|
||||
mEmpty = true;
|
||||
return new InterleavedComplexSamples(mInterleavedComplexSamples, mTimestamp);
|
||||
return new InterleavedComplexSamples(mInterleavedComplexSamples, getTimestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,15 @@ public interface INativeBufferFactory
|
||||
*
|
||||
* @param samples byte array copied from native memory
|
||||
* @param timestamp of the samples
|
||||
* @param samplesPerMillisecond to calculate timestamp offset for child buffers.
|
||||
* @return instance
|
||||
*/
|
||||
INativeBuffer getBuffer(ByteBuffer samples, long timestamp);
|
||||
|
||||
/**
|
||||
* Sets the samples per millisecond rate based on the current sample rate.
|
||||
*
|
||||
* @param samplesPerMillisecond to calculate timestamp offset for child buffers.
|
||||
*/
|
||||
void setSamplesPerMillisecond(float samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@@ -21,14 +21,13 @@ package io.github.dsheirer.buffer;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Native buffer sample array wrapper class that provides access to a stream of either interleaved or
|
||||
* non-interleaved complex sample buffers converted from the raw byte sample array.
|
||||
*/
|
||||
public class SignedByteNativeBuffer implements INativeBuffer
|
||||
public class SignedByteNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
private static final int FRAGMENT_SIZE = 2048;
|
||||
private final static float[] LOOKUP_VALUES;
|
||||
@@ -47,7 +46,6 @@ public class SignedByteNativeBuffer implements INativeBuffer
|
||||
}
|
||||
|
||||
private byte[] mSamples;
|
||||
private long mTimestamp;
|
||||
private float mIAverageDc;
|
||||
private float mQAverageDc;
|
||||
|
||||
@@ -57,9 +55,12 @@ public class SignedByteNativeBuffer implements INativeBuffer
|
||||
* @param timestamp of the samples
|
||||
* @param iAverageDc of the sample stream
|
||||
* @param qAverageDc of the sample stream
|
||||
* @param samplesPerMillisecond to calculate sub-buffer timestamps
|
||||
*/
|
||||
public SignedByteNativeBuffer(byte[] samples, long timestamp, float iAverageDc, float qAverageDc)
|
||||
public SignedByteNativeBuffer(byte[] samples, long timestamp, float iAverageDc, float qAverageDc, float samplesPerMillisecond)
|
||||
{
|
||||
super(timestamp, samplesPerMillisecond);
|
||||
|
||||
//Ensure we're an even multiple of the fragment size. Typically, this will be 64k or 128k
|
||||
if(samples.length % FRAGMENT_SIZE != 0)
|
||||
{
|
||||
@@ -67,17 +68,10 @@ public class SignedByteNativeBuffer implements INativeBuffer
|
||||
}
|
||||
|
||||
mSamples = samples;
|
||||
mTimestamp = timestamp;
|
||||
mIAverageDc = iAverageDc;
|
||||
mQAverageDc = qAverageDc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sampleCount()
|
||||
{
|
||||
@@ -112,6 +106,8 @@ public class SignedByteNativeBuffer implements INativeBuffer
|
||||
@Override
|
||||
public ComplexSamples next()
|
||||
{
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
|
||||
float[] i = new float[FRAGMENT_SIZE];
|
||||
float[] q = new float[FRAGMENT_SIZE];
|
||||
int samplesOffset = mSamplesPointer;
|
||||
@@ -123,7 +119,7 @@ public class SignedByteNativeBuffer implements INativeBuffer
|
||||
}
|
||||
|
||||
mSamplesPointer = samplesOffset;
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +139,8 @@ public class SignedByteNativeBuffer implements INativeBuffer
|
||||
@Override
|
||||
public InterleavedComplexSamples next()
|
||||
{
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
|
||||
float[] converted = new float[FRAGMENT_SIZE * 2];
|
||||
|
||||
int samplesOffset = mSamplesPointer;
|
||||
@@ -155,7 +153,7 @@ public class SignedByteNativeBuffer implements INativeBuffer
|
||||
|
||||
mSamplesPointer = samplesOffset;
|
||||
|
||||
return new InterleavedComplexSamples(converted, mTimestamp);
|
||||
return new InterleavedComplexSamples(converted, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import java.util.concurrent.TimeUnit;
|
||||
/**
|
||||
* Implements a factory for creating SignedByteNativeBuffer instances
|
||||
*/
|
||||
public class SignedByteNativeBufferFactory implements INativeBufferFactory
|
||||
public class SignedByteNativeBufferFactory extends AbstractNativeBufferFactory
|
||||
{
|
||||
/**
|
||||
* DC removal calculations will run once a minute
|
||||
@@ -78,7 +78,7 @@ public class SignedByteNativeBufferFactory implements INativeBufferFactory
|
||||
calculateDc(copy);
|
||||
}
|
||||
|
||||
return new SignedByteNativeBuffer(copy, timestamp, mIAverageDc, mQAverageDc);
|
||||
return new SignedByteNativeBuffer(copy, timestamp, mIAverageDc, mQAverageDc, getSamplesPerMillisecond());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.hilbert.HilbertTransform;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
@@ -42,8 +41,9 @@ public abstract class AirspyBufferIterator<T> implements Iterator<T>
|
||||
protected float[] mQBuffer = new float[FRAGMENT_SIZE + Q_OVERLAP];
|
||||
protected short[] mSamples;
|
||||
protected int mSamplesPointer = 0;
|
||||
protected long mTimestamp;
|
||||
protected float mAverageDc;
|
||||
private long mTimestamp;
|
||||
private float mSamplesPerMillisecond;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
@@ -52,8 +52,10 @@ public abstract class AirspyBufferIterator<T> implements Iterator<T>
|
||||
* @param residualQ samples from previous buffer
|
||||
* @param averageDc as measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyBufferIterator(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp)
|
||||
public AirspyBufferIterator(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp,
|
||||
float samplesPerMillisecond)
|
||||
{
|
||||
if(residualI.length != I_OVERLAP || residualQ.length != Q_OVERLAP)
|
||||
{
|
||||
@@ -73,6 +75,7 @@ public abstract class AirspyBufferIterator<T> implements Iterator<T>
|
||||
mAverageDc = averageDc;
|
||||
mSamples = samples;
|
||||
mTimestamp = timestamp;
|
||||
mSamplesPerMillisecond = samplesPerMillisecond;
|
||||
|
||||
//Transfer and scale the residual I & Q samples from the previous buffer
|
||||
for(int i = 0; i < residualI.length; i++)
|
||||
@@ -86,6 +89,17 @@ public abstract class AirspyBufferIterator<T> implements Iterator<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the timestamp for a samples buffer fragment based on where it is extracted from relative to the
|
||||
* native buffer starting timestamp.
|
||||
* @param samplesPointer for the start of the fragment. Note: this value will be divided by 2 to account for I/Q sample pairs.
|
||||
* @return timestamp adjusted to the fragment or sub-buffer start sample.
|
||||
*/
|
||||
protected long getFragmentTimestamp(int samplesPointer)
|
||||
{
|
||||
return mTimestamp + (long)(samplesPointer / 2 / mSamplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -36,10 +35,11 @@ public class AirspyBufferIteratorScalar extends AirspyBufferIterator<ComplexSamp
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp)
|
||||
public AirspyBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp);
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,6 +50,8 @@ public class AirspyBufferIteratorScalar extends AirspyBufferIterator<ComplexSamp
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
|
||||
int offset = mSamplesPointer;
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
@@ -91,6 +93,6 @@ public class AirspyBufferIteratorScalar extends AirspyBufferIterator<ComplexSamp
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,11 @@
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
*/
|
||||
@@ -41,10 +40,12 @@ public class AirspyBufferIteratorVector128Bits extends AirspyBufferIterator<Comp
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp)
|
||||
public AirspyBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp);
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,6 +56,7 @@ public class AirspyBufferIteratorVector128Bits extends AirspyBufferIterator<Comp
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
@@ -121,6 +123,6 @@ public class AirspyBufferIteratorVector128Bits extends AirspyBufferIterator<Comp
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,11 @@
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
*/
|
||||
@@ -41,10 +40,12 @@ public class AirspyBufferIteratorVector256Bits extends AirspyBufferIterator<Comp
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp)
|
||||
public AirspyBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp);
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,6 +56,7 @@ public class AirspyBufferIteratorVector256Bits extends AirspyBufferIterator<Comp
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
@@ -115,6 +117,6 @@ public class AirspyBufferIteratorVector256Bits extends AirspyBufferIterator<Comp
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,11 @@
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
*/
|
||||
@@ -42,10 +41,12 @@ public class AirspyBufferIteratorVector512Bits extends AirspyBufferIterator<Comp
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp)
|
||||
public AirspyBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp);
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
//Arrange the last 8 taps of the filter into the higher indices of an array to align for SIMD
|
||||
mFilterPart2 = new float[16];
|
||||
System.arraycopy(COEFFICIENTS, 16, mFilterPart2, 8, 8);
|
||||
@@ -59,6 +60,8 @@ public class AirspyBufferIteratorVector512Bits extends AirspyBufferIterator<Comp
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
@@ -117,6 +120,6 @@ public class AirspyBufferIteratorVector512Bits extends AirspyBufferIterator<Comp
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,11 @@
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
*/
|
||||
@@ -41,10 +40,12 @@ public class AirspyBufferIteratorVector64Bits extends AirspyBufferIterator<Compl
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp)
|
||||
public AirspyBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp);
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,6 +56,8 @@ public class AirspyBufferIteratorVector64Bits extends AirspyBufferIterator<Compl
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
@@ -133,6 +136,6 @@ public class AirspyBufferIteratorVector64Bits extends AirspyBufferIterator<Compl
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,12 @@ public class AirspyInterleavedBufferIteratorScalar extends AirspyBufferIterator<
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyInterleavedBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp)
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp);
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,6 +50,7 @@ public class AirspyInterleavedBufferIteratorScalar extends AirspyBufferIterator<
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
@@ -89,6 +91,6 @@ public class AirspyInterleavedBufferIteratorScalar extends AirspyBufferIterator<
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new InterleavedComplexSamples(samples, mTimestamp);
|
||||
return new InterleavedComplexSamples(samples, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,11 +39,12 @@ public class AirspyInterleavedBufferIteratorVector128Bits extends AirspyBufferIt
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyInterleavedBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp)
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp);
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,6 +55,7 @@ public class AirspyInterleavedBufferIteratorVector128Bits extends AirspyBufferIt
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
@@ -118,6 +120,6 @@ public class AirspyInterleavedBufferIteratorVector128Bits extends AirspyBufferIt
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new InterleavedComplexSamples(samples, mTimestamp);
|
||||
return new InterleavedComplexSamples(samples, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,11 +39,12 @@ public class AirspyInterleavedBufferIteratorVector256Bits extends AirspyBufferIt
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyInterleavedBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp)
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp);
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,6 +55,7 @@ public class AirspyInterleavedBufferIteratorVector256Bits extends AirspyBufferIt
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
@@ -112,6 +114,6 @@ public class AirspyInterleavedBufferIteratorVector256Bits extends AirspyBufferIt
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new InterleavedComplexSamples(samples, mTimestamp);
|
||||
return new InterleavedComplexSamples(samples, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,11 +40,12 @@ public class AirspyInterleavedBufferIteratorVector512Bits extends AirspyBufferIt
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyInterleavedBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp)
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp);
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
|
||||
//Arrange the last 8 taps of the filter into the higher indices of an array to align for SIMD
|
||||
mFilterPart2 = new float[16];
|
||||
@@ -59,6 +60,7 @@ public class AirspyInterleavedBufferIteratorVector512Bits extends AirspyBufferIt
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
@@ -115,6 +117,6 @@ public class AirspyInterleavedBufferIteratorVector512Bits extends AirspyBufferIt
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new InterleavedComplexSamples(samples, mTimestamp);
|
||||
return new InterleavedComplexSamples(samples, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,11 +39,12 @@ public class AirspyInterleavedBufferIteratorVector64Bits extends AirspyBufferIte
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public AirspyInterleavedBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp)
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp);
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,6 +55,7 @@ public class AirspyInterleavedBufferIteratorVector64Bits extends AirspyBufferIte
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
@@ -130,6 +132,6 @@ public class AirspyInterleavedBufferIteratorVector64Bits extends AirspyBufferIte
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new InterleavedComplexSamples(samples, mTimestamp);
|
||||
return new InterleavedComplexSamples(samples, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,23 +19,21 @@
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.buffer.INativeBuffer;
|
||||
import io.github.dsheirer.buffer.AbstractNativeBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Native buffer scalar implementation for Airspy non-packed samples.
|
||||
*/
|
||||
public class AirspyNativeBuffer implements INativeBuffer
|
||||
public class AirspyNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
private short[] mSamples;
|
||||
private short[] mResidualI;
|
||||
private short[] mResidualQ;
|
||||
private float mAverageDc;
|
||||
private long mTimestamp;
|
||||
private Implementation mInterleavedImplementation;
|
||||
private Implementation mNonInterleavedImplementation;
|
||||
|
||||
@@ -48,11 +46,14 @@ public class AirspyNativeBuffer implements INativeBuffer
|
||||
* @param timestamp of the buffer
|
||||
* @param interleavedImplementation optimal, scalar vs vector SIMD
|
||||
* @param nonInterleavedImplementation optimal, scalar vs vector SIMD
|
||||
* @param samplesPerMillisecond used to calculate sub-buffer fragment timestamp offsets from the start of this buffer.
|
||||
*/
|
||||
public AirspyNativeBuffer(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, Implementation interleavedImplementation,
|
||||
Implementation nonInterleavedImplementation)
|
||||
Implementation nonInterleavedImplementation, float samplesPerMillisecond)
|
||||
{
|
||||
super(timestamp, samplesPerMillisecond);
|
||||
|
||||
//Ensure we're an even multiple of the fragment size. Typically, this will be 64k or 128k
|
||||
if(samples.length % AirspyBufferIterator.FRAGMENT_SIZE != 0)
|
||||
{
|
||||
@@ -64,7 +65,6 @@ public class AirspyNativeBuffer implements INativeBuffer
|
||||
mResidualI = residualI;
|
||||
mResidualQ = residualQ;
|
||||
mAverageDc = averageDc;
|
||||
mTimestamp = timestamp;
|
||||
mInterleavedImplementation = interleavedImplementation;
|
||||
mNonInterleavedImplementation = nonInterleavedImplementation;
|
||||
}
|
||||
@@ -75,22 +75,16 @@ public class AirspyNativeBuffer implements INativeBuffer
|
||||
return mSamples.length / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ComplexSamples> iterator()
|
||||
{
|
||||
return switch(mInterleavedImplementation)
|
||||
{
|
||||
case VECTOR_SIMD_512 -> new AirspyBufferIteratorVector512Bits(mSamples, mResidualI, mResidualQ, mAverageDc, mTimestamp);
|
||||
case VECTOR_SIMD_256-> new AirspyBufferIteratorVector256Bits(mSamples, mResidualI, mResidualQ, mAverageDc, mTimestamp);
|
||||
case VECTOR_SIMD_128-> new AirspyBufferIteratorVector128Bits(mSamples, mResidualI, mResidualQ, mAverageDc, mTimestamp);
|
||||
case VECTOR_SIMD_64 ->new AirspyBufferIteratorVector64Bits(mSamples, mResidualI, mResidualQ, mAverageDc, mTimestamp);
|
||||
default -> new AirspyBufferIteratorScalar(mSamples, mResidualI, mResidualQ, mAverageDc, mTimestamp);
|
||||
case VECTOR_SIMD_512 -> new AirspyBufferIteratorVector512Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_256-> new AirspyBufferIteratorVector256Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_128-> new AirspyBufferIteratorVector128Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_64 ->new AirspyBufferIteratorVector64Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
default -> new AirspyBufferIteratorScalar(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -99,11 +93,11 @@ public class AirspyNativeBuffer implements INativeBuffer
|
||||
{
|
||||
return switch(mInterleavedImplementation)
|
||||
{
|
||||
case VECTOR_SIMD_512 -> new AirspyInterleavedBufferIteratorVector512Bits(mSamples, mResidualI, mResidualQ, mAverageDc, mTimestamp);
|
||||
case VECTOR_SIMD_256-> new AirspyInterleavedBufferIteratorVector256Bits(mSamples, mResidualI, mResidualQ, mAverageDc, mTimestamp);
|
||||
case VECTOR_SIMD_128-> new AirspyInterleavedBufferIteratorVector128Bits(mSamples, mResidualI, mResidualQ, mAverageDc, mTimestamp);
|
||||
case VECTOR_SIMD_64 ->new AirspyInterleavedBufferIteratorVector64Bits(mSamples, mResidualI, mResidualQ, mAverageDc, mTimestamp);
|
||||
default -> new AirspyInterleavedBufferIteratorScalar(mSamples, mResidualI, mResidualQ, mAverageDc, mTimestamp);
|
||||
case VECTOR_SIMD_512 -> new AirspyInterleavedBufferIteratorVector512Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_256-> new AirspyInterleavedBufferIteratorVector256Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_128-> new AirspyInterleavedBufferIteratorVector128Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_64 ->new AirspyInterleavedBufferIteratorVector64Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
default -> new AirspyInterleavedBufferIteratorScalar(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -19,19 +19,18 @@
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.buffer.AbstractNativeBufferFactory;
|
||||
import io.github.dsheirer.buffer.INativeBuffer;
|
||||
import io.github.dsheirer.buffer.INativeBufferFactory;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationManager;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationType;
|
||||
import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Implements a factory for creating SignedByteNativeBuffer instances
|
||||
*/
|
||||
public class AirspyNativeBufferFactory implements INativeBufferFactory
|
||||
public class AirspyNativeBufferFactory extends AbstractNativeBufferFactory
|
||||
{
|
||||
private boolean mSamplePacking = false;
|
||||
private short[] mResidualI = new short[AirspyBufferIterator.I_OVERLAP];
|
||||
@@ -106,7 +105,7 @@ public class AirspyNativeBufferFactory implements INativeBufferFactory
|
||||
INativeBuffer nativeBuffer = new AirspyNativeBuffer(samples,
|
||||
Arrays.copyOf(mResidualI, mResidualI.length),
|
||||
Arrays.copyOf(mResidualQ, mResidualQ.length), mConverter.getAverageDc(), timestamp,
|
||||
mInterleavedIteratorImplementation, mNonInterleavedIteratorImplementation);
|
||||
mInterleavedIteratorImplementation, mNonInterleavedIteratorImplementation, getSamplesPerMillisecond());
|
||||
|
||||
extractResidual(samples);
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
private double mChannelSampleRate;
|
||||
private long mIndexCenterFrequency;
|
||||
private long mChannelFrequencyCorrection;
|
||||
private long mCurrentSamplesTimestamp;
|
||||
private ReentrantLock mOutputProcessorLock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
@@ -106,6 +107,7 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
public void receive(ComplexSamples complexSamples)
|
||||
{
|
||||
mStreamHeartbeatProcessor.receive(complexSamples);
|
||||
mCurrentSamplesTimestamp = complexSamples.timestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,7 +224,7 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
|
||||
{
|
||||
if(mPolyphaseChannelOutputProcessor != null)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor.receiveChannelResults(channelResultsList);
|
||||
mPolyphaseChannelOutputProcessor.receiveChannelResults(channelResultsList, mCurrentSamplesTimestamp);
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -156,7 +156,7 @@ public class TwoChannelSynthesizerM2
|
||||
mTopBlockFlag = !mTopBlockFlag;
|
||||
}
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, channelBuffer1.timestamp());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP
|
||||
private Dispatcher<List<float[]>> mChannelResultsDispatcher;
|
||||
protected Listener<ComplexSamples> mComplexSamplesListener;
|
||||
private int mInputChannelCount;
|
||||
private long mCurrentSampleTimestamp = System.currentTimeMillis();
|
||||
|
||||
/**
|
||||
* Base class for polyphase channelizer output channel processing. Provides built-in frequency translation
|
||||
@@ -49,6 +50,15 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP
|
||||
mChannelResultsDispatcher.setListener(floats -> process(floats));
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamp for the current series of samples.
|
||||
* @return time in milliseconds to use with assembled complex sample buffers.
|
||||
*/
|
||||
protected long getCurrentSampleTimestamp()
|
||||
{
|
||||
return mCurrentSampleTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
@@ -81,9 +91,10 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveChannelResults(List<float[]> channelResultsList)
|
||||
public void receiveChannelResults(List<float[]> channelResultsList, long timestamp)
|
||||
{
|
||||
mChannelResultsDispatcher.receive(channelResultsList);
|
||||
mCurrentSampleTimestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,8 +36,9 @@ public interface IPolyphaseChannelOutputProcessor
|
||||
/**
|
||||
* Receive and enqueue output results from the polyphase analysis channelizer
|
||||
* @param channelResults to enqueue
|
||||
* @param timestamp for the first channel results buffer
|
||||
*/
|
||||
void receiveChannelResults(List<float[]> channelResults);
|
||||
void receiveChannelResults(List<float[]> channelResults, long timestamp);
|
||||
|
||||
/**
|
||||
* Listener to receive assembled complex samples buffers
|
||||
|
||||
@@ -65,7 +65,8 @@ public abstract class MixerAssembler
|
||||
|
||||
/**
|
||||
* Access the fully assembled buffer when hasBuffer() indicates that a buffer is ready.
|
||||
* @param timestamp to use for the assembled complex samples buffer
|
||||
* @return true if a full buffer is assembled and ready.
|
||||
*/
|
||||
public abstract ComplexSamples getBuffer();
|
||||
public abstract ComplexSamples getBuffer(long timestamp);
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ public class OneChannelMixerAssembler extends MixerAssembler
|
||||
* @return assembled buffer.
|
||||
*/
|
||||
@Override
|
||||
public ComplexSamples getBuffer()
|
||||
public ComplexSamples getBuffer(long timestamp)
|
||||
{
|
||||
ComplexSamples buffer = mSampleAssembler.getBufferAndReset();
|
||||
ComplexSamples buffer = mSampleAssembler.getBufferAndReset(timestamp);
|
||||
|
||||
if(getMixer().hasFrequency())
|
||||
{
|
||||
|
||||
@@ -90,7 +90,7 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor
|
||||
|
||||
if(mMixerAssembler.hasBuffer())
|
||||
{
|
||||
ComplexSamples buffer = mMixerAssembler.getBuffer();
|
||||
ComplexSamples buffer = mMixerAssembler.getBuffer(getCurrentSampleTimestamp());
|
||||
|
||||
if(mComplexSamplesListener != null)
|
||||
{
|
||||
|
||||
@@ -76,9 +76,9 @@ public class SampleAssembler
|
||||
/**
|
||||
* Extracts the full buffer from this assembler and resets the assembler to accept more samples.
|
||||
*/
|
||||
public ComplexSamples getBufferAndReset()
|
||||
public ComplexSamples getBufferAndReset(long timestamp)
|
||||
{
|
||||
ComplexSamples buffer = new ComplexSamples(mI, mQ);
|
||||
ComplexSamples buffer = new ComplexSamples(mI, mQ, timestamp);
|
||||
reset();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -76,13 +76,14 @@ public class TwoChannelMixerAssembler extends MixerAssembler
|
||||
|
||||
/**
|
||||
* Access the assembled buffer. Frequency offset and gain are applied to the returned buffer.
|
||||
* @param timestamp to use for the assembled buffer
|
||||
* @return assembled buffer.
|
||||
*/
|
||||
@Override
|
||||
public ComplexSamples getBuffer()
|
||||
public ComplexSamples getBuffer(long timestamp)
|
||||
{
|
||||
ComplexSamples channel1 = mChannel1SampleAssembler.getBufferAndReset();
|
||||
ComplexSamples channel2 = mChannel2SampleAssembler.getBufferAndReset();
|
||||
ComplexSamples channel1 = mChannel1SampleAssembler.getBufferAndReset(timestamp);
|
||||
ComplexSamples channel2 = mChannel2SampleAssembler.getBufferAndReset(timestamp);
|
||||
|
||||
//Synthesize two channels into one, reducing sample rate by 2 so it's same as single channel rate
|
||||
ComplexSamples buffer = mTwoChannelSynthesizer.process(channel1, channel2);
|
||||
|
||||
@@ -90,7 +90,7 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
|
||||
* Extract the channel from the channel results array, apply frequency translation, and deliver the
|
||||
* extracted frequency-corrected channel I/Q sample set to the complex sample listener.
|
||||
*
|
||||
* @param lists to process containing a list of a list of an array of channel I/Q sample pairs (I0,Q0,I1,Q1...In,Qn)
|
||||
* @param channelResultsList to process containing a list of a list of an array of channel I/Q sample pairs (I0,Q0,I1,Q1...In,Qn)
|
||||
*/
|
||||
@Override
|
||||
public void process(List<float[]> channelResultsList)
|
||||
@@ -102,7 +102,7 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
|
||||
|
||||
if(mMixerAssembler.hasBuffer())
|
||||
{
|
||||
ComplexSamples buffer = mMixerAssembler.getBuffer();
|
||||
ComplexSamples buffer = mMixerAssembler.getBuffer(getCurrentSampleTimestamp());
|
||||
|
||||
if(mComplexSamplesListener != null)
|
||||
{
|
||||
|
||||
@@ -80,9 +80,10 @@ public abstract class HilbertTransform
|
||||
/**
|
||||
* Converts the real sample array to complex samples as half the sample rate
|
||||
* @param samples to convert
|
||||
* @param timestamp of the first sample
|
||||
* @return converted samples
|
||||
*/
|
||||
public abstract ComplexSamples filter(float[] samples);
|
||||
public abstract ComplexSamples filter(float[] samples, long timestamp);
|
||||
|
||||
/**
|
||||
* Converts the half-band filter coefficients for use as hilbert transform filter coefficients. Sets all
|
||||
|
||||
@@ -1,248 +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.dsp.filter.hilbert;
|
||||
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class LegacyHilbertTransform
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(LegacyHilbertTransform.class);
|
||||
|
||||
private float[] mHilbertFilter;
|
||||
private float[] mBuffer;
|
||||
private int mBufferSize;
|
||||
private int mBufferPointer;
|
||||
private int[][] mIndexMap;
|
||||
private int mMapHeight;
|
||||
private int mCenterTapIndex;
|
||||
private float mCenterCoefficent;
|
||||
|
||||
private boolean mInvertFlag = false;
|
||||
|
||||
/**
|
||||
* Hilbert transform filter used for converting real-valued samples into
|
||||
* complex valued samples using frequency translation (FS/4) and a half-band
|
||||
* filter.
|
||||
*
|
||||
* This filter uses a circular sample buffer and a pre-calculated
|
||||
* index map to correctly map the filter coefficients to the circular delay
|
||||
* buffer contents as each new sample is added to the buffer and processed.
|
||||
*
|
||||
* Half-band filter coefficients used in this filter must be of length N
|
||||
* where (N + 1) is a multiple of 4. This filter uses a pre-defined half
|
||||
* band filter with 47 coefficients.
|
||||
*
|
||||
* This transform process is described in Understanding Digital Signal
|
||||
* Processing, Lyons, 3e, 2011, sections 13.1.2 and 13.1.3 (p 674-678) and
|
||||
* implemented as described in Section 13.37.1 and 13.37.2 (p 802-804)
|
||||
*/
|
||||
public LegacyHilbertTransform()
|
||||
{
|
||||
convertHalfBandToHilbert(HilbertTransform.HALF_BAND_FILTER_47_TAP);
|
||||
|
||||
mBufferSize = mHilbertFilter.length + 1;
|
||||
mBuffer = new float[mBufferSize];
|
||||
|
||||
mCenterCoefficent = mHilbertFilter[mHilbertFilter.length / 2];
|
||||
|
||||
generateIndexMap(mHilbertFilter.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the continuous real-sample stream by treating the stream as a
|
||||
* set of interleaved values and applying two hilbert filters against each
|
||||
* interleaved set (I & Q). Frequency translation has already been
|
||||
* pre-applied to the hilbert filter coefficients.
|
||||
*
|
||||
* Filtering of the inphase component is a simple multiply by the center
|
||||
* coefficient (0.5) since all of the other coefficients are zero-valued.
|
||||
*
|
||||
* Filtering of the quadrature component uses a folded FIR structure and
|
||||
* expects the hilbert coefficients to be symmetric with all negative valued
|
||||
* coefficients below the center coefficient and all positive-valued coefficients
|
||||
* above the center coefficient. So, convolution only applies the filter
|
||||
* coefficient against the difference of the two samples.
|
||||
*
|
||||
* ( coefficient * value1 ) + (-coefficient * value2 ) = coefficient * ( value1 - value2 )
|
||||
*
|
||||
* Performs frequency translation by FS/2 on the final filtered values by
|
||||
* applying a sequence of 1,-1 (sign change) to each I/Q output sample.
|
||||
*/
|
||||
public float[] filter(float[] samples)
|
||||
{
|
||||
float accumulator;
|
||||
|
||||
for(int y = 0; y < samples.length; y += 2)
|
||||
{
|
||||
insert(samples[y]);
|
||||
insert(samples[y + 1]);
|
||||
|
||||
accumulator = 0.0f;
|
||||
|
||||
int index = mBufferPointer / 2;
|
||||
|
||||
for(int x = 0; x < mHilbertFilter.length / 2; x += 2)
|
||||
{
|
||||
accumulator += mHilbertFilter[x] *
|
||||
(mBuffer[mIndexMap[index][x + 1]] -
|
||||
mBuffer[mIndexMap[index][x]]);
|
||||
}
|
||||
|
||||
//Perform FS/2 frequency translation on the final filtered values
|
||||
if(mInvertFlag)
|
||||
{
|
||||
//inphase
|
||||
samples[y] = -(mBuffer[mIndexMap[index][mCenterTapIndex]]);
|
||||
|
||||
//quadrature
|
||||
samples[y + 1] = -accumulator;
|
||||
}
|
||||
else
|
||||
{
|
||||
//inphase
|
||||
samples[y] = mBuffer[mIndexMap[index][mCenterTapIndex]];
|
||||
|
||||
//quadrature
|
||||
samples[y + 1] = accumulator;
|
||||
}
|
||||
|
||||
mInvertFlag = !mInvertFlag;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the sample into the circular buffer, overwriting the oldest value
|
||||
*/
|
||||
private void insert(float sample)
|
||||
{
|
||||
mBuffer[mBufferPointer++] = sample;
|
||||
|
||||
mBufferPointer = mBufferPointer % mBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an index map to support efficient lookup of sample indexes from
|
||||
* the circular buffer.
|
||||
*
|
||||
* The first index of the map corresponds to the current buffer pointer
|
||||
* setting.
|
||||
*
|
||||
* The second index is structured in sample pairs to align with
|
||||
* the hilbert filter coefficients. For example, the first iteration of
|
||||
* the filter would use filter coefficient 0 and the index map values 0 and
|
||||
* 1 would be pointing to samples 46 and 0.
|
||||
*
|
||||
* The final element of each index set is the index of the center sample.
|
||||
*/
|
||||
private void generateIndexMap(int size)
|
||||
{
|
||||
mMapHeight = size / 2 + 1;
|
||||
int mapWidth = mMapHeight + 1;
|
||||
|
||||
mIndexMap = new int[mMapHeight][mapWidth];
|
||||
|
||||
//Setup the first row with buffer pointer index 0 as a starting point
|
||||
for(int x = 0; x < mapWidth - 1; x += 2)
|
||||
{
|
||||
mIndexMap[0][x] = size - 1 - x;
|
||||
mIndexMap[0][x + 1] = x;
|
||||
}
|
||||
|
||||
//Place the center index at the end of the array
|
||||
mCenterTapIndex = mapWidth - 1;
|
||||
|
||||
mIndexMap[0][mCenterTapIndex] = size / 2;
|
||||
|
||||
//For each subsequent row, increment the previous row's value by 2,
|
||||
//wrapping as needed, to keep the values between 0 and size - 1
|
||||
for(int x = 1; x < mMapHeight; x++)
|
||||
{
|
||||
for(int y = 0; y < mapWidth; y++)
|
||||
{
|
||||
mIndexMap[x][y] = mIndexMap[x - 1][y] + 2;
|
||||
|
||||
if(mIndexMap[x][y] >= size)
|
||||
{
|
||||
mIndexMap[x][y] -= size + 1;
|
||||
|
||||
//Special handling for center tap wrap around
|
||||
if(y == mCenterTapIndex && mIndexMap[x][y] < 0)
|
||||
{
|
||||
mIndexMap[x][y] = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the index map contents
|
||||
*/
|
||||
public void logIndexMap()
|
||||
{
|
||||
for(int[] indexSet : mIndexMap)
|
||||
{
|
||||
mLog.debug("Row:" + Arrays.toString(indexSet));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the half-band filter coefficients for use as hilbert transform
|
||||
* filter coefficients. Sets all even-numbered coefficients left of center
|
||||
* to negative and all even-numbered coefficients right of center to positive
|
||||
* which applies a FS/4 frequency translation to the coefficients.
|
||||
*
|
||||
* Note: even though the coefficients above the center coefficient are being
|
||||
* defined, they are unused during the convolution process since we're using
|
||||
* a folded FIR structure to exploit the symmetric nature of the half-band
|
||||
* filter coefficients. The polarity difference between the upper and lower
|
||||
* halves is accounted for in the filter() method.
|
||||
*
|
||||
* Note: we apply a 2.0 gain to the coefficients to compensate for the loss
|
||||
* of splitting the signal via two filters.
|
||||
*/
|
||||
private void convertHalfBandToHilbert(float[] coefficients)
|
||||
{
|
||||
mHilbertFilter = new float[coefficients.length];
|
||||
|
||||
int middle = coefficients.length / 2;
|
||||
|
||||
for(int x = 0; x < coefficients.length; x++)
|
||||
{
|
||||
if(x < middle)
|
||||
{
|
||||
mHilbertFilter[x] = 2.0f * -FastMath.abs(coefficients[x]);
|
||||
}
|
||||
else if(x > middle)
|
||||
{
|
||||
mHilbertFilter[x] = 2.0f * FastMath.abs(coefficients[x]);
|
||||
}
|
||||
else
|
||||
{
|
||||
mHilbertFilter[x] = 2.0f * coefficients[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import io.github.dsheirer.dsp.oscillator.IRealOscillator;
|
||||
import io.github.dsheirer.dsp.oscillator.OscillatorFactory;
|
||||
import io.github.dsheirer.sample.SampleUtils;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -35,9 +34,10 @@ public class ScalarHilbertTransform extends HilbertTransform
|
||||
/**
|
||||
* Filters the real samples array into complex samples at half the sample rate.
|
||||
* @param realSamples to filter
|
||||
* @param timestamp of the first sample
|
||||
* @return complex samples at half the sample rate
|
||||
*/
|
||||
public ComplexSamples filter(float[] realSamples)
|
||||
public ComplexSamples filter(float[] realSamples, long timestamp)
|
||||
{
|
||||
int bufferLength = realSamples.length / 2;
|
||||
|
||||
@@ -52,7 +52,7 @@ public class ScalarHilbertTransform extends HilbertTransform
|
||||
mQBuffer = qTemp;
|
||||
}
|
||||
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples);
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples, timestamp);
|
||||
System.arraycopy(deinterleaved.i(), 0, mIBuffer, mIOverlap, deinterleaved.i().length);
|
||||
System.arraycopy(deinterleaved.q(), 0, mQBuffer, mQOverlap, deinterleaved.q().length);
|
||||
|
||||
@@ -78,7 +78,7 @@ public class ScalarHilbertTransform extends HilbertTransform
|
||||
System.arraycopy(mIBuffer, mIBuffer.length - mIOverlap, mIBuffer, 0, mIOverlap);
|
||||
System.arraycopy(mQBuffer, mQBuffer.length - mQOverlap, mQBuffer, 0, mQOverlap);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
@@ -104,8 +104,8 @@ public class ScalarHilbertTransform extends HilbertTransform
|
||||
|
||||
if(validate)
|
||||
{
|
||||
ComplexSamples scalar = hilbertTransform.filter(realSamples);
|
||||
ComplexSamples vector = vector512.filter(realSamples);
|
||||
ComplexSamples scalar = hilbertTransform.filter(realSamples, 0l);
|
||||
ComplexSamples vector = vector512.filter(realSamples, start);
|
||||
|
||||
System.out.println("SI: " + Arrays.toString(scalar.i()));
|
||||
System.out.println("VI: " + Arrays.toString(vector.i()));
|
||||
@@ -119,7 +119,7 @@ public class ScalarHilbertTransform extends HilbertTransform
|
||||
{
|
||||
// ComplexSamples filtered = hilbertTransform.filter(realSamples);
|
||||
// ComplexSamples filtered = vectorDefault.filter(realSamples);
|
||||
ComplexSamples filtered = vector512.filter(realSamples);
|
||||
ComplexSamples filtered = vector512.filter(realSamples, start);
|
||||
// ComplexSamples filtered = vector256.filter(realSamples);
|
||||
// ComplexSamples filtered = vector128.filter(realSamples);
|
||||
// ComplexSamples filtered = vector64.filter(realSamples);
|
||||
|
||||
@@ -37,9 +37,10 @@ public class VectorHilbertTransform128Bits extends HilbertTransform
|
||||
/**
|
||||
* Filters the real samples array into complex samples at half the sample rate.
|
||||
* @param realSamples to filter
|
||||
* @param timestamp of the first sample
|
||||
* @return complex samples at half the sample rate
|
||||
*/
|
||||
public ComplexSamples filter(float[] realSamples)
|
||||
public ComplexSamples filter(float[] realSamples, long timestamp)
|
||||
{
|
||||
int bufferLength = realSamples.length / 2;
|
||||
|
||||
@@ -54,7 +55,7 @@ public class VectorHilbertTransform128Bits extends HilbertTransform
|
||||
mQBuffer = qTemp;
|
||||
}
|
||||
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples);
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples, timestamp);
|
||||
VectorUtilities.checkComplexArrayLength(deinterleaved.i(), deinterleaved.q(), VECTOR_SPECIES);
|
||||
|
||||
System.arraycopy(deinterleaved.i(), 0, mIBuffer, mIOverlap, deinterleaved.i().length);
|
||||
@@ -91,6 +92,6 @@ public class VectorHilbertTransform128Bits extends HilbertTransform
|
||||
System.arraycopy(mIBuffer, mIBuffer.length - mIOverlap, mIBuffer, 0, mIOverlap);
|
||||
System.arraycopy(mQBuffer, mQBuffer.length - mQOverlap, mQBuffer, 0, mQOverlap);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,10 @@ public class VectorHilbertTransform256Bits extends HilbertTransform
|
||||
/**
|
||||
* Filters the real samples array into complex samples at half the sample rate.
|
||||
* @param realSamples to filter
|
||||
* @param timestamp of the first sample
|
||||
* @return complex samples at half the sample rate
|
||||
*/
|
||||
public ComplexSamples filter(float[] realSamples)
|
||||
public ComplexSamples filter(float[] realSamples, long timestamp)
|
||||
{
|
||||
int bufferLength = realSamples.length / 2;
|
||||
|
||||
@@ -54,7 +55,7 @@ public class VectorHilbertTransform256Bits extends HilbertTransform
|
||||
mQBuffer = qTemp;
|
||||
}
|
||||
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples);
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples, timestamp);
|
||||
VectorUtilities.checkComplexArrayLength(deinterleaved.i(), deinterleaved.q(), VECTOR_SPECIES);
|
||||
|
||||
System.arraycopy(deinterleaved.i(), 0, mIBuffer, mIOverlap, deinterleaved.i().length);
|
||||
@@ -85,6 +86,6 @@ public class VectorHilbertTransform256Bits extends HilbertTransform
|
||||
System.arraycopy(mIBuffer, mIBuffer.length - mIOverlap, mIBuffer, 0, mIOverlap);
|
||||
System.arraycopy(mQBuffer, mQBuffer.length - mQOverlap, mQBuffer, 0, mQOverlap);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,11 @@ package io.github.dsheirer.dsp.filter.hilbert;
|
||||
import io.github.dsheirer.sample.SampleUtils;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.vector.VectorUtilities;
|
||||
import java.util.Arrays;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Implements a Hilbert transform using vector SIMD operations against a pre-defined 47-tap filter.
|
||||
* As described in Understanding Digital Signal Processing, Lyons, 3e, 2011, Section 13.37.2 (p 804-805)
|
||||
@@ -53,9 +52,10 @@ public class VectorHilbertTransform512Bits extends HilbertTransform
|
||||
/**
|
||||
* Filters the real samples array into complex samples at half the sample rate.
|
||||
* @param realSamples to filter
|
||||
* @param timestamp of the first sample
|
||||
* @return complex samples at half the sample rate
|
||||
*/
|
||||
public ComplexSamples filter(float[] realSamples)
|
||||
public ComplexSamples filter(float[] realSamples, long timestamp)
|
||||
{
|
||||
int bufferLength = realSamples.length / 2;
|
||||
|
||||
@@ -70,7 +70,7 @@ public class VectorHilbertTransform512Bits extends HilbertTransform
|
||||
mQBuffer = qTemp;
|
||||
}
|
||||
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples);
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples, timestamp);
|
||||
VectorUtilities.checkComplexArrayLength(deinterleaved.i(), deinterleaved.q(), VECTOR_SPECIES);
|
||||
|
||||
System.arraycopy(deinterleaved.i(), 0, mIBuffer, mIOverlap, deinterleaved.i().length);
|
||||
@@ -103,6 +103,6 @@ public class VectorHilbertTransform512Bits extends HilbertTransform
|
||||
System.arraycopy(mIBuffer, mIBuffer.length - mIOverlap, mIBuffer, 0, mIOverlap);
|
||||
System.arraycopy(mQBuffer, mQBuffer.length - mQOverlap, mQBuffer, 0, mQOverlap);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,10 @@ public class VectorHilbertTransform64Bits extends HilbertTransform
|
||||
/**
|
||||
* Filters the real samples array into complex samples at half the sample rate.
|
||||
* @param realSamples to filter
|
||||
* @param timestamp of the first sample
|
||||
* @return complex samples at half the sample rate
|
||||
*/
|
||||
public ComplexSamples filter(float[] realSamples)
|
||||
public ComplexSamples filter(float[] realSamples, long timestamp)
|
||||
{
|
||||
int bufferLength = realSamples.length / 2;
|
||||
|
||||
@@ -54,7 +55,7 @@ public class VectorHilbertTransform64Bits extends HilbertTransform
|
||||
mQBuffer = qTemp;
|
||||
}
|
||||
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples);
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples, timestamp);
|
||||
VectorUtilities.checkComplexArrayLength(deinterleaved.i(), deinterleaved.q(), VECTOR_SPECIES);
|
||||
|
||||
System.arraycopy(deinterleaved.i(), 0, mIBuffer, mIOverlap, deinterleaved.i().length);
|
||||
@@ -103,6 +104,6 @@ public class VectorHilbertTransform64Bits extends HilbertTransform
|
||||
System.arraycopy(mIBuffer, mIBuffer.length - mIOverlap, mIBuffer, 0, mIOverlap);
|
||||
System.arraycopy(mQBuffer, mQBuffer.length - mQOverlap, mQBuffer, 0, mQOverlap);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,10 @@ public class VectorHilbertTransformDefaultBits extends HilbertTransform
|
||||
/**
|
||||
* Filters the real samples array into complex samples at half the sample rate.
|
||||
* @param realSamples to filter
|
||||
* @param timestamp of the first sample
|
||||
* @return complex samples at half the sample rate
|
||||
*/
|
||||
public ComplexSamples filter(float[] realSamples)
|
||||
public ComplexSamples filter(float[] realSamples, long timestamp)
|
||||
{
|
||||
int bufferLength = realSamples.length / 2;
|
||||
|
||||
@@ -54,7 +55,7 @@ public class VectorHilbertTransformDefaultBits extends HilbertTransform
|
||||
mQBuffer = qTemp;
|
||||
}
|
||||
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples);
|
||||
ComplexSamples deinterleaved = SampleUtils.deinterleave(realSamples, timestamp);
|
||||
VectorUtilities.checkComplexArrayLength(deinterleaved.i(), deinterleaved.q(), VECTOR_SPECIES);
|
||||
|
||||
System.arraycopy(deinterleaved.i(), 0, mIBuffer, mIOverlap, deinterleaved.i().length);
|
||||
@@ -84,6 +85,6 @@ public class VectorHilbertTransformDefaultBits extends HilbertTransform
|
||||
System.arraycopy(mIBuffer, mIBuffer.length - mIOverlap, mIBuffer, 0, mIOverlap);
|
||||
System.arraycopy(mQBuffer, mQBuffer.length - mQOverlap, mQBuffer, 0, mQOverlap);
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.dsp.gain.complex;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
@@ -46,7 +65,7 @@ public abstract class ComplexGain
|
||||
*/
|
||||
public ComplexSamples apply(ComplexSamples samples)
|
||||
{
|
||||
return apply(samples.i(), samples.q());
|
||||
return apply(samples.i(), samples.q(), samples.timestamp());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +75,8 @@ public abstract class ComplexGain
|
||||
* original sample buffer.
|
||||
* @param i samples to amplify
|
||||
* @param q samples to amplify
|
||||
* @param timestamp of the first sample
|
||||
* @return amplified samples
|
||||
*/
|
||||
public abstract ComplexSamples apply(float[] i, float q[]);
|
||||
public abstract ComplexSamples apply(float[] i, float q[], long timestamp);
|
||||
}
|
||||
|
||||
@@ -18,13 +18,9 @@
|
||||
*/
|
||||
package io.github.dsheirer.dsp.gain.complex;
|
||||
|
||||
import io.github.dsheirer.sample.SampleUtils;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ComplexGainControl implements IComplexGainControl
|
||||
{
|
||||
public static final float OBJECTIVE_ENVELOPE = 1.0f;
|
||||
@@ -48,9 +44,10 @@ public class ComplexGainControl implements IComplexGainControl
|
||||
* gain for the single sample in the buffer that has the largest envelope.
|
||||
* @param i samples
|
||||
* @param q samples
|
||||
* @param timestamp of the first sample
|
||||
* @return processed/amplified buffer
|
||||
*/
|
||||
@Override public ComplexSamples process(float[] i, float[] q)
|
||||
@Override public ComplexSamples process(float[] i, float[] q, long timestamp)
|
||||
{
|
||||
float maxEnvelope = MINIMUM_ENVELOPE;
|
||||
|
||||
@@ -70,45 +67,6 @@ public class ComplexGainControl implements IComplexGainControl
|
||||
qProcessed[x] = q[x] * gain;
|
||||
}
|
||||
|
||||
return new ComplexSamples(iProcessed, qProcessed);
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
int sampleSize = 2048;
|
||||
ComplexSamples samples = SampleUtils.generateComplexRandomVectorLength(sampleSize);
|
||||
ComplexGainControl scalar = new ComplexGainControl();
|
||||
VectorComplexGainControl vector = new VectorComplexGainControl();
|
||||
|
||||
boolean validate = false;
|
||||
|
||||
if(validate)
|
||||
{
|
||||
ComplexSamples scalarSamples = scalar.process(samples.i(), samples.q());
|
||||
ComplexSamples vectorSamples = vector.process(samples.i(), samples.q());
|
||||
|
||||
System.out.println("SCALAR I: " + Arrays.toString(scalarSamples.i()));
|
||||
System.out.println("VECTOR I: " + Arrays.toString(vectorSamples.i()));
|
||||
System.out.println("SCALAR Q: " + Arrays.toString(scalarSamples.q()));
|
||||
System.out.println("VECTOR Q: " + Arrays.toString(vectorSamples.q()));
|
||||
}
|
||||
else
|
||||
{
|
||||
double accumulator = 0.0d;
|
||||
int iterations = 10_000_000;
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
for(int x = 0; x < iterations; x++)
|
||||
{
|
||||
// ComplexSamples processed = scalar.process(samples.i(), samples.q());
|
||||
ComplexSamples processed = vector.process(samples.i(), samples.q());
|
||||
accumulator += processed.i()[3];
|
||||
}
|
||||
|
||||
double elapsed = System.currentTimeMillis() - start;
|
||||
DecimalFormat df = new DecimalFormat("0.000");
|
||||
System.out.println("Accumulator: " + accumulator);
|
||||
System.out.println("Test Complete. Elapsed Time: " + df.format(elapsed / 1000.0d) + " seconds");
|
||||
}
|
||||
return new ComplexSamples(iProcessed, qProcessed, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.dsp.gain.complex;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
@@ -13,5 +32,5 @@ public interface IComplexGainControl
|
||||
* @param q samples
|
||||
* @return amplified gain samples
|
||||
*/
|
||||
ComplexSamples process(float[] i, float[] q);
|
||||
ComplexSamples process(float[] i, float[] q, long timestamp);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.dsp.gain.complex;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
@@ -22,7 +41,7 @@ public class ScalarComplexGain extends ComplexGain
|
||||
* @param q samples to amplify
|
||||
* @return amplified samples
|
||||
*/
|
||||
@Override public ComplexSamples apply(float[] i, float[] q)
|
||||
@Override public ComplexSamples apply(float[] i, float[] q, long timestamp)
|
||||
{
|
||||
for(int x = 0; x < i.length; x++)
|
||||
{
|
||||
@@ -30,6 +49,6 @@ public class ScalarComplexGain extends ComplexGain
|
||||
q[x] *= mGain;
|
||||
}
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.dsp.gain.complex;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
@@ -27,7 +46,7 @@ public class VectorComplexGain extends ComplexGain
|
||||
* @param q samples to amplify
|
||||
* @return amplified samples.
|
||||
*/
|
||||
@Override public ComplexSamples apply(float[] i, float[] q)
|
||||
@Override public ComplexSamples apply(float[] i, float[] q, long timestamp)
|
||||
{
|
||||
VectorUtilities.checkComplexArrayLength(i, q, VECTOR_SPECIES);
|
||||
|
||||
@@ -37,6 +56,6 @@ public class VectorComplexGain extends ComplexGain
|
||||
FloatVector.fromArray(VECTOR_SPECIES, q, x).mul(mGain).intoArray(q, x);
|
||||
}
|
||||
|
||||
return new ComplexSamples(i, q);
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.dsp.gain.complex;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
@@ -18,7 +37,7 @@ public class VectorComplexGainControl implements IComplexGainControl
|
||||
private static final float MINIMUM_ENVELOPE = 0.0001f;
|
||||
private static final float ENVELOPE_ESTIMATE = 0.4f;
|
||||
|
||||
@Override public ComplexSamples process(float[] i, float[] q)
|
||||
@Override public ComplexSamples process(float[] i, float[] q, long timestamp)
|
||||
{
|
||||
VectorUtilities.checkComplexArrayLength(i, q, VECTOR_SPECIES);
|
||||
|
||||
@@ -47,6 +66,6 @@ public class VectorComplexGainControl implements IComplexGainControl
|
||||
FloatVector.fromArray(VECTOR_SPECIES, q, bufferPointer).mul(gain).intoArray(qProcessed, bufferPointer);
|
||||
}
|
||||
|
||||
return new ComplexSamples(iProcessed, qProcessed);
|
||||
return new ComplexSamples(iProcessed, qProcessed, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,11 +84,12 @@ public abstract class ComplexMixer
|
||||
/**
|
||||
* Generates complex samples from the underlying oscillator
|
||||
* @param sampleCount to generate
|
||||
* @param timestamp of the first sample
|
||||
* @return complex samples
|
||||
*/
|
||||
protected ComplexSamples generate(int sampleCount)
|
||||
protected ComplexSamples generate(int sampleCount, long timestamp)
|
||||
{
|
||||
return mOscillator.generateComplexSamples(sampleCount);
|
||||
return mOscillator.generateComplexSamples(sampleCount, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +99,7 @@ public abstract class ComplexMixer
|
||||
*/
|
||||
public ComplexSamples mix(ComplexSamples samples)
|
||||
{
|
||||
return mix(samples.i(), samples.q());
|
||||
return mix(samples.i(), samples.q(), samples.timestamp());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +118,5 @@ public abstract class ComplexMixer
|
||||
* @param q complex samples to mix
|
||||
* @return mixed samples
|
||||
*/
|
||||
public abstract ComplexSamples mix(float[] i, float[] q);
|
||||
|
||||
|
||||
public abstract ComplexSamples mix(float[] i, float[] q, long timestamp);
|
||||
}
|
||||
|
||||
@@ -52,11 +52,12 @@ public class ScalarComplexMixer extends ComplexMixer
|
||||
* Mixes the complex I & Q samples with samples generated from an oscillator.
|
||||
* @param iSamples complex samples to mix
|
||||
* @param qSamples complex samples to mix
|
||||
* @param timestamp of the first sample
|
||||
* @return mixed samples
|
||||
*/
|
||||
@Override public ComplexSamples mix(float[] iSamples, float[] qSamples)
|
||||
@Override public ComplexSamples mix(float[] iSamples, float[] qSamples, long timestamp)
|
||||
{
|
||||
ComplexSamples mixer = generate(iSamples.length);
|
||||
ComplexSamples mixer = generate(iSamples.length, timestamp);
|
||||
|
||||
float[] iMixer = mixer.i();
|
||||
float[] qMixer = mixer.q();
|
||||
@@ -82,7 +83,7 @@ public class ScalarComplexMixer extends ComplexMixer
|
||||
@Override
|
||||
public ComplexSamples mix(InterleavedComplexSamples samples)
|
||||
{
|
||||
ComplexSamples mixer = generate(samples.samples().length / 2);
|
||||
ComplexSamples mixer = generate(samples.samples().length / 2, samples.timestamp());
|
||||
|
||||
float[] iMixer = mixer.i();
|
||||
float[] qMixer = mixer.q();
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.dsp.mixer;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
@@ -26,14 +45,15 @@ public class VectorComplexMixer extends ComplexMixer
|
||||
* Mixes the complex I & Q samples with samples generated from an oscillator.
|
||||
* @param iSamples complex samples to mix
|
||||
* @param qSamples complex samples to mix
|
||||
* @param timestamp for the first sample
|
||||
* @return mixed samples
|
||||
*/
|
||||
@Override public ComplexSamples mix(float[] iSamples, float[] qSamples)
|
||||
@Override public ComplexSamples mix(float[] iSamples, float[] qSamples, long timestamp)
|
||||
{
|
||||
VectorUtilities.checkComplexArrayLength(iSamples, qSamples, VECTOR_SPECIES);
|
||||
|
||||
//Reuse this complex samples buffer to store the results and return to caller
|
||||
ComplexSamples mixer = generate(iSamples.length);
|
||||
ComplexSamples mixer = generate(iSamples.length, timestamp);
|
||||
|
||||
float[] iMixer = mixer.i();
|
||||
float[] qMixer = mixer.q();
|
||||
|
||||
@@ -33,7 +33,8 @@ public interface IComplexOscillator extends IOscillator
|
||||
/**
|
||||
* Generates the specified number of complex samples into a complex samples object.
|
||||
* @param sampleCount number of complex samples to generate.
|
||||
* @param timestamp for the first sample
|
||||
* @return generated samples
|
||||
*/
|
||||
ComplexSamples generateComplexSamples(int sampleCount);
|
||||
ComplexSamples generateComplexSamples(int sampleCount, long timestamp);
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class ScalarComplexOscillator extends AbstractOscillator implements IComp
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexSamples generateComplexSamples(int sampleCount)
|
||||
public ComplexSamples generateComplexSamples(int sampleCount, long timestamp)
|
||||
{
|
||||
float[] iSamples = new float[sampleCount];
|
||||
float[] qSamples = new float[sampleCount];
|
||||
@@ -133,6 +133,6 @@ public class ScalarComplexOscillator extends AbstractOscillator implements IComp
|
||||
mPreviousInphase = previousInphase;
|
||||
mPreviousQuadrature = previousQuadrature;
|
||||
|
||||
return new ComplexSamples(iSamples, qSamples);
|
||||
return new ComplexSamples(iSamples, qSamples, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,13 @@ package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.vector.VectorUtilities;
|
||||
import java.util.Arrays;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Complex oscillator that uses JDK17 SIMD vector operations to generate complex sample arrays.
|
||||
*
|
||||
@@ -145,9 +144,10 @@ public class VectorComplexOscillator extends AbstractOscillator implements IComp
|
||||
/**
|
||||
* Generates complex samples.
|
||||
* @param sampleCount number of samples to generate and length of the resulting float array.
|
||||
* @param timestamp of the first sample
|
||||
* @return generated samples
|
||||
*/
|
||||
@Override public ComplexSamples generateComplexSamples(int sampleCount)
|
||||
@Override public ComplexSamples generateComplexSamples(int sampleCount, long timestamp)
|
||||
{
|
||||
if(sampleCount % VECTOR_SPECIES.length() != 0)
|
||||
{
|
||||
@@ -184,6 +184,6 @@ public class VectorComplexOscillator extends AbstractOscillator implements IComp
|
||||
previousInphase.intoArray(mPreviousInphases, 0);
|
||||
previousQuadrature.intoArray(mPreviousQuadratures, 0);
|
||||
|
||||
return new ComplexSamples(iSamples, qSamples);
|
||||
return new ComplexSamples(iSamples, qSamples, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ import io.github.dsheirer.spectrum.DFTSize;
|
||||
import io.github.dsheirer.spectrum.SpectrumPanel;
|
||||
import io.github.dsheirer.spectrum.converter.ComplexDecibelConverter;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -45,9 +48,6 @@ import javax.swing.JSpinner;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SynthesizerViewer extends JFrame
|
||||
{
|
||||
@@ -281,15 +281,15 @@ public class SynthesizerViewer extends JFrame
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
ComplexSamples channel1Buffer = getChannel1ControlPanel().getOscillator().generateComplexSamples(mSamplesPerCycle);
|
||||
ComplexSamples channel2Buffer = getChannel2ControlPanel().getOscillator().generateComplexSamples(mSamplesPerCycle);
|
||||
ComplexSamples channel1Buffer = getChannel1ControlPanel().getOscillator().generateComplexSamples(mSamplesPerCycle, 0l);
|
||||
ComplexSamples channel2Buffer = getChannel2ControlPanel().getOscillator().generateComplexSamples(mSamplesPerCycle, 0l);
|
||||
|
||||
ComplexSamples synthesizedBuffer = mSynthesizer.process(channel1Buffer, channel2Buffer);
|
||||
getChannel1Panel().receive(new FloatNativeBuffer(channel1Buffer, System.currentTimeMillis()));
|
||||
getChannel1Panel().receive(new FloatNativeBuffer(channel1Buffer));
|
||||
|
||||
getChannel2Panel().receive(new FloatNativeBuffer(channel2Buffer, System.currentTimeMillis()));
|
||||
getChannel2Panel().receive(new FloatNativeBuffer(channel2Buffer));
|
||||
|
||||
getSpectrumPanel().receive(new FloatNativeBuffer(synthesizedBuffer, System.currentTimeMillis()));
|
||||
getSpectrumPanel().receive(new FloatNativeBuffer(synthesizedBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,12 +42,11 @@ import io.github.dsheirer.sample.complex.IComplexSamplesListener;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.ISourceEventProvider;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* DMR decoder module.
|
||||
@@ -147,7 +146,7 @@ public class DMRDecoder extends FeedbackDecoder implements ISourceEventListener,
|
||||
@Override
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
mMessageFramer.setCurrentTime(samples.timestamp());
|
||||
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
@@ -155,7 +154,7 @@ public class DMRDecoder extends FeedbackDecoder implements ISourceEventListener,
|
||||
//Process buffer for power measurements
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
ComplexSamples amplified = mAGC.process(i, q, samples.timestamp());
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,20 +53,20 @@ public class DMRDecoderInstrumented extends DMRDecoder
|
||||
@Override
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
mMessageFramer.setCurrentTime(samples.timestamp());
|
||||
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
if(mFilteredSymbolListener != null)
|
||||
{
|
||||
mFilteredSymbolListener.receive(new ComplexSamples(i, q));
|
||||
mFilteredSymbolListener.receive(new ComplexSamples(i, q, samples.timestamp()));
|
||||
}
|
||||
|
||||
//Process buffer for power measurements
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
ComplexSamples amplified = mAGC.process(i, q, samples.timestamp());
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
* 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.module.decode.p25.phase1;
|
||||
|
||||
@@ -35,11 +32,10 @@ import io.github.dsheirer.dsp.psk.pll.PLLBandwidth;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class P25P1DecoderC4FM extends P25P1Decoder
|
||||
{
|
||||
@@ -110,7 +106,7 @@ public class P25P1DecoderC4FM extends P25P1Decoder
|
||||
//Process the buffer for power meter measurements (before gain is applied)
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
ComplexSamples amplified = mAGC.process(i, q, samples.timestamp());
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,20 +49,20 @@ public class P25P1DecoderC4FMInstrumented extends P25P1DecoderC4FM
|
||||
@Override
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
mMessageFramer.setCurrentTime(samples.timestamp());
|
||||
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
if(mFilteredSymbolListener != null)
|
||||
{
|
||||
mFilteredSymbolListener.receive(new ComplexSamples(i, q));
|
||||
mFilteredSymbolListener.receive(new ComplexSamples(i, q, samples.timestamp()));
|
||||
}
|
||||
|
||||
//Process the buffer for power meter measurements (before gain is applied)
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
ComplexSamples amplified = mAGC.process(i, q, samples.timestamp());
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
* 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.module.decode.p25.phase1;
|
||||
|
||||
@@ -34,11 +31,10 @@ import io.github.dsheirer.dsp.window.WindowType;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class P25P1DecoderLSM extends P25P1Decoder
|
||||
{
|
||||
@@ -107,7 +103,7 @@ public class P25P1DecoderLSM extends P25P1Decoder
|
||||
@Override
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
mMessageFramer.setCurrentTime(samples.timestamp());
|
||||
|
||||
//The filter will decrement the user count when finished
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
@@ -116,7 +112,7 @@ public class P25P1DecoderLSM extends P25P1Decoder
|
||||
//Process the buffer for power measurements
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
ComplexSamples amplified = mAGC.process(i, q, samples.timestamp());
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public class P25P1DecoderLSMInstrumented extends P25P1DecoderLSM
|
||||
@Override
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
mMessageFramer.setCurrentTime(samples.timestamp());
|
||||
|
||||
//The filter will decrement the user count when finished
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
@@ -58,13 +58,13 @@ public class P25P1DecoderLSMInstrumented extends P25P1DecoderLSM
|
||||
|
||||
if(mFilteredSymbolListener != null)
|
||||
{
|
||||
mFilteredSymbolListener.receive(new ComplexSamples(i, q));
|
||||
mFilteredSymbolListener.receive(new ComplexSamples(i, q, samples.timestamp()));
|
||||
}
|
||||
|
||||
//Process the buffer for power measurements
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
ComplexSamples amplified = mAGC.process(i, q, samples.timestamp());
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
* 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.module.decode.p25.phase2;
|
||||
|
||||
@@ -40,11 +37,10 @@ import io.github.dsheirer.module.decode.p25.phase2.enumeration.ScrambleParameter
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* P25 Phase 2 HDQPSK 2-timeslot Decoder
|
||||
@@ -121,7 +117,7 @@ public class P25P2DecoderHDQPSK extends P25P2Decoder implements IdentifierUpdate
|
||||
//Process the buffer for power measurements
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
ComplexSamples amplified = mAGC.process(i, q, samples.timestamp());
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ public class P25P2DecoderHDQPSKInstrumented extends P25P2DecoderHDQPSK
|
||||
//Process the buffer for power measurements
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
ComplexSamples amplified = mAGC.process(i, q, samples.timestamp());
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,14 +28,14 @@ import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.util.Dispatcher;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import io.github.dsheirer.util.TimeStamp;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
|
||||
/**
|
||||
* WAVE audio recorder module for recording complex (I&Q) samples to a wave file
|
||||
@@ -46,7 +46,7 @@ public class ComplexSamplesWaveRecorder extends Module implements IComplexSample
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ComplexSamplesWaveRecorder.class);
|
||||
|
||||
private Dispatcher<ComplexSamples> mBufferProcessor = new Dispatcher<>(500,
|
||||
"sdrtrunk complex wave recorder", new ComplexSamples(new float[0], new float[0]));
|
||||
"sdrtrunk complex wave recorder", new ComplexSamples(new float[0], new float[0], System.currentTimeMillis()));
|
||||
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
private BufferWaveWriter mWriter;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,12 @@ import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
||||
import io.github.dsheirer.source.tuner.TunerType;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.usb.USBTunerController;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import org.apache.commons.io.EndianUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -33,12 +39,6 @@ import org.usb4java.LibUsb;
|
||||
import org.usb4java.LibUsbException;
|
||||
|
||||
import javax.usb.UsbException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Airspy tuner controller
|
||||
@@ -272,6 +272,7 @@ public class AirspyTunerController extends USBTunerController
|
||||
*/
|
||||
public void setSampleRate(AirspySampleRate rate) throws LibUsbException, UsbException, SourceException
|
||||
{
|
||||
getNativeBufferFactory().setSamplesPerMillisecond((float)rate.getRate() / 1000.0f);
|
||||
if(rate.getRate() != mSampleRate)
|
||||
{
|
||||
int result = readByte(Command.SET_SAMPLE_RATE, 0, rate.getIndex(), true);
|
||||
|
||||
@@ -29,11 +29,10 @@ import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.util.Dispatcher;
|
||||
import java.util.Iterator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Complex sample source that provides a frequency-translated and decimated sample buffer stream. Uses a
|
||||
* CIC decimation filter that requires the decimation rate to be an integer multiple. Sample buffer processing
|
||||
@@ -229,7 +228,7 @@ public class HalfBandTunerChannelSource<T extends INativeBuffer> extends TunerCh
|
||||
ComplexSamples basebanded = mFrequencyCorrectionMixer.mix(iterator.next());
|
||||
float[] i = mIDecimationFilter.decimateReal(basebanded.i());
|
||||
float[] q = mQDecimationFilter.decimateReal(basebanded.q());
|
||||
mStreamHeartbeatProcessor.receive(new ComplexSamples(i, q));
|
||||
mStreamHeartbeatProcessor.receive(new ComplexSamples(i, q, basebanded.timestamp()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
||||
import io.github.dsheirer.source.tuner.TunerType;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.usb.USBTunerController;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import org.apache.commons.io.EndianUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -32,10 +36,6 @@ import org.usb4java.LibUsb;
|
||||
import org.usb4java.LibUsbException;
|
||||
|
||||
import javax.usb.UsbException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
|
||||
public class HackRFTunerController extends USBTunerController
|
||||
{
|
||||
@@ -404,6 +404,7 @@ public class HackRFTunerController extends USBTunerController
|
||||
mFrequencyController.setSampleRate(rate.getRate());
|
||||
setBasebandFilter(rate.getFilter());
|
||||
mSampleRate = rate;
|
||||
getNativeBufferFactory().setSamplesPerMillisecond((float)getSampleRate() / 1000.0f);
|
||||
}
|
||||
|
||||
public void setSampleRateManual(int frequency, int divider) throws UsbException
|
||||
|
||||
@@ -27,6 +27,12 @@ import io.github.dsheirer.source.tuner.TunerFactory;
|
||||
import io.github.dsheirer.source.tuner.TunerType;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.usb.USBTunerController;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.usb4java.DeviceHandle;
|
||||
@@ -35,12 +41,6 @@ import org.usb4java.LibUsbException;
|
||||
|
||||
import javax.usb.UsbDisconnectedException;
|
||||
import javax.usb.UsbException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* RTL-2832 tuner controller implementation.
|
||||
@@ -953,6 +953,9 @@ public class RTL2832TunerController extends USBTunerController
|
||||
setSampleRateFilters(sampleRate.getRate());
|
||||
mSampleRate = sampleRate;
|
||||
mFrequencyController.setSampleRate(sampleRate.getRate());
|
||||
|
||||
//Updates native buffer factory so that sample buffers can be accurately timestamped
|
||||
getNativeBufferFactory().setSamplesPerMillisecond(sampleRate.getRate() / 1000.0f);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -25,13 +25,12 @@ import io.github.dsheirer.dsp.oscillator.OscillatorFactory;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SampleGenerator
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(SampleGenerator.class);
|
||||
@@ -189,7 +188,7 @@ public class SampleGenerator
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
FloatNativeBuffer buffer = new FloatNativeBuffer(samples, now);
|
||||
FloatNativeBuffer buffer = new FloatNativeBuffer(samples, now, 0.0f);
|
||||
|
||||
mNativeBufferBroadcaster.broadcast(buffer);
|
||||
|
||||
|
||||
@@ -28,6 +28,11 @@ import io.github.dsheirer.source.IFrameLocationListener;
|
||||
import io.github.dsheirer.source.Source;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -36,11 +41,6 @@ import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ComplexWaveSource extends Source implements IControllableFileSource, AutoCloseable
|
||||
{
|
||||
@@ -276,7 +276,8 @@ public class ComplexWaveSource extends Source implements IControllableFileSource
|
||||
}
|
||||
|
||||
float[] samples = ConversionUtils.convertFromSigned16BitSamples(buffer);
|
||||
mListener.receive(new FloatNativeBuffer(samples, System.currentTimeMillis()));
|
||||
mListener.receive(new FloatNativeBuffer(samples, System.currentTimeMillis(),
|
||||
mInputStream.getFormat().getSampleRate() / 1000.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ public class AirspyUnpackedCalibration extends Calibration
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
AirspyBufferIteratorScalar iterator = new AirspyBufferIteratorScalar(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis());
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
@@ -235,7 +235,7 @@ public class AirspyUnpackedCalibration extends Calibration
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
AirspyBufferIteratorVector64Bits iterator = new AirspyBufferIteratorVector64Bits(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis());
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
@@ -255,7 +255,7 @@ public class AirspyUnpackedCalibration extends Calibration
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
AirspyBufferIteratorVector128Bits iterator = new AirspyBufferIteratorVector128Bits(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis());
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
@@ -276,7 +276,8 @@ public class AirspyUnpackedCalibration extends Calibration
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
AirspyBufferIteratorVector256Bits iterator =
|
||||
new AirspyBufferIteratorVector256Bits(samples, residualI, residualQ, 0.0f, System.currentTimeMillis());
|
||||
new AirspyBufferIteratorVector256Bits(samples, residualI, residualQ, 0.0f,
|
||||
System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
@@ -297,7 +298,8 @@ public class AirspyUnpackedCalibration extends Calibration
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
AirspyBufferIteratorVector512Bits iterator =
|
||||
new AirspyBufferIteratorVector512Bits(samples, residualI, residualQ, 0.0f, System.currentTimeMillis());
|
||||
new AirspyBufferIteratorVector512Bits(samples, residualI, residualQ, 0.0f,
|
||||
System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
|
||||
@@ -225,7 +225,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
AirspyInterleavedBufferIteratorScalar iterator = new AirspyInterleavedBufferIteratorScalar(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis());
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
@@ -246,7 +246,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
AirspyInterleavedBufferIteratorVector64Bits iterator = new AirspyInterleavedBufferIteratorVector64Bits(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis());
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
@@ -267,7 +267,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
|
||||
for(int x = 0; x < ITERATION_DURATION_MS; x++)
|
||||
{
|
||||
AirspyInterleavedBufferIteratorVector128Bits iterator = new AirspyInterleavedBufferIteratorVector128Bits(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis());
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
@@ -288,7 +288,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
AirspyInterleavedBufferIteratorVector256Bits iterator =
|
||||
new AirspyInterleavedBufferIteratorVector256Bits(samples, residualI, residualQ, 0.0f, System.currentTimeMillis());
|
||||
new AirspyInterleavedBufferIteratorVector256Bits(samples, residualI, residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
@@ -309,7 +309,8 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
AirspyInterleavedBufferIteratorVector512Bits iterator =
|
||||
new AirspyInterleavedBufferIteratorVector512Bits(samples, residualI, residualQ, 0.0f, System.currentTimeMillis());
|
||||
new AirspyInterleavedBufferIteratorVector512Bits(samples, residualI, residualQ, 0.0f,
|
||||
System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
|
||||
@@ -121,7 +121,7 @@ public class ComplexGainCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples amplified = scalar.apply(i, q);
|
||||
ComplexSamples amplified = scalar.apply(i, q, start);
|
||||
accumulator += amplified.i()[2];
|
||||
count++;
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public class ComplexGainCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples amplified = scalar.apply(i, q);
|
||||
ComplexSamples amplified = scalar.apply(i, q, start);
|
||||
accumulator += amplified.i()[2];
|
||||
count++;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public class ComplexGainControlCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples amplified = mScalarGainControl.process(i, q);
|
||||
ComplexSamples amplified = mScalarGainControl.process(i, q, 0l);
|
||||
accumulator += amplified.i()[2];
|
||||
count++;
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public class ComplexGainControlCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples amplified = mVectorGainControl.process(i, q);
|
||||
ComplexSamples amplified = mVectorGainControl.process(i, q, 0l);
|
||||
accumulator += amplified.i()[2];
|
||||
count++;
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ public class HilbertCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples complex = mScalar.filter(samples);
|
||||
ComplexSamples complex = mScalar.filter(samples, start);
|
||||
accumulator += complex.i()[0];
|
||||
count++;
|
||||
}
|
||||
@@ -272,7 +272,7 @@ public class HilbertCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples complex = mVectorPreferred.filter(samples);
|
||||
ComplexSamples complex = mVectorPreferred.filter(samples, start);
|
||||
accumulator += complex.i()[0];
|
||||
count++;
|
||||
}
|
||||
@@ -289,7 +289,7 @@ public class HilbertCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples complex = mVector512.filter(samples);
|
||||
ComplexSamples complex = mVector512.filter(samples, start);
|
||||
accumulator += complex.i()[0];
|
||||
count++;
|
||||
}
|
||||
@@ -306,7 +306,7 @@ public class HilbertCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples complex = mVector256.filter(samples);
|
||||
ComplexSamples complex = mVector256.filter(samples, start);
|
||||
accumulator += complex.i()[0];
|
||||
count++;
|
||||
}
|
||||
@@ -323,7 +323,7 @@ public class HilbertCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples complex = mVector128.filter(samples);
|
||||
ComplexSamples complex = mVector128.filter(samples, start);
|
||||
accumulator += complex.i()[0];
|
||||
count++;
|
||||
}
|
||||
@@ -340,7 +340,7 @@ public class HilbertCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples complex = mVector64.filter(samples);
|
||||
ComplexSamples complex = mVector64.filter(samples, start);
|
||||
accumulator += complex.i()[0];
|
||||
count++;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ public class ComplexMixerCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples mixed = mScalarComplexMixer.mix(i, q);
|
||||
ComplexSamples mixed = mScalarComplexMixer.mix(i, q, start);
|
||||
accumulator += mixed.i()[2];
|
||||
count++;
|
||||
}
|
||||
@@ -131,7 +131,7 @@ public class ComplexMixerCalibration extends Calibration
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
ComplexSamples mixed = mVectorComplexMixer.mix(i, q);
|
||||
ComplexSamples mixed = mVectorComplexMixer.mix(i, q, start);
|
||||
accumulator += mixed.i()[2];
|
||||
count++;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user