From bc462b9652222a4018eda539f35b779b6612c3b7 Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Sat, 17 Dec 2022 06:39:35 -0500 Subject: [PATCH] #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/buffer/AbstractNativeBuffer.java | 70 +++++ .../buffer/AbstractNativeBufferFactory.java | 44 ++++ .../dsheirer/buffer/ByteNativeBuffer.java | 23 +- .../buffer/ByteNativeBufferFactory.java | 4 +- .../dsheirer/buffer/FloatNativeBuffer.java | 35 +-- .../dsheirer/buffer/INativeBufferFactory.java | 8 + .../buffer/SignedByteNativeBuffer.java | 24 +- .../buffer/SignedByteNativeBufferFactory.java | 4 +- .../buffer/airspy/AirspyBufferIterator.java | 20 +- .../airspy/AirspyBufferIteratorScalar.java | 10 +- .../AirspyBufferIteratorVector128Bits.java | 12 +- .../AirspyBufferIteratorVector256Bits.java | 12 +- .../AirspyBufferIteratorVector512Bits.java | 13 +- .../AirspyBufferIteratorVector64Bits.java | 13 +- ...AirspyInterleavedBufferIteratorScalar.java | 8 +- ...nterleavedBufferIteratorVector128Bits.java | 8 +- ...nterleavedBufferIteratorVector256Bits.java | 8 +- ...nterleavedBufferIteratorVector512Bits.java | 8 +- ...InterleavedBufferIteratorVector64Bits.java | 8 +- .../buffer/airspy/AirspyNativeBuffer.java | 38 ++- .../airspy/AirspyNativeBufferFactory.java | 7 +- .../channelizer/PolyphaseChannelSource.java | 4 +- .../channelizer/TwoChannelSynthesizerM2.java | 2 +- .../output/ChannelOutputProcessor.java | 13 +- .../IPolyphaseChannelOutputProcessor.java | 3 +- .../channelizer/output/MixerAssembler.java | 3 +- .../output/OneChannelMixerAssembler.java | 4 +- .../output/OneChannelOutputProcessor.java | 2 +- .../channelizer/output/SampleAssembler.java | 4 +- .../output/TwoChannelMixerAssembler.java | 7 +- .../output/TwoChannelOutputProcessor.java | 4 +- .../dsp/filter/hilbert/HilbertTransform.java | 3 +- .../hilbert/LegacyHilbertTransform.java | 248 ------------------ .../hilbert/ScalarHilbertTransform.java | 14 +- .../VectorHilbertTransform128Bits.java | 7 +- .../VectorHilbertTransform256Bits.java | 7 +- .../VectorHilbertTransform512Bits.java | 10 +- .../hilbert/VectorHilbertTransform64Bits.java | 7 +- .../VectorHilbertTransformDefaultBits.java | 7 +- .../dsp/gain/complex/ComplexGain.java | 24 +- .../dsp/gain/complex/ComplexGainControl.java | 48 +--- .../dsp/gain/complex/IComplexGainControl.java | 21 +- .../dsp/gain/complex/ScalarComplexGain.java | 23 +- .../dsp/gain/complex/VectorComplexGain.java | 23 +- .../complex/VectorComplexGainControl.java | 23 +- .../dsheirer/dsp/mixer/ComplexMixer.java | 11 +- .../dsp/mixer/ScalarComplexMixer.java | 7 +- .../dsp/mixer/VectorComplexMixer.java | 24 +- .../dsp/oscillator/IComplexOscillator.java | 3 +- .../oscillator/ScalarComplexOscillator.java | 4 +- .../oscillator/VectorComplexOscillator.java | 8 +- .../gui/channelizer/SynthesizerViewer.java | 16 +- .../module/decode/dmr/DMRDecoder.java | 9 +- .../decode/dmr/DMRDecoderInstrumented.java | 6 +- .../decode/p25/phase1/P25P1DecoderC4FM.java | 36 ++- .../phase1/P25P1DecoderC4FMInstrumented.java | 6 +- .../decode/p25/phase1/P25P1DecoderLSM.java | 38 ++- .../phase1/P25P1DecoderLSMInstrumented.java | 6 +- .../decode/p25/phase2/P25P2DecoderHDQPSK.java | 36 ++- .../P25P2DecoderHDQPSKInstrumented.java | 2 +- .../wave/ComplexSamplesWaveRecorder.java | 10 +- .../github/dsheirer/sample/SampleUtils.java | 12 +- .../sample/adapter/ComplexShortAdapter.java | 7 +- .../sample/complex/ComplexSamples.java | 2 +- .../ComplexSamplesNativeBufferAdapter.java | 3 +- .../complex/ComplexSamplesRepackager.java | 95 ------- .../complex/InterleavedComplexSamples.java | 2 +- .../tuner/airspy/AirspyTunerController.java | 13 +- .../channel/HalfBandTunerChannelSource.java | 5 +- .../tuner/hackrf/HackRFTunerController.java | 9 +- .../tuner/rtl/RTL2832TunerController.java | 15 +- .../source/tuner/test/SampleGenerator.java | 7 +- .../source/wave/ComplexWaveSource.java | 13 +- .../airspy/AirspyUnpackedCalibration.java | 12 +- .../AirspyUnpackedInterleavedCalibration.java | 11 +- .../gain/ComplexGainCalibration.java | 4 +- .../gain/ComplexGainControlCalibration.java | 4 +- .../calibrate/hilbert/HilbertCalibration.java | 12 +- .../mixer/ComplexMixerCalibration.java | 4 +- 79 files changed, 607 insertions(+), 733 deletions(-) create mode 100644 src/main/java/io/github/dsheirer/buffer/AbstractNativeBuffer.java create mode 100644 src/main/java/io/github/dsheirer/buffer/AbstractNativeBufferFactory.java delete mode 100644 src/main/java/io/github/dsheirer/dsp/filter/hilbert/LegacyHilbertTransform.java delete mode 100644 src/main/java/io/github/dsheirer/sample/complex/ComplexSamplesRepackager.java diff --git a/src/main/java/io/github/dsheirer/buffer/AbstractNativeBuffer.java b/src/main/java/io/github/dsheirer/buffer/AbstractNativeBuffer.java new file mode 100644 index 00000000..ddee858c --- /dev/null +++ b/src/main/java/io/github/dsheirer/buffer/AbstractNativeBuffer.java @@ -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 + * **************************************************************************** + */ + +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()); + } +} diff --git a/src/main/java/io/github/dsheirer/buffer/AbstractNativeBufferFactory.java b/src/main/java/io/github/dsheirer/buffer/AbstractNativeBufferFactory.java new file mode 100644 index 00000000..0e96eecb --- /dev/null +++ b/src/main/java/io/github/dsheirer/buffer/AbstractNativeBufferFactory.java @@ -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 + * **************************************************************************** + */ + +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; + } +} diff --git a/src/main/java/io/github/dsheirer/buffer/ByteNativeBuffer.java b/src/main/java/io/github/dsheirer/buffer/ByteNativeBuffer.java index 6594f6a6..bfe81129 100644 --- a/src/main/java/io/github/dsheirer/buffer/ByteNativeBuffer.java +++ b/src/main/java/io/github/dsheirer/buffer/ByteNativeBuffer.java @@ -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 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); } } } diff --git a/src/main/java/io/github/dsheirer/buffer/ByteNativeBufferFactory.java b/src/main/java/io/github/dsheirer/buffer/ByteNativeBufferFactory.java index 257a5d1e..ba395321 100644 --- a/src/main/java/io/github/dsheirer/buffer/ByteNativeBufferFactory.java +++ b/src/main/java/io/github/dsheirer/buffer/ByteNativeBufferFactory.java @@ -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()); } /** diff --git a/src/main/java/io/github/dsheirer/buffer/FloatNativeBuffer.java b/src/main/java/io/github/dsheirer/buffer/FloatNativeBuffer.java index e1a9fbf1..daf4999b 100644 --- a/src/main/java/io/github/dsheirer/buffer/FloatNativeBuffer.java +++ b/src/main/java/io/github/dsheirer/buffer/FloatNativeBuffer.java @@ -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 { 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()); } } } diff --git a/src/main/java/io/github/dsheirer/buffer/INativeBufferFactory.java b/src/main/java/io/github/dsheirer/buffer/INativeBufferFactory.java index fb15bb54..a6632dc6 100644 --- a/src/main/java/io/github/dsheirer/buffer/INativeBufferFactory.java +++ b/src/main/java/io/github/dsheirer/buffer/INativeBufferFactory.java @@ -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); } diff --git a/src/main/java/io/github/dsheirer/buffer/SignedByteNativeBuffer.java b/src/main/java/io/github/dsheirer/buffer/SignedByteNativeBuffer.java index 0d363213..f87ea66a 100644 --- a/src/main/java/io/github/dsheirer/buffer/SignedByteNativeBuffer.java +++ b/src/main/java/io/github/dsheirer/buffer/SignedByteNativeBuffer.java @@ -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); } } } diff --git a/src/main/java/io/github/dsheirer/buffer/SignedByteNativeBufferFactory.java b/src/main/java/io/github/dsheirer/buffer/SignedByteNativeBufferFactory.java index 699b946b..f21511fc 100644 --- a/src/main/java/io/github/dsheirer/buffer/SignedByteNativeBufferFactory.java +++ b/src/main/java/io/github/dsheirer/buffer/SignedByteNativeBufferFactory.java @@ -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()); } /** diff --git a/src/main/java/io/github/dsheirer/buffer/airspy/AirspyBufferIterator.java b/src/main/java/io/github/dsheirer/buffer/airspy/AirspyBufferIterator.java index e41bec7b..cd035cd4 100644 --- a/src/main/java/io/github/dsheirer/buffer/airspy/AirspyBufferIterator.java +++ b/src/main/java/io/github/dsheirer/buffer/airspy/AirspyBufferIterator.java @@ -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 implements Iterator 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 implements Iterator * @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 implements Iterator 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 implements Iterator } } + /** + * 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() { diff --git a/src/main/java/io/github/dsheirer/buffer/airspy/AirspyBufferIteratorScalar.java b/src/main/java/io/github/dsheirer/buffer/airspy/AirspyBufferIteratorScalar.java index 805f8f43..d2f60ab4 100644 --- a/src/main/java/io/github/dsheirer/buffer/airspy/AirspyBufferIteratorScalar.java +++ b/src/main/java/io/github/dsheirer/buffer/airspy/AirspyBufferIteratorScalar.java @@ -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 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()); }; } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/buffer/airspy/AirspyNativeBufferFactory.java b/src/main/java/io/github/dsheirer/buffer/airspy/AirspyNativeBufferFactory.java index b8c349eb..03552407 100644 --- a/src/main/java/io/github/dsheirer/buffer/airspy/AirspyNativeBufferFactory.java +++ b/src/main/java/io/github/dsheirer/buffer/airspy/AirspyNativeBufferFactory.java @@ -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); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java index fb90d83f..08b1f8ec 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java @@ -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 diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/TwoChannelSynthesizerM2.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/TwoChannelSynthesizerM2.java index 995bf6ce..f1929860 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/TwoChannelSynthesizerM2.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/TwoChannelSynthesizerM2.java @@ -156,7 +156,7 @@ public class TwoChannelSynthesizerM2 mTopBlockFlag = !mTopBlockFlag; } - return new ComplexSamples(i, q); + return new ComplexSamples(i, q, channelBuffer1.timestamp()); } /** diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java index 09343108..df5a936e 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java @@ -33,6 +33,7 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP private Dispatcher> mChannelResultsDispatcher; protected Listener 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 channelResultsList) + public void receiveChannelResults(List channelResultsList, long timestamp) { mChannelResultsDispatcher.receive(channelResultsList); + mCurrentSampleTimestamp = timestamp; } /** diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java index ec347451..1bfd7836 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java @@ -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 channelResults); + void receiveChannelResults(List channelResults, long timestamp); /** * Listener to receive assembled complex samples buffers diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/MixerAssembler.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/MixerAssembler.java index f610ab84..9ac7799e 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/MixerAssembler.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/MixerAssembler.java @@ -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); } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelMixerAssembler.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelMixerAssembler.java index df9e1e36..988f3099 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelMixerAssembler.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelMixerAssembler.java @@ -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()) { diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java index f6bdaaa1..73137095 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java @@ -90,7 +90,7 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor if(mMixerAssembler.hasBuffer()) { - ComplexSamples buffer = mMixerAssembler.getBuffer(); + ComplexSamples buffer = mMixerAssembler.getBuffer(getCurrentSampleTimestamp()); if(mComplexSamplesListener != null) { diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/SampleAssembler.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/SampleAssembler.java index af696548..f63c9f63 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/SampleAssembler.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/SampleAssembler.java @@ -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; } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelMixerAssembler.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelMixerAssembler.java index a0f944ed..d216ccf9 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelMixerAssembler.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelMixerAssembler.java @@ -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); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java index 11d01b51..98c2fe31 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java @@ -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 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) { diff --git a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/HilbertTransform.java b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/HilbertTransform.java index aacd5870..bbdc1569 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/HilbertTransform.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/HilbertTransform.java @@ -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 diff --git a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/LegacyHilbertTransform.java b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/LegacyHilbertTransform.java deleted file mode 100644 index dcaa0660..00000000 --- a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/LegacyHilbertTransform.java +++ /dev/null @@ -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 - * **************************************************************************** - */ -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]; - } - } - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/ScalarHilbertTransform.java b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/ScalarHilbertTransform.java index 3c66beeb..244b066b 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/ScalarHilbertTransform.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/ScalarHilbertTransform.java @@ -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); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform128Bits.java b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform128Bits.java index 3aa34efd..8b851c71 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform128Bits.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform128Bits.java @@ -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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform256Bits.java b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform256Bits.java index 782a53cd..2d18f04c 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform256Bits.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform256Bits.java @@ -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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform512Bits.java b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform512Bits.java index 240a0121..5982df4c 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform512Bits.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform512Bits.java @@ -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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform64Bits.java b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform64Bits.java index e242ae85..cbc286b9 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform64Bits.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransform64Bits.java @@ -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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransformDefaultBits.java b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransformDefaultBits.java index 138ecb00..21f87a53 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransformDefaultBits.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/VectorHilbertTransformDefaultBits.java @@ -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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGain.java b/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGain.java index 6f8c0469..037993b4 100644 --- a/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGain.java +++ b/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGain.java @@ -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 + * **************************************************************************** + */ + 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); } diff --git a/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGainControl.java b/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGainControl.java index afe8a477..3497fa80 100644 --- a/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGainControl.java +++ b/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGainControl.java @@ -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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/gain/complex/IComplexGainControl.java b/src/main/java/io/github/dsheirer/dsp/gain/complex/IComplexGainControl.java index bc2be9eb..b1ae3cba 100644 --- a/src/main/java/io/github/dsheirer/dsp/gain/complex/IComplexGainControl.java +++ b/src/main/java/io/github/dsheirer/dsp/gain/complex/IComplexGainControl.java @@ -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 + * **************************************************************************** + */ + 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); } diff --git a/src/main/java/io/github/dsheirer/dsp/gain/complex/ScalarComplexGain.java b/src/main/java/io/github/dsheirer/dsp/gain/complex/ScalarComplexGain.java index d6cb9875..300df62c 100644 --- a/src/main/java/io/github/dsheirer/dsp/gain/complex/ScalarComplexGain.java +++ b/src/main/java/io/github/dsheirer/dsp/gain/complex/ScalarComplexGain.java @@ -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 + * **************************************************************************** + */ + 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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/gain/complex/VectorComplexGain.java b/src/main/java/io/github/dsheirer/dsp/gain/complex/VectorComplexGain.java index 899eda23..6b610c09 100644 --- a/src/main/java/io/github/dsheirer/dsp/gain/complex/VectorComplexGain.java +++ b/src/main/java/io/github/dsheirer/dsp/gain/complex/VectorComplexGain.java @@ -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 + * **************************************************************************** + */ + 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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/gain/complex/VectorComplexGainControl.java b/src/main/java/io/github/dsheirer/dsp/gain/complex/VectorComplexGainControl.java index 2d6d15b5..52dbd4a8 100644 --- a/src/main/java/io/github/dsheirer/dsp/gain/complex/VectorComplexGainControl.java +++ b/src/main/java/io/github/dsheirer/dsp/gain/complex/VectorComplexGainControl.java @@ -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 + * **************************************************************************** + */ + 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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/mixer/ComplexMixer.java b/src/main/java/io/github/dsheirer/dsp/mixer/ComplexMixer.java index 3f531d60..8f34fce7 100644 --- a/src/main/java/io/github/dsheirer/dsp/mixer/ComplexMixer.java +++ b/src/main/java/io/github/dsheirer/dsp/mixer/ComplexMixer.java @@ -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); } diff --git a/src/main/java/io/github/dsheirer/dsp/mixer/ScalarComplexMixer.java b/src/main/java/io/github/dsheirer/dsp/mixer/ScalarComplexMixer.java index bd4cdd60..aa39afe7 100644 --- a/src/main/java/io/github/dsheirer/dsp/mixer/ScalarComplexMixer.java +++ b/src/main/java/io/github/dsheirer/dsp/mixer/ScalarComplexMixer.java @@ -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(); diff --git a/src/main/java/io/github/dsheirer/dsp/mixer/VectorComplexMixer.java b/src/main/java/io/github/dsheirer/dsp/mixer/VectorComplexMixer.java index b41bae2d..c8cfea15 100644 --- a/src/main/java/io/github/dsheirer/dsp/mixer/VectorComplexMixer.java +++ b/src/main/java/io/github/dsheirer/dsp/mixer/VectorComplexMixer.java @@ -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 + * **************************************************************************** + */ + 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(); diff --git a/src/main/java/io/github/dsheirer/dsp/oscillator/IComplexOscillator.java b/src/main/java/io/github/dsheirer/dsp/oscillator/IComplexOscillator.java index 9b79e190..e774e885 100644 --- a/src/main/java/io/github/dsheirer/dsp/oscillator/IComplexOscillator.java +++ b/src/main/java/io/github/dsheirer/dsp/oscillator/IComplexOscillator.java @@ -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); } diff --git a/src/main/java/io/github/dsheirer/dsp/oscillator/ScalarComplexOscillator.java b/src/main/java/io/github/dsheirer/dsp/oscillator/ScalarComplexOscillator.java index e945ae15..29f8d08f 100644 --- a/src/main/java/io/github/dsheirer/dsp/oscillator/ScalarComplexOscillator.java +++ b/src/main/java/io/github/dsheirer/dsp/oscillator/ScalarComplexOscillator.java @@ -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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/oscillator/VectorComplexOscillator.java b/src/main/java/io/github/dsheirer/dsp/oscillator/VectorComplexOscillator.java index 3c362dde..cea9b836 100644 --- a/src/main/java/io/github/dsheirer/dsp/oscillator/VectorComplexOscillator.java +++ b/src/main/java/io/github/dsheirer/dsp/oscillator/VectorComplexOscillator.java @@ -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); } } diff --git a/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java b/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java index 1167a036..734d1355 100644 --- a/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java +++ b/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java @@ -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)); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoder.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoder.java index 32c3bf6e..5b6f19b2 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoder.java @@ -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); } diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderInstrumented.java index 1c7d1433..cb078cea 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderInstrumented.java @@ -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); } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderC4FM.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderC4FM.java index de5d4af9..f29d4fad 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderC4FM.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderC4FM.java @@ -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 - * * ***************************************************************************** + * 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 + * **************************************************************************** */ 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); } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderC4FMInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderC4FMInstrumented.java index 6101314b..0e3ee04c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderC4FMInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderC4FMInstrumented.java @@ -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); } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderLSM.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderLSM.java index 6db8f44f..5f86ddfc 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderLSM.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderLSM.java @@ -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 - * * ***************************************************************************** + * 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 + * **************************************************************************** */ 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); } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderLSMInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderLSMInstrumented.java index 2cf8b055..9969e16c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderLSMInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderLSMInstrumented.java @@ -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); } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderHDQPSK.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderHDQPSK.java index 1cf97217..9667cc15 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderHDQPSK.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderHDQPSK.java @@ -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 - * * ***************************************************************************** + * 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 + * **************************************************************************** */ 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); } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderHDQPSKInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderHDQPSKInstrumented.java index d6f53e92..ab269b92 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderHDQPSKInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderHDQPSKInstrumented.java @@ -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); } diff --git a/src/main/java/io/github/dsheirer/record/wave/ComplexSamplesWaveRecorder.java b/src/main/java/io/github/dsheirer/record/wave/ComplexSamplesWaveRecorder.java index 39140ded..c2e921e0 100644 --- a/src/main/java/io/github/dsheirer/record/wave/ComplexSamplesWaveRecorder.java +++ b/src/main/java/io/github/dsheirer/record/wave/ComplexSamplesWaveRecorder.java @@ -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 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; diff --git a/src/main/java/io/github/dsheirer/sample/SampleUtils.java b/src/main/java/io/github/dsheirer/sample/SampleUtils.java index 255a26bc..32dbeb68 100644 --- a/src/main/java/io/github/dsheirer/sample/SampleUtils.java +++ b/src/main/java/io/github/dsheirer/sample/SampleUtils.java @@ -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) diff --git a/src/main/java/io/github/dsheirer/sample/adapter/ComplexShortAdapter.java b/src/main/java/io/github/dsheirer/sample/adapter/ComplexShortAdapter.java index 0efef045..40f0d543 100644 --- a/src/main/java/io/github/dsheirer/sample/adapter/ComplexShortAdapter.java +++ b/src/main/java/io/github/dsheirer/sample/adapter/ComplexShortAdapter.java @@ -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 long now = System.currentTimeMillis(); - return new FloatNativeBuffer(convertedSamples, now); + return new FloatNativeBuffer(convertedSamples, now, 192.0f); } /** diff --git a/src/main/java/io/github/dsheirer/sample/complex/ComplexSamples.java b/src/main/java/io/github/dsheirer/sample/complex/ComplexSamples.java index 9d6fb0cd..936d3936 100644 --- a/src/main/java/io/github/dsheirer/sample/complex/ComplexSamples.java +++ b/src/main/java/io/github/dsheirer/sample/complex/ComplexSamples.java @@ -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. diff --git a/src/main/java/io/github/dsheirer/sample/complex/ComplexSamplesNativeBufferAdapter.java b/src/main/java/io/github/dsheirer/sample/complex/ComplexSamplesNativeBufferAdapter.java index 5597c317..137ef18e 100644 --- a/src/main/java/io/github/dsheirer/sample/complex/ComplexSamplesNativeBufferAdapter.java +++ b/src/main/java/io/github/dsheirer/sample/complex/ComplexSamplesNativeBufferAdapter.java @@ -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(); } /** diff --git a/src/main/java/io/github/dsheirer/sample/complex/ComplexSamplesRepackager.java b/src/main/java/io/github/dsheirer/sample/complex/ComplexSamplesRepackager.java deleted file mode 100644 index 1164e3e7..00000000 --- a/src/main/java/io/github/dsheirer/sample/complex/ComplexSamplesRepackager.java +++ /dev/null @@ -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 - * **************************************************************************** - */ - -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 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 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; - } -} diff --git a/src/main/java/io/github/dsheirer/sample/complex/InterleavedComplexSamples.java b/src/main/java/io/github/dsheirer/sample/complex/InterleavedComplexSamples.java index d08ac31b..b42e47f7 100644 --- a/src/main/java/io/github/dsheirer/sample/complex/InterleavedComplexSamples.java +++ b/src/main/java/io/github/dsheirer/sample/complex/InterleavedComplexSamples.java @@ -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()); } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java index 9d125e34..7c74f998 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java @@ -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); diff --git a/src/main/java/io/github/dsheirer/source/tuner/channel/HalfBandTunerChannelSource.java b/src/main/java/io/github/dsheirer/source/tuner/channel/HalfBandTunerChannelSource.java index 23e02c88..2df83923 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/channel/HalfBandTunerChannelSource.java +++ b/src/main/java/io/github/dsheirer/source/tuner/channel/HalfBandTunerChannelSource.java @@ -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 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())); } } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java index eaedae4f..3d20b81a 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java @@ -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 diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java index 65de47e5..876ae8a3 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java @@ -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 { diff --git a/src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java b/src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java index 8914c7fa..69e39893 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java +++ b/src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java @@ -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); diff --git a/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java b/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java index e089d192..d6d74a99 100644 --- a/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java +++ b/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java @@ -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)); } } } diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/airspy/AirspyUnpackedCalibration.java b/src/main/java/io/github/dsheirer/vector/calibrate/airspy/AirspyUnpackedCalibration.java index 249d70bd..e0d2093d 100644 --- a/src/main/java/io/github/dsheirer/vector/calibrate/airspy/AirspyUnpackedCalibration.java +++ b/src/main/java/io/github/dsheirer/vector/calibrate/airspy/AirspyUnpackedCalibration.java @@ -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)) { diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/airspy/AirspyUnpackedInterleavedCalibration.java b/src/main/java/io/github/dsheirer/vector/calibrate/airspy/AirspyUnpackedInterleavedCalibration.java index 893c44f8..daf7accf 100644 --- a/src/main/java/io/github/dsheirer/vector/calibrate/airspy/AirspyUnpackedInterleavedCalibration.java +++ b/src/main/java/io/github/dsheirer/vector/calibrate/airspy/AirspyUnpackedInterleavedCalibration.java @@ -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)) { diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/gain/ComplexGainCalibration.java b/src/main/java/io/github/dsheirer/vector/calibrate/gain/ComplexGainCalibration.java index d5811b75..f1248e8c 100644 --- a/src/main/java/io/github/dsheirer/vector/calibrate/gain/ComplexGainCalibration.java +++ b/src/main/java/io/github/dsheirer/vector/calibrate/gain/ComplexGainCalibration.java @@ -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++; } diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/gain/ComplexGainControlCalibration.java b/src/main/java/io/github/dsheirer/vector/calibrate/gain/ComplexGainControlCalibration.java index ea5146e7..708b5d3f 100644 --- a/src/main/java/io/github/dsheirer/vector/calibrate/gain/ComplexGainControlCalibration.java +++ b/src/main/java/io/github/dsheirer/vector/calibrate/gain/ComplexGainControlCalibration.java @@ -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++; } diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/hilbert/HilbertCalibration.java b/src/main/java/io/github/dsheirer/vector/calibrate/hilbert/HilbertCalibration.java index 0a71df56..ce1f545d 100644 --- a/src/main/java/io/github/dsheirer/vector/calibrate/hilbert/HilbertCalibration.java +++ b/src/main/java/io/github/dsheirer/vector/calibrate/hilbert/HilbertCalibration.java @@ -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++; } diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/mixer/ComplexMixerCalibration.java b/src/main/java/io/github/dsheirer/vector/calibrate/mixer/ComplexMixerCalibration.java index 9bc312fe..94011d42 100644 --- a/src/main/java/io/github/dsheirer/vector/calibrate/mixer/ComplexMixerCalibration.java +++ b/src/main/java/io/github/dsheirer/vector/calibrate/mixer/ComplexMixerCalibration.java @@ -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++; }