Updates complex decimation filters to use either scalar or vector optimal implementation. (#1148)

Updates HackRF native byte buffers with automatic DC removal.
Creates new half-band channel source to replace the cic-based channel source in the heterodyne channelizer.  Resolves issue in RTL native buffer processing.  Creates a CACHED thread pool and reduces the number of threads for the SCHEDULED thread pool to 2.  Changes each of the channel source implementations to use a dedicated thread (Dispatcher class) instead of the scheduled thread pool, to reduce the amount of buffered/retained samples objects in memory. Lots of other small changes and tweaks.
Removes last of Reusable buffer code.  Updates DFTProcessor to use a single thread scheduled executor.  Updates Polyphase channelizer to use threads instead of an executor.  Removes a number of unused classes.
Commenting out window calibration.  Scalar is faster in both implementations.
Adds vector and scalar implementations for windows.  Repackages window and window type classes.
WIP adds vector implementations for Airspy non-interleaved buffer iterator and calibration plugins for both interleaved and non-interleaved buffer iterators.
WIP adds vector implementations for Airspy interleaved buffer iterator.
Work on developing scalar and vector implementations of airspy sample conversion and airspy native buffer processing.
Introduces new INativeBuffer framework where tuner raw samples are retained in their native (compressed) format until when they're needed and they are converted on-demand into smaller complex sample buffers to aid garbage collection.
Removes all usage of ReusableComplexBuffers and ReusableChannelREsultsBuffers.  Updates polyphase channelizer to be more efficient in output channel processing.
Updates ViterbiDecoder to change Map usage to array for memory efficiency.
Updates channel processors to used scalar implementation for gain and mixer, until the polyphase channel results can be fixed to be a multiple of the SIMD lane width.
Resolves final changes for the conversion from interleaved sample arrays to complexsamples buffers for distributing baseband samples to channels and processing chains.
Updates tuner channel sources and all decoders to receive new complex samples buffers, removing the previous reusable complex buffers as the default baseband samples buffer object.
Still to resolve is the ComplexMixer implementations.  Needs validation and a calibration implementation with an update to the complex mixer factory.
Updates the DMR decoder to use optimal (scalar vs vector) implementations of filter and AGC controls.
Updates the AM demodulator for separated I&Q float sample processing.
Code review and testing changes.
updates build script for runtime configuration.
Integrates calibration manager with main sdrtrunk application.  Updates NBFMDecoder to use new optimized FIR and Decimation filter options.
Updates real half-band decimation filters to select the optimal (scalar vs vector) implementation.
Adds calibration manager plugins.
Adds calibration manager and plugins to determine the optimal (scalar vs vector) implementation type to use for various classes of dsp components.
Adds vector squelching FM demodulator and cleans up the IIR filter and power squelch to use all floats.
Adds vector implementation of FM demodulator.  This introduces a new interface that accepts i/q samples as two separate float arrays, versus an interleaved array, which enables more efficient processing.  This new complex sample interface will be used across the codebase eventually.
Adds ComplexOscillator and a SIMD version VectorComplexOscillator.  Deprecates the LowPhaseNoiseOscillator because the ComplexOscillator is about 35% faster.  The vectorized version is about 43% faster when SIMD lane width is 8x.
Adds new vector complex filters and new real oscillators.  Note: the RealOscillator using straight Java math sin() function is 3x slower than the existing legacy implementation.  Will replace this with a complex oscillator that only uses the real part.
Updates ComplexFIRFilter to be 50% more efficient.
Adds 23 tap filter implementations.  Will not add 63-tap implementations because they are slower than the default vector implementation.
Adds 15-tap customized vector half band filters.
Adds initial custom complex vector half-band decimation filters for 11-tap and 15-tap decimation filters..
Adds vector complex half band filter implementations.  Note: performance of the generic 64-bit 2-lane filter is horrible and is not included.
Optimizes complex half-band filter to be 27% more efficient.
Implements vectorized real half-band filter implementations.
Implements a new FIR filter class that processes sample buffers instead of single sample at a time.
Refactored all of the calibratons to be time-duration based.  Testing on windows i7 gen 3 CPU showed that it took 1000x longer for one of the calibrations to run, versus my AMD 5800x.
Changed gradle jvm args so that the windows plaf export is always applied.
Updates gradle build script to ensure incubator module is included and to cleanup javafx library management for building runtime images.
Testing tweaks and changes.
Adds logging limiter to dispatcher for buffer overflows
Adds User Preferences editor for performing Vector/SIMD calibrations and new startup dialog prompting the user to perform calibrations.
Updates calibration manager and all calibration implementations to add warm-up and test phases and to reduce the overall time it takes to calibrate a system.

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2022-03-04 08:45:05 -05:00
committed by GitHub
parent 5e9c239ea3
commit 0eec2e7ab8
403 changed files with 22916 additions and 15088 deletions

View File

@@ -1,6 +1,26 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample;
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
import io.github.dsheirer.sample.complex.ComplexSamples;
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -52,12 +72,32 @@ public class ConversionUtils
return converted;
}
/**
* Converts the float samples into a little-endian 16-bit sample byte buffer.
*
* @param samples - float array of sample data
* @return - little-endian 16-bit sample byte buffer
*/
public static ByteBuffer convertToSigned16BitSamples(ComplexSamples samples)
{
ByteBuffer converted = ByteBuffer.allocate(samples.i().length * 4);
converted.order(ByteOrder.LITTLE_ENDIAN);
for(int x = 0; x < samples.i().length; x++)
{
converted.putShort((short)(samples.i()[x] * Short.MAX_VALUE));
converted.putShort((short)(samples.q()[x] * Short.MAX_VALUE));
}
return converted;
}
/**
* Converts the float samples in a complex buffer to a little endian 16-bit
* buffer
*/
public static ByteBuffer convertToSigned16BitSamples(ReusableFloatBuffer buffer)
public static ByteBuffer convertToSigned16BitSamples(InterleavedComplexSamples complexSamples)
{
return convertToSigned16BitSamples(buffer.getSamples());
return convertToSigned16BitSamples(complexSamples.samples());
}
}

View File

@@ -1,17 +1,17 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
@@ -19,7 +19,5 @@ package io.github.dsheirer.sample;
public interface Provider<T>
{
public void setListener( Listener<T> listener );
public void removeListener( Listener<T> listener );
void setListener(Listener<T> listener);
}

View File

@@ -0,0 +1,145 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample;
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;
public class SampleUtils
{
public static final double TWO_PI = Math.PI * 2.0;
/**
* 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
* @return deinterleaved complex samples instance
*/
public static ComplexSamples deinterleave(float[] samples)
{
float[] i = new float[samples.length / 2];
float[] q = new float[samples.length / 2];
int offset;
for(int x = 0; x < i.length; x++)
{
offset = 2 * x;
i[x] = samples[offset];
q[x] = samples[offset + 1];
}
return new ComplexSamples(i, q);
}
/**
* Converts a complex samples buffer to an array of interleaved (I, Q, etc.) samples
* @param complexSamples to interleave
* @return interleaved samples array
*/
public static float[] interleave(ComplexSamples complexSamples)
{
float[] i = complexSamples.i();
float[] q = complexSamples.q();;
float[] complex = new float[i.length + q.length];
for(int x = 0; x < i.length; x++)
{
complex[2 * x] = i[x];
complex[2 * x + 1] = q[x];
}
return complex;
}
/**
* Generates a buffer of complex samples where each sample's vector length is unity (1.0)
* @param count of buffer samples
* @return complex buffer
*/
public static ComplexSamples generateComplex(int count)
{
Random random = new Random();
float[] i = new float[count];
float[] q = new float[count];
for(int x = 0; x < i.length; x++)
{
//Random angle in radians in range of -PI to + PI
double angle = random.nextDouble() * TWO_PI - Math.PI;
i[x] = (float)Math.cos(angle);
q[x] = (float)Math.sin(angle);
}
return new ComplexSamples(i, q);
}
/**
* Generates random complex samples with vector length in range 0.5 to 1.0 (unity).
* @param count of samples
* @return complex sample buffer
*/
public static ComplexSamples generateComplexRandomVectorLength(int count)
{
Random random = new Random();
ComplexSamples unitySamples = generateComplex(count);
float[] i = unitySamples.i();
float[] q = unitySamples.q();
float gain;
//Apply random gain reduction to samples in range 0.5 to 1.0 gain
for(int x = 0; x < i.length; x++)
{
gain = 0.5f + random.nextFloat() / 2.0f;
i[x] *= gain;
q[x] *= gain;
}
return new ComplexSamples(i, q);
}
public static Complex multiply(Complex a, Complex b)
{
return new Complex((a.inphase() * b.inphase()) - (a.quadrature() * b.quadrature()),
((a.quadrature() * b.inphase()) + (a.inphase() * b.quadrature())));
}
public static Complex minus(Complex a, Complex b)
{
return new Complex(a.inphase() - b.inphase(), a.quadrature() - b.quadrature());
}
public static double magnitude(Complex sample)
{
return FastMath.sqrt(magnitudeSquared(sample));
}
public static int magnitudeSquared(Complex sample)
{
return (int)((sample.inphase() * sample.inphase()) + (sample.quadrature() * sample.quadrature()));
}
}

View File

@@ -1,23 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.adapter;
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
public abstract class AbstractSampleAdapter<T extends ReusableFloatBuffer>
{
public abstract T convert(byte[] sampleBytes);
}

View File

@@ -1,39 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.adapter;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.sample.buffer.ReusableComplexBufferQueue;
public abstract class ComplexSampleAdapter extends AbstractSampleAdapter<ReusableComplexBuffer>
{
private ReusableComplexBufferQueue mReusableBufferQueue;
/**
* Constructs a real sample adapter
*
* @param debugName to use for debug logging
*/
public ComplexSampleAdapter(String debugName)
{
mReusableBufferQueue = new ReusableComplexBufferQueue(debugName);
}
protected ReusableComplexBuffer getBuffer(int size)
{
return mReusableBufferQueue.getBuffer(size);
}
}

View File

@@ -1,21 +1,25 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
/*
* *****************************************************************************
* 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 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.adapter;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.buffer.FloatNativeBuffer;
import io.github.dsheirer.buffer.INativeBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -25,7 +29,7 @@ import java.nio.ByteOrder;
/**
* Converts 16-bit/2-byte little endian byte data into a reusable buffer of complex float sample data
*/
public class ComplexShortAdapter extends ComplexSampleAdapter
public class ComplexShortAdapter implements ISampleAdapter<INativeBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(ComplexShortAdapter.class);
private ByteOrder mByteOrder = ByteOrder.LITTLE_ENDIAN;
@@ -33,19 +37,15 @@ public class ComplexShortAdapter extends ComplexSampleAdapter
/**
* Constructs a real sample adapter
*
* @param debugName to use for debug logging
*/
public ComplexShortAdapter(String debugName)
public ComplexShortAdapter()
{
super(debugName);
}
@Override
public ReusableComplexBuffer convert(byte[] samples)
public INativeBuffer convert(byte[] samples)
{
ReusableComplexBuffer reusableBuffer = getBuffer(samples.length / 2);
float[] convertedSamples = reusableBuffer.getSamples();
float[] convertedSamples = new float[samples.length / 2];
int pointer = 0;
@@ -60,7 +60,9 @@ public class ComplexShortAdapter extends ComplexSampleAdapter
pointer++;
}
return reusableBuffer;
long now = System.currentTimeMillis();
return new FloatNativeBuffer(convertedSamples, now);
}
/**

View File

@@ -0,0 +1,24 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.adapter;
public interface ISampleAdapter<T>
{
T convert(byte[] sampleBytes);
}

View File

@@ -1,21 +1,23 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
/*
* *****************************************************************************
* 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 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.adapter;
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
import io.github.dsheirer.source.mixer.MixerChannel;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
@@ -34,10 +36,8 @@ public class RealChannelShortAdapter extends RealSampleAdapter
private MixerChannel mMixerChannel;
private ByteBuffer mByteBuffer;
public RealChannelShortAdapter(MixerChannel channel, String debugName)
public RealChannelShortAdapter(MixerChannel channel)
{
super(debugName);
/* Only use with LEFT/RIGHT channels */
Validate.isTrue(channel != MixerChannel.MONO);
@@ -45,10 +45,9 @@ public class RealChannelShortAdapter extends RealSampleAdapter
}
@Override
public ReusableFloatBuffer convert(byte[] samples)
public float[] convert(byte[] samples)
{
ReusableFloatBuffer reusableFloatBuffer = getBuffer(samples.length / 4);
float[] convertedSamples = reusableFloatBuffer.getSamples();
float[] convertedSamples = new float[samples.length / 4];
mByteBuffer = ByteBuffer.wrap(samples);
@@ -75,7 +74,7 @@ public class RealChannelShortAdapter extends RealSampleAdapter
}
}
return reusableFloatBuffer;
return convertedSamples;
}
/**

View File

@@ -1,39 +1,29 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
/*
* *****************************************************************************
* 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 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.adapter;
import io.github.dsheirer.sample.buffer.ReusableBufferQueue;
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
public abstract class RealSampleAdapter extends AbstractSampleAdapter<ReusableFloatBuffer>
public abstract class RealSampleAdapter implements ISampleAdapter<float[]>
{
private ReusableBufferQueue mReusableBufferQueue;
/**
* Constructs a real sample adapter
*
* @param debugName to use for debug logging
*/
public RealSampleAdapter(String debugName)
public RealSampleAdapter()
{
mReusableBufferQueue = new ReusableBufferQueue(debugName);
}
protected ReusableFloatBuffer getBuffer(int size)
{
return mReusableBufferQueue.getBuffer(size);
}
}

View File

@@ -1,22 +1,23 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
/*
* *****************************************************************************
* 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 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.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.adapter;
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -30,19 +31,15 @@ public class RealShortAdapter extends RealSampleAdapter
/**
* Constructs a real sample adapter
*
* @param debugName to use for debug logging
*/
public RealShortAdapter(String debugName)
public RealShortAdapter()
{
super(debugName);
}
@Override
public ReusableFloatBuffer convert(byte[] samples)
public float[] convert(byte[] samples)
{
ReusableFloatBuffer reusableFloatBuffer = getBuffer(samples.length / 2);
float[] convertedSamples = reusableFloatBuffer.getSamples();
float[] convertedSamples = new float[samples.length / 2];
int pointer = 0;
@@ -57,7 +54,7 @@ public class RealShortAdapter extends RealSampleAdapter
pointer++;
}
return reusableFloatBuffer;
return convertedSamples;
}
/**

View File

@@ -1,177 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class AbstractReusableBuffer
{
private final static Logger mLog = LoggerFactory.getLogger(AbstractReusableBuffer.class);
protected IReusableBufferDisposedListener mBufferDisposedListener;
protected long mTimestamp;
protected AtomicInteger mUserCount = new AtomicInteger();
private String mDebugName;
public AbstractReusableBuffer(IReusableBufferDisposedListener bufferDisposedListener, long timestamp)
{
mBufferDisposedListener = bufferDisposedListener;
mTimestamp = timestamp;
}
public AbstractReusableBuffer(IReusableBufferDisposedListener bufferDisposedListener)
{
mBufferDisposedListener = bufferDisposedListener;
}
public void dispose()
{
mBufferDisposedListener = null;
}
public void setDebugName(String debugName)
{
mDebugName = debugName;
}
public String name()
{
return (mDebugName != null ? mDebugName : "(null)") + " User Count:" + getUserCount();
}
public String toString()
{
return name();
}
/**
* Reference timestamp for this complex buffer. Timestamp is relative to the first sample in the buffer.
* @return timestamp as milliseconds since epoch
*/
public long getTimestamp()
{
return mTimestamp;
}
public void setTimestamp(long timestamp)
{
mTimestamp = timestamp;
}
/**
* Sets the user count to 0 and recycles this buffer
*/
public void clearUserCount()
{
mUserCount.set(0);
recycle();
}
/**
* Decrements the user count for this buffer to indicate that the user/receiver of this buffer is finished using
* the buffer.
*
* User count is established prior to sending this buffer to each listener and each listener invokes this method to
* indicate that they have finished processing the buffer so that when the user count reaches zero, this buffer
* can be reused.
*
* This method is thread-safe.
*/
public synchronized void decrementUserCount()
{
if(mUserCount.decrementAndGet() <= 0)
{
recycle();
}
}
/**
* Sends this buffer back to the owning buffer queue for reuse
*/
private synchronized void recycle()
{
prepareForRecycle();
if(mBufferDisposedListener != null)
{
mBufferDisposedListener.disposed(this);
}
if(mUserCount.get() < 0)
{
mUserCount.set(0);
throw new IllegalStateException("User count is below zero. This indicates that this buffer's decrement" +
" user count was invoked by more than the expected user count");
}
}
/**
* Invoked just prior to notifying the owner that this buffer is ready for prepareForRecycle. This method
* is intended for sub-class implementations to perform any prepareForRecycle cleanup actions.
*/
protected void prepareForRecycle()
{
//No-op in this abstract class
}
/**
* Increments the user count to indicate that this buffer will be sent to another user.
*
* Users/receivers of this buffer that wish to forward the buffer to additional users should increment the expected
* user count prior to invoking the decrementUserCount() to indicate that the original receiver of the buffer is
* finished to avoid the user count reaching zero and the buffer being flagged as ready for reuse.
*
* This method is thread-safe.
*/
public void incrementUserCount()
{
mUserCount.incrementAndGet();
}
/**
* Increments the user count by the specified count to indicate that this buffer will be sent to additional users.
*
* Users/receivers of this buffer that wish to forward the buffer to additional users should increment the expected
* user count prior to invoking the decrementUserCount() to indicate that the original receiver of the buffer is
* finished to avoid the user count reaching zero and the buffer being flagged as ready for reuse.
*
* This method is thread-safe.
*
* @param additionalUserCount to add to the current user count
*/
public void incrementUserCount(int additionalUserCount)
{
if(additionalUserCount >= 0)
{
mUserCount.addAndGet(additionalUserCount);
}
else
{
mLog.debug("Request to add negative user count [" + additionalUserCount + "] ignored");
}
}
/**
* Number of users currently registered for this buffer
*/
public int getUserCount()
{
return mUserCount.get();
}
}

View File

@@ -1,129 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Queue;
import java.util.concurrent.LinkedTransferQueue;
public abstract class AbstractReusableBufferQueue<T extends AbstractReusableBuffer>
implements IReusableBufferDisposedListener<T>
{
private final static Logger mLog = LoggerFactory.getLogger(AbstractReusableBufferQueue.class);
private Queue<T> mReusableBufferQueue = new LinkedTransferQueue<>();
private int mBufferCount = 0;
private String mDebugName;
//Set to true to log when reusable buffers are created to monitor proper user count management
protected boolean mBufferCreationLoggingEnabled = false;
/**
* Base queue for managing reusable buffers.
*
* @param debugName to associate a debug string with this buffer instance
*/
public AbstractReusableBufferQueue(String debugName)
{
mDebugName = debugName;
}
/**
* Base queue for managing reusable buffers.
*/
public AbstractReusableBufferQueue()
{
}
/**
* Turns on/off logging of buffer creation
*/
public void setDebugLogging(boolean enabled)
{
mBufferCreationLoggingEnabled = enabled;
}
/**
* Disposes of any reclaimed buffers to prepare this queue for disposal.
*/
public void dispose()
{
T buffer = mReusableBufferQueue.poll();
while(buffer != null)
{
buffer.dispose();
buffer = mReusableBufferQueue.poll();
}
mBufferCount = 0;
}
/**
* Implements buffer disposed listener interface. Disposed (ie user count = 0) buffers will automatically callback
* to this method to indicate when they are disposed.
*
* @param reusableBuffer that has been disposed
*/
@Override
public void disposed(T reusableBuffer)
{
mReusableBufferQueue.offer(reusableBuffer);
}
/**
* Get a recycled buffer from the queue
*/
protected T getRecycledBuffer()
{
return mReusableBufferQueue.poll();
}
/**
* Increments the count of buffers managed by this queue. Note: this method is NOT thread safe
*/
protected void incrementBufferCount()
{
mBufferCount++;
if(mBufferCreationLoggingEnabled)
{
mLog.debug("Buffer Created - count:" + mBufferCount +
" debug:" + (mDebugName != null ? mDebugName : "null") + " class:" + this.getClass());
}
}
/**
* Current count of buffers created by this queue
*/
protected int getBufferCount()
{
return mBufferCount;
}
/**
* Debug name for this queue instance
*/
protected String getDebugName()
{
return mDebugName;
}
}

View File

@@ -0,0 +1,28 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.sample.Listener;
import java.nio.ByteBuffer;
public interface IByteBufferListener
{
Listener<ByteBuffer> getByteBufferListener();
}

View File

@@ -0,0 +1,41 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.sample.Listener;
import java.nio.ByteBuffer;
public interface IByteBufferProvider
{
/**
* Adds the listener to receive complex buffer samples
*/
void setBufferListener(Listener<ByteBuffer> listener);
/**
* Removes the listener from receiving complex buffer samples
*/
void removeBufferListener(Listener<ByteBuffer> listener);
/**
* Indicates if there are any complex buffer listeners registered
*/
boolean hasBufferListeners();
}

View File

@@ -1,25 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
public interface IReusableBufferDisposedListener<T extends AbstractReusableBuffer>
{
/**
* Notifies the listener that the timestamped buffer has been disposed and can now be reused.
* @param t reusableBuffer that has been disposed
*/
void disposed(T t);
}

View File

@@ -1,31 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.sample.Listener;
public interface IReusableBufferProvider
{
/**
* Adds the listener to receive complex buffer samples
*/
void setBufferListener(Listener<ReusableFloatBuffer> listener);
/**
* Removes the listener from receiving complex buffer samples
*/
void removeBufferListener();
}

View File

@@ -1,23 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.sample.Listener;
public interface IReusableByteBufferListener<ReusableByteBuffer>
{
Listener<ReusableByteBuffer> getReusableByteBufferListener();
}

View File

@@ -1,23 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.sample.Listener;
public interface IReusableComplexBufferListener<ReusableComplexBuffer>
{
Listener<ReusableComplexBuffer> getReusableComplexBufferListener();
}

View File

@@ -1,36 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.sample.Listener;
public interface IReusableComplexBufferProvider
{
/**
* Adds the listener to receive complex buffer samples
*/
void addBufferListener(Listener<ReusableComplexBuffer> listener);
/**
* Removes the listener from receiving complex buffer samples
*/
void removeBufferListener(Listener<ReusableComplexBuffer> listener);
/**
* Indicates if there are any complex buffer listeners registered
*/
boolean hasBufferListeners();
}

View File

@@ -0,0 +1,28 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.buffer;
/**
* Interface for objects that have timestamps.
*/
public interface ITimestamped
{
long timestamp();
}

View File

@@ -1,69 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.sample.OverflowableTransferQueue;
public class OverflowableReusableBufferTransferQueue<T extends AbstractReusableBuffer> extends OverflowableTransferQueue<T>
{
/**
* Concurrent transfer queue that couples a higher-throughput linked transfer queue with an atomic integer for
* monitoring queue size. When the queue size exceeds maximum size (overflow), all inbound elements are ignored
* until the queue size is reduced to or below the reset threshold.
*
* This implementation includes special handling for reusable complex buffers.
*
* @param maximumSize of the queue. Overflow state will occur once queue size exceeds this value.
* @param resetThreshold for resetting overflow state to normal, once queue size is at or below this value.
*/
public OverflowableReusableBufferTransferQueue(int maximumSize, int resetThreshold)
{
super(maximumSize, resetThreshold);
}
/**
* Overrides the overflow method to decrement the user count on any buffers that are being discarded when the queue
* is in an overflow state.
*
* @param t reusableBuffer that will be discarded
*/
@Override
protected void overflow(T t)
{
t.decrementUserCount();
}
/**
* Overrides the buffer clear method to decrement the user count on each buffer that is being cleared from the queue.
*/
@Override
public void clear()
{
synchronized(mQueue)
{
T buffer = mQueue.poll();
while(buffer != null)
{
buffer.decrementUserCount();
buffer = mQueue.poll();
}
mCounter.set(0);
mOverflow.set(false);
}
}
}

View File

@@ -1,80 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.real.RealSampleListener;
/**
* Assembles single float samples into a ReusableFloatBuffer containing an array of floats
*/
public class ReusableBufferAssembler implements RealSampleListener
{
private int mBufferSize;
private int mBufferPointer;
private ReusableFloatBuffer mCurrentBuffer;
private ReusableBufferQueue mReusableBufferQueue = new ReusableBufferQueue("Real");
private Listener<ReusableFloatBuffer> mListener;
public ReusableBufferAssembler(int bufferSize)
{
mBufferSize = bufferSize;
}
public void dispose()
{
mListener = null;
}
public void reset()
{
mCurrentBuffer.decrementUserCount();
mCurrentBuffer = null;
mBufferPointer = 0;
}
@Override
public void receive(float sample)
{
if(mCurrentBuffer == null)
{
mCurrentBuffer = mReusableBufferQueue.getBuffer(mBufferSize);
mBufferPointer = 0;
}
mCurrentBuffer.getSamples()[mBufferPointer++] = sample;
if (mBufferPointer >= mBufferSize)
{
if (mListener != null)
{
mListener.receive(mCurrentBuffer);
}
else
{
mCurrentBuffer.decrementUserCount();
}
mCurrentBuffer = null;
}
}
public void setListener(Listener<ReusableFloatBuffer> listener)
{
mListener = listener;
}
}

View File

@@ -1,48 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReusableBufferBroadcaster<T extends AbstractReusableBuffer> extends Broadcaster<T>
{
private final static Logger mLog = LoggerFactory.getLogger(ReusableBufferBroadcaster.class);
/**
* Increments the user count for the reusable complex buffer and then broadcasts the buffer to all registered
* listeners.
*
* The total user count is established and applied to the buffer prior to dispatching. If we were to simply
* increment the user count prior to sending to each consumer, there is a possibility that the consumer could
* immediately decrement the user count and prematurely signal that the buffer is ready for disposal before we
* send the buffer to all consumers.
*/
@Override
public void broadcast(T reusableBuffer)
{
for(Listener<T> listener : mListeners)
{
reusableBuffer.incrementUserCount();
listener.receive(reusableBuffer);
}
//Decrement user counter for this broadcaster
reusableBuffer.decrementUserCount();
}
}

View File

@@ -1,80 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReusableBufferQueue extends AbstractReusableBufferQueue<ReusableFloatBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(ReusableBufferQueue.class);
public ReusableBufferQueue(String debugName)
{
super(debugName);
}
/**
* Returns a reusable buffer from an internal recycling queue, or creates a new buffer if there are currently no
* buffers available for reuse. The user count is set to one and must be managed throughout the lifecycle
* of the buffer.
*
* @param size of the samples that will be loaded into the buffer
* @return a reusable buffer
*/
public ReusableFloatBuffer getBuffer(int size)
{
ReusableFloatBuffer buffer = getRecycledBuffer();
if(buffer == null)
{
buffer = new ReusableFloatBuffer(this, new float[size]);
buffer.setDebugName("Owner:" + getDebugName());
incrementBufferCount();
}
buffer.resize(size);
buffer.incrementUserCount();
return buffer;
}
/**
* Creates or reuses a buffer and loads it with the samples and timestamp and increments user count to one.
* @param samples to load
* @param timestamp to set
* @return loaded buffer with user count set to one
*/
public ReusableFloatBuffer getBuffer(float[] samples, long timestamp)
{
ReusableFloatBuffer buffer = getRecycledBuffer();
if(buffer == null)
{
buffer = new ReusableFloatBuffer(this, new float[samples.length]);
buffer.setDebugName("Owner:" + getDebugName());
incrementBufferCount();
}
buffer.reloadFrom(samples, timestamp);
buffer.incrementUserCount();
return buffer;
}
}

View File

@@ -1,97 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import org.apache.commons.lang3.Validate;
public class ReusableByteBuffer extends AbstractReusableBuffer
{
private byte[] mSamples;
/**
* Creates a reusable, timestamped byte buffer using the specified time in milliseconds.
*
* NOTE: reusability of this buffer requires strict user count tracking. Each component that receives this
* buffer should not modify the buffer contents. Each component should also increment the user count before
* sending this buffer to another component and should decrement the user count when finished using this buffer.
*
* @param bufferDisposedListener to be notified when all consumers/users are finished using the buffer
* @param bytes of data
* @param timestamp in millis for the buffer
*/
ReusableByteBuffer(IReusableBufferDisposedListener bufferDisposedListener, byte[] bytes, long timestamp)
{
super(bufferDisposedListener, timestamp);
mSamples = bytes;
Validate.notNull(bufferDisposedListener, "Reusable Byte Buffer Listener cannot be null");
}
/**
* Constructs a timestamped byte buffer using the current system time in milliseconds.
*
* NOTE: reusability of this buffer requires strict user count tracking. Each component that receives this
* buffer should not modify the buffer contents. Each component should also increment the user count before
* sending this buffer to another component and should decrement the user count when finished using this buffer.
*
* @param bufferDisposedListener to be notified when all consumers are finished using the buffer
* @param bytes of data
*/
public ReusableByteBuffer(IReusableBufferDisposedListener bufferDisposedListener, byte[] bytes)
{
this(bufferDisposedListener, bytes, System.currentTimeMillis());
}
/**
* Samples for this buffer
*/
public byte[] getBytes()
{
return mSamples;
}
/**
* Creates a copy of the samples from this buffer
*/
public byte[] getSamplesCopy()
{
byte[] bytes = getBytes();
byte[] copy = new byte[bytes.length];
System.arraycopy(bytes, 0, copy, 0, bytes.length);
return copy;
}
/**
* Number of bytes contained in this buffer
*/
public int getSampleCount()
{
return getBytes().length;
}
/**
* Resizes the internal array to the size argument
*
* @param size for the internal array
*/
protected void resize(int size)
{
if(mSamples.length != size)
{
mSamples = new byte[size];
}
}
}

View File

@@ -1,54 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReusableByteBufferQueue extends AbstractReusableBufferQueue<ReusableByteBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(ReusableByteBufferQueue.class);
public ReusableByteBufferQueue(String debugName)
{
super(debugName);
}
/**
* Returns a reusable buffer from an internal recycling queue, or creates a new buffer if there are currently no
* buffers available for reuse. The user count is set to one and must be managed throughout the lifecycle
* of the buffer.
*
* @param size of the samples that will be loaded into the buffer
* @return a reusable buffer
*/
public ReusableByteBuffer getBuffer(int size)
{
ReusableByteBuffer buffer = getRecycledBuffer();
if(buffer == null)
{
buffer = new ReusableByteBuffer(this, new byte[size]);
buffer.setDebugName("Owner:" + getDebugName());
incrementBufferCount();
}
buffer.resize(size);
buffer.incrementUserCount();
return buffer;
}
}

View File

@@ -1,164 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.LinkedList;
import java.util.List;
public class ReusableChannelResultsBuffer extends AbstractReusableBuffer
{
private final static Logger mLog = LoggerFactory.getLogger(ReusableChannelResultsBuffer.class);
private ReusableComplexBufferQueue mReusableComplexBufferQueue =
new ReusableComplexBufferQueue("ReusableChannelResultsBuffer");
private LinkedList<float[]> mEmptyBuffers = new LinkedList<>();
private LinkedList<float[]> mFilledBuffers = new LinkedList<>();
private Integer mLength;
/**
* Reusable buffer for storing polyphase channelizer results arrays. This buffer is NOT thread safe and is
* designed to be used by only one consumer. The consumer must invoke the prepareForRecycle() method to indicate that this
* buffer can be reused.
*
* @param disposedListener to receive notification when this buffer is ready for prepareForRecycle
*/
ReusableChannelResultsBuffer(IReusableBufferDisposedListener disposedListener)
{
super(disposedListener);
}
/**
* Recycles this buffer for reuse.
*/
protected void prepareForRecycle()
{
mEmptyBuffers.addAll(mFilledBuffers);
mFilledBuffers.clear();
mLength = null;
}
/**
* Gets an empty channel results array correctly sized to the requested length. This method should be used to
* obtain all empty channel results buffers to fill with information and add back to this buffer so that the results
* arrays can be reused.
*
* @param length of the requested array
* @return a float array of the requested length that may contain stale data from previous use.
*/
public float[] getEmptyBuffer(int length)
{
float[] emptyBuffer = mEmptyBuffers.poll();
if(emptyBuffer == null || emptyBuffer.length != length)
{
emptyBuffer = new float[length];
}
return emptyBuffer;
}
/**
* Adds the channel results to this buffer. Note: you should only use channel results arrays obtained via the
* getEmptyBuffer() method and filled with data and then added back to this buffer via this method, so that array
* reuse can be accurately controlled.
*
* @param channelResults to add back to this buffer
*/
public void addChannelResults(float[] channelResults)
{
if(mLength == null)
{
mLength = channelResults.length;
}
else if(mLength != channelResults.length)
{
throw new IllegalArgumentException("Channel results length must be the same for all added results");
}
mFilledBuffers.offer(channelResults);
}
/**
* Channel results array contained in this buffer
*/
public List<float[]> getChannelResults()
{
return mFilledBuffers;
}
/**
* Extracts a single I/Q interleaved sample buffer from this buffer of channel results arrays
*
* @param iChannelIndex to extract for the inphase sample. The q index is assumed to be one greater than i.
* @return float[] containing interleaved I and Q samples
* @throws IllegalArgumentException if this buffer doesn't contain any samples or if the requested channel is
* not within the channel range of the contained channel results.
*/
public ReusableComplexBuffer getChannel(int iChannelIndex)
{
if(mFilledBuffers.isEmpty())
{
throw new IllegalArgumentException("Buffer is empty - cannot extract channel [" + iChannelIndex + "]");
}
if(!isValidChannelIndex(iChannelIndex))
{
throw new IllegalArgumentException("Channel [" + iChannelIndex + "] is not valid for the contained channel " +
"results -- max channel is " + mLength);
}
ReusableComplexBuffer channelBuffer = mReusableComplexBufferQueue.getBuffer(mFilledBuffers.size() * 2);
float[] samples = channelBuffer.getSamples();
int pointer = 0;
int qChannelIndex = iChannelIndex + 1;
for(float[] channelResults: mFilledBuffers)
{
try
{
samples[pointer] = channelResults[iChannelIndex];
pointer++;
samples[pointer] = channelResults[qChannelIndex];
pointer++;
}
catch(Exception e)
{
mLog.error("Error accessing channel results - iIndex:" + iChannelIndex +
" Results Size: " + mFilledBuffers.size() +
" Samples Length:" + samples.length +
" Pointer:" + pointer, e);
}
}
return channelBuffer;
}
/**
* Indicates if the channel argument is valid for the contained channel results.
*
* @param channelIndex number (0 to N) to check
* @return false if the buffer is empty or if the channel is greater than the contained channel results
*/
private boolean isValidChannelIndex(int channelIndex)
{
return (mLength != null) && (channelIndex <= mLength);
}
}

View File

@@ -1,55 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReusableChannelResultsBufferQueue extends AbstractReusableBufferQueue<ReusableChannelResultsBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(ReusableChannelResultsBufferQueue.class);
public ReusableChannelResultsBufferQueue(String debugName)
{
super(debugName);
}
public ReusableChannelResultsBufferQueue()
{
}
/**
* Returns a reusable buffer from an internal recycling queue, or creates a new buffer if there are currently no
* buffers available for reuse.
*
* @return a reusable buffer
*/
public ReusableChannelResultsBuffer getBuffer()
{
ReusableChannelResultsBuffer buffer = getRecycledBuffer();
if(buffer == null)
{
buffer = new ReusableChannelResultsBuffer(this);
buffer.setDebugName("Owner:" + getDebugName());
incrementBufferCount();
}
buffer.incrementUserCount();
return buffer;
}
}

View File

@@ -1,72 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
public class ReusableComplexBuffer extends ReusableFloatBuffer
{
/**
* Creates a reusable, timestamped complex buffer using the specified time in milliseconds.
*
* NOTE: reusability of this buffer requires strict user count tracking. Each component that receives this
* buffer should not modify the buffer contents. Each component should also increment the user count before
* sending this buffer to another component and should decrement the user count when finished using this buffer.
*
* @param disposalListener to be notified when all consumers/users are finished using the buffer
* @param samples of data
* @param timestamp in millis for the buffer
*/
ReusableComplexBuffer(IReusableBufferDisposedListener disposalListener, float[] samples, long timestamp)
{
super(disposalListener, samples, timestamp);
}
/**
* Constructs a timestamped complex buffer using the current system time in milliseconds.
*
* NOTE: reusability of this buffer requires strict user count tracking. Each component that receives this
* buffer should not modify the buffer contents. Each component should also increment the user count before
* sending this buffer to another component and should decrement the user count when finished using this buffer.
*
* @param disposalListener to be notified when all consumers are finished using the buffer
* @param samples of data
*/
protected ReusableComplexBuffer(IReusableBufferDisposedListener disposalListener, float[] samples)
{
this(disposalListener, samples, System.currentTimeMillis());
}
/**
* Number of complex samples contained in this buffer
*/
@Override
public int getSampleCount()
{
return getSamples().length / 2;
}
/**
* Applies the gain value to the samples contained in this buffer
*/
public void applyGain(double gain)
{
float[] samples = getSamples();
for(int x = 0; x < samples.length; x++)
{
samples[x] *= gain;
}
}
}

View File

@@ -1,174 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* *****************************************************************************
*/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.dsp.filter.channelizer.SampleTimestampManager;
import io.github.dsheirer.sample.Listener;
import org.apache.commons.lang3.Validate;
import java.nio.FloatBuffer;
/**
* Assembles complex float samples into a ComplexBuffer containing an array of floats. Monitors incoming sample count
* to accurately assign a relative timestamp to each assembled buffer.
*/
public class ReusableComplexBufferAssembler
{
// private final static Logger mLog = LoggerFactory.getLogger(ReusableComplexBufferAssembler.class);
private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("ReusableComplexBufferAssembler");
private SampleTimestampManager mTimestampManager;
private FloatBuffer mBuffer;
private int mBufferSize;
private long mCurrentBufferTimestamp;
private Listener<ReusableComplexBuffer> mListener;
public ReusableComplexBufferAssembler(int bufferSize, double sampleRate)
{
Validate.isTrue(bufferSize % 2 == 0);
mBufferSize = bufferSize;
mBuffer = FloatBuffer.allocate(mBufferSize);
mTimestampManager = new SampleTimestampManager(sampleRate);
}
/**
* Updates the reference timestamp to use for all subsequent samples that are assembled. This timestamp should
* identify the relative timestamp of the first/next sample that is received by this assembler.
* @param timestamp to use relative to the incoming sample arrays.
*/
public void updateTimestamp(long timestamp)
{
mTimestampManager.setReferenceTimestamp(timestamp);
if(mBuffer.position() == 0)
{
mCurrentBufferTimestamp = timestamp;
}
}
public void dispose()
{
mReusableComplexBufferQueue.dispose();
}
/**
* Adds the samples to this assembler. As each buffer is assembled, it will be dispatched to the registered
* listener.
*
* @param inphase sample to assemble
* @param quadrature sample to assemble
*/
public void receive(float inphase, float quadrature)
{
if(mBuffer.remaining() >= 2)
{
mBuffer.put(inphase);
mBuffer.put(quadrature);
mTimestampManager.increment(1);
if(!mBuffer.hasRemaining())
{
flush();
}
}
}
/**
* Adds the samples to this assembler. As each buffer is assembled, it will be dispatched to the registered
* listener.
*
* @param samples to assemble
*/
public void receive(float[] samples)
{
if(mBuffer.remaining() >= samples.length)
{
mBuffer.put(samples);
mTimestampManager.increment(samples.length / 2);
if(!mBuffer.hasRemaining())
{
flush();
}
}
else
{
int offset = 0;
while(offset < samples.length)
{
int toCopy = mBuffer.remaining();
if((samples.length - offset) < toCopy)
{
toCopy = samples.length - offset;
}
mBuffer.put(samples, offset, toCopy);
offset += toCopy;
mTimestampManager.increment(toCopy / 2);
if(!mBuffer.hasRemaining())
{
flush();
}
}
}
}
/**
* Adds the samples to this assembler. As each buffer is assembled, it will be dispatched to the registered
* listener.
*/
public void receive(ReusableComplexBuffer samplesBuffer)
{
receive(samplesBuffer.getSamples());
samplesBuffer.decrementUserCount();
}
/**
* Flushes the current buffer contents to the registered listener
*/
public void flush()
{
Listener<ReusableComplexBuffer> listener = mListener;
if(listener != null && mBuffer.position() > 0)
{
mBuffer.rewind();
ReusableComplexBuffer reusableComplexBuffer = mReusableComplexBufferQueue.getBuffer(mBufferSize);
reusableComplexBuffer.reloadFrom(mBuffer, mCurrentBufferTimestamp);
listener.receive(reusableComplexBuffer);
}
mBuffer.rewind();
mCurrentBufferTimestamp = mTimestampManager.getCurrentTimestamp();
}
/**
* Sets the listener to receive the output from this buffer assembler. Note: the listener is responsible for
* managing the user count for any buffers received from this buffer assembler.
*/
public void setListener(Listener<ReusableComplexBuffer> listener)
{
mListener = listener;
}
}

View File

@@ -1,94 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReusableComplexBufferQueue extends AbstractReusableBufferQueue<ReusableComplexBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(ReusableComplexBufferQueue.class);
/**
* Queue for managing the creation and reuse of reusable complex sample buffers.
* @param debugName to associate with this queue instance
*/
public ReusableComplexBufferQueue(String debugName)
{
super(debugName);
}
/**
* Queue for managing the creation and reuse of reusable complex sample buffers.
*/
public ReusableComplexBufferQueue()
{
}
/**
* Returns a reusable buffer from an internal recycling queue, or creates a new buffer if there are currently no
* buffers available for reuse.
*
* @param size of the samples that will be loaded into the buffer
* @return a reusable buffer
*/
public ReusableComplexBuffer getBuffer(int size)
{
ReusableComplexBuffer buffer = getRecycledBuffer();
if(buffer == null)
{
buffer = new ReusableComplexBuffer(this, new float[size]);
buffer.setDebugName("Owner:" + getDebugName());
incrementBufferCount();
}
buffer.resize(size);
buffer.incrementUserCount();
return buffer;
}
/**
* Returns a buffer loaded with the samples.
* @param samples to load
* @param timestamp to apply
* @return buffer
*/
public ReusableComplexBuffer getBuffer(float[] samples, long timestamp)
{
ReusableComplexBuffer buffer = getRecycledBuffer();
if(buffer == null)
{
buffer = new ReusableComplexBuffer(this, samples);
buffer.setTimestamp(timestamp);
buffer.setDebugName("Owner:" + getDebugName());
incrementBufferCount();
}
else
{
buffer.reloadFrom(samples, timestamp);
}
buffer.incrementUserCount();
return buffer;
}
}

View File

@@ -1,268 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedTransferQueue;
/**
* Delay buffer/queue for reusable complex buffers with support for pre-loading new buffer listeners (ie channels)
* with delayed sample buffers and broadcasting of incoming sample buffers to a list of listeners.
*
* This class is designed to compensate/account for any delays in processing of a control channel and subsequent
* channel grants so that when a new channel is allocated, that channel can be pre-loaded with time delayed sample
* buffers that start as close as possible to the channel grant timestamp, in order to avoid cutting off the
* beginning of a channel grant transmission.
*
* New channel listeners are enqueued with a request timestamp and as new buffers arrive, the new channel listeners
* are preloaded with delayed buffers from the queue and then added to an internal broadcaster so that they
* receive all subsequent buffers as normal.
*
* This class is designed to be controlled by the sample producer thread for adding new listeners and pre-loading
* delayed buffers. Any listeners that are added to this class are expected to implement a non-blocking receive method
* so as not to delay the stream of sample buffers. Channel listeners are expected to implement buffer queue processing
* on another thread.
*/
public class ReusableComplexDelayBuffer implements Listener<ReusableComplexBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(ReusableComplexDelayBuffer.class);
private ReusableBufferBroadcaster<ReusableComplexBuffer> mBroadcaster = new ReusableBufferBroadcaster<>();
private LinkedTransferQueue<ActionRequest> mActionRequestQueue = new LinkedTransferQueue<>();
private ActionRequest mActionRequest;
private ReusableComplexBuffer[] mDelayBuffer;
private int mDelayBufferPointer = 0;
private long mBufferDuration;
/**
* Creates a new delay buffer with the specified delay length and the buffer sample rate.
*
* @param size of the delay queue
* @param bufferDuration in milliseconds for each complex buffer processed by this instance
*/
public ReusableComplexDelayBuffer(int size, long bufferDuration)
{
mDelayBuffer = new ReusableComplexBuffer[size];
mBufferDuration = bufferDuration;
}
/**
* Prepares this instance for disposal by releasing all stored sample buffers.
*/
public void dispose()
{
clearBuffer();
}
public void clear()
{
//Submit a clear buffer request to be processed upon the next buffer that arrives
mActionRequestQueue.offer(new ActionRequest());
}
/**
* Clears any delayed/enqueued sample buffers.
*/
private void clearBuffer()
{
for(int x = 0; x < mDelayBuffer.length; x++)
{
if(mDelayBuffer[x] != null)
{
mDelayBuffer[x].decrementUserCount();
mDelayBuffer[x] = null;
}
}
mDelayBufferPointer = 0;
}
@Override
public synchronized void receive(ReusableComplexBuffer reusableComplexBuffer)
{
mActionRequest = mActionRequestQueue.poll();
while(mActionRequest != null)
{
switch(mActionRequest.getAction())
{
case ADD_USER:
processNewListener(mActionRequest);
break;
case REMOVE_USER:
mBroadcaster.removeListener(mActionRequest.getListener());
break;
case CLEAR_BUFFER:
clearBuffer();
break;
}
mActionRequest = mActionRequestQueue.poll();
}
//Increment user count before we hand it to the broadcaster so that we can keep it as a copy for the
//delay buffer
reusableComplexBuffer.incrementUserCount();
mBroadcaster.receive(reusableComplexBuffer);
//Decrement the user count on the oldest buffer before we replace it in the delay queue
if(mDelayBuffer[mDelayBufferPointer] != null)
{
mDelayBuffer[mDelayBufferPointer].decrementUserCount();
}
//Store the new buffer in the delay queue and increment the pointer
mDelayBuffer[mDelayBufferPointer++] = reusableComplexBuffer;
//Wrap the delay buffer pointer as needed
if(mDelayBufferPointer >= mDelayBuffer.length)
{
mDelayBufferPointer = 0;
}
}
/**
* Indicates if any listeners are registered with this delay buffer
*/
public boolean hasListeners()
{
return mBroadcaster.hasListeners();
}
/**
* Processes any newly added listeners by checking all buffers in the delay queue and pre-loading the
* listeners with any buffers that occur on or after the listener's requested start timestamp.
*/
private void processNewListener(ActionRequest listenerToAdd)
{
ReusableComplexBuffer bufferToEvaluate;
int pointer = mDelayBufferPointer;
int preloadedBufferCount = 0;
for(int x = 0; x < mDelayBuffer.length; x++)
{
bufferToEvaluate = mDelayBuffer[pointer];
if(bufferToEvaluate != null &&
(bufferToEvaluate.getTimestamp() >= listenerToAdd.getTimestamp() ||
bufferToEvaluate.getTimestamp() + mBufferDuration >= listenerToAdd.getTimestamp()))
{
bufferToEvaluate.incrementUserCount();
listenerToAdd.getListener().receive(bufferToEvaluate);
preloadedBufferCount++;
}
pointer++;
if(pointer >= mDelayBuffer.length)
{
pointer = 0;
}
}
mBroadcaster.addListener(listenerToAdd.getListener());
}
/**
* Adds the listener to receive a copy of each buffer. The listener will be preloaded with delayed buffers
* that are at or after the specified timestamp.
*
* @param listener to add
* @param timestamp of the oldest sample buffers to preload to the listener
*/
public void addListener(Listener<ReusableComplexBuffer> listener, long timestamp)
{
mActionRequestQueue.add(new ActionRequest(listener, timestamp));
}
/**
* Removes the listener from receiving sample buffers
*/
public void removeListener(Listener<ReusableComplexBuffer> listener)
{
mActionRequestQueue.add(new ActionRequest(listener));
}
private enum Action{ADD_USER, REMOVE_USER, CLEAR_BUFFER};
/**
* Actions that must be completed on the incoming sample stream thread.
*/
public class ActionRequest
{
private Action mAction;
private Listener<ReusableComplexBuffer> mListener;
private long mTimestamp;
/**
* Creates an add listener request
* @param listener to add
* @param timestamp for the preloading buffers to the listener from the delay queue
*/
public ActionRequest(Listener<ReusableComplexBuffer> listener, long timestamp)
{
mAction = Action.ADD_USER;
mListener = listener;
mTimestamp = timestamp;
}
/**
* Creates a remove listener request
* @param listener
*/
public ActionRequest(Listener<ReusableComplexBuffer> listener)
{
mAction = Action.REMOVE_USER;
mListener = listener;
}
/**
* Creates a clearBuffer buffer request
*/
public ActionRequest()
{
mAction = Action.CLEAR_BUFFER;
}
public Action getAction()
{
return mAction;
}
/**
* Listener to be added/removed
*/
public Listener<ReusableComplexBuffer> getListener()
{
return mListener;
}
/**
* Requested timestamp for buffers to be preloaded.
*/
public long getTimestamp()
{
return mTimestamp;
}
}
}

View File

@@ -1,140 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
import org.apache.commons.lang3.Validate;
import java.nio.FloatBuffer;
public class ReusableFloatBuffer extends AbstractReusableBuffer
{
private float[] mSamples;
/**
* Creates a reusable, timestamped complex buffer using the specified time in milliseconds.
*
* NOTE: reusability of this buffer requires strict user count tracking. Each component that receives this
* buffer should not modify the buffer contents. Each component should also increment the user count before
* sending this buffer to another component and should decrement the user count when finished using this buffer.
*
* @param bufferDisposedListener to be notified when all consumers/users are finished using the buffer
* @param samples of data
* @param timestamp in millis for the buffer
*/
ReusableFloatBuffer(IReusableBufferDisposedListener bufferDisposedListener, float[] samples, long timestamp)
{
super(bufferDisposedListener, timestamp);
mSamples = samples;
Validate.notNull(bufferDisposedListener, "Reusable Buffer Listener cannot be null");
}
/**
* Samples for this buffer
*/
public float[] getSamples()
{
return mSamples;
}
/**
* Creates a copy of the samples from this buffer
*/
public float[] getSamplesCopy()
{
float[] samples = getSamples();
float[] copy = new float[samples.length];
System.arraycopy(samples, 0, copy, 0, samples.length);
return copy;
}
/**
* Number of samples contained in this buffer
*/
public int getSampleCount()
{
return getSamples().length;
}
/**
* Constructs a timestamped complex buffer using the current system time in milliseconds.
*
* NOTE: reusability of this buffer requires strict user count tracking. Each component that receives this
* buffer should not modify the buffer contents. Each component should also increment the user count before
* sending this buffer to another component and should decrement the user count when finished using this buffer.
*
* @param bufferDisposedListener to be notified when all consumers are finished using the buffer
* @param samples of data
*/
public ReusableFloatBuffer(IReusableBufferDisposedListener bufferDisposedListener, float[] samples)
{
this(bufferDisposedListener, samples, System.currentTimeMillis());
}
/**
* Reloads this buffer by copying the sample array into this buffer. Internal buffer length will be automatically
* resized to fit the new sample array length.
*
* This is convenient if the source of the sample array originates via a native buffer.
* @param samples to copy into this buffer
* @param timestamp for the samples in millis
*/
public void reloadFrom(float[] samples, long timestamp)
{
if(mUserCount.get() > 0)
{
throw new IllegalStateException("New data cannot be loaded into this reusable buffer while the user count " +
"is above zero - user count:" + mUserCount.get());
}
mUserCount.set(0);
resize(samples.length);
System.arraycopy(samples, 0, mSamples, 0, samples.length);
mTimestamp = timestamp;
}
/**
* Reloads this buffer by filling this buffer from the float buffer (ie copying the buffer contents). Internal
* buffer length will be automatically resized to accommodate the float buffer's capacity/size.
*
* @param floatBuffer to copy into this buffer
* @param timestamp for the samples in millis
*/
public void reloadFrom(FloatBuffer floatBuffer, long timestamp)
{
resize(floatBuffer.capacity());
floatBuffer.rewind();
floatBuffer.get(mSamples);
mTimestamp = timestamp;
}
/**
* Resizes the internal array to the size argument
* @param size for the internal array
*/
protected void resize(int size)
{
if(mSamples.length != size)
{
mSamples = new float[size];
}
}
}

View File

@@ -1,72 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package io.github.dsheirer.sample.complex;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.Provider;
import java.util.ArrayList;
import java.util.List;
public class ComplexSampleAssembler implements Listener<Complex>,
Provider<List<Complex>>
{
private ArrayList<Complex> mBuffer;
private int mBufferSize;
private Listener<List<Complex>> mListener;
public ComplexSampleAssembler( int bufferSize )
{
mBufferSize = bufferSize;
mBuffer = new ArrayList<>();
}
public void dispose()
{
mBuffer.clear();
mListener = null;
}
@Override
public void receive( Complex sample )
{
mBuffer.add( sample );
if( mBuffer.size() >= mBufferSize )
{
if( mListener != null )
{
mListener.receive( mBuffer );
}
mBuffer = new ArrayList<>();
}
}
@Override
public void setListener( Listener<List<Complex>> listener )
{
mListener = listener;
}
@Override
public void removeListener( Listener<List<Complex>> listener )
{
mListener = null;
}
}

View File

@@ -1,76 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package io.github.dsheirer.sample.complex;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Broadcasts received float samples to multiple listeners
*/
public class ComplexSampleBroadcaster implements ComplexSampleListener
{
private CopyOnWriteArrayList<ComplexSampleListener> mListeners =
new CopyOnWriteArrayList<ComplexSampleListener>();
@Override
public void receive( float i, float q )
{
broadcast( i, q );
}
/**
* Clear listeners to prepare for garbage collection
*/
public void dispose()
{
mListeners.clear();
}
public boolean hasListeners()
{
return !mListeners.isEmpty();
}
public int getListenerCount()
{
return mListeners.size();
}
public void addListener( ComplexSampleListener listener )
{
mListeners.add( listener );
}
public void removeListener( ComplexSampleListener listener )
{
mListeners.remove( listener );
}
public void clear()
{
mListeners.clear();
}
public void broadcast( float i, float q )
{
for( ComplexSampleListener listener: mListeners )
{
listener.receive( i, q );
}
}
}

View File

@@ -1,25 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package io.github.dsheirer.sample.complex;
import java.util.List;
public interface ComplexSampleListListener
{
public void receive( List<Complex> samples );
}

View File

@@ -1,51 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package io.github.dsheirer.sample.complex;
import org.apache.commons.math3.util.FastMath;
public class ComplexSampleUtils
{
public static Complex multiply( Complex a,
Complex b )
{
return new Complex(
( a.inphase() * b.inphase() ) - ( a.quadrature() * b.quadrature() ),
( ( a.quadrature() * b.inphase() ) + ( a.inphase() * b.quadrature() ) ) );
}
public static Complex minus( Complex a, Complex b )
{
return new Complex( a.inphase() - b.inphase(),
a.quadrature() - b.quadrature() );
}
public static double magnitude( Complex sample )
{
return FastMath.sqrt( magnitudeSquared( sample ) );
}
public static int magnitudeSquared( Complex sample )
{
return (int)( ( sample.inphase() * sample.inphase() ) +
( sample.quadrature() * sample.quadrature() ) );
}
}

View File

@@ -0,0 +1,44 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.complex;
/**
* Wrapper for a complex sample array where I and Q samples are in separate arrays.
*/
public record ComplexSamples(float[] i, float[] q)
{
/**
* Converts this non-interleaved complex samples to interleaved.
* @return interleaved samples.
*/
public InterleavedComplexSamples toInterleaved()
{
float[] interleaved = new float[i().length * 2];
for(int x = 0; x < i().length; x++)
{
interleaved[2 * x] = i()[x];
interleaved[2 * x + 1] = q()[x];
}
return new InterleavedComplexSamples(interleaved, System.currentTimeMillis());
}
}

View File

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

View File

@@ -13,11 +13,11 @@
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
package io.github.dsheirer.sample.complex;
import io.github.dsheirer.sample.Listener;
public interface IReusableBufferListener
public interface IComplexSamplesListener
{
Listener<ReusableFloatBuffer> getReusableBufferListener();
Listener<ComplexSamples> getComplexSamplesListener();
}

View File

@@ -13,21 +13,21 @@
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.buffer;
package io.github.dsheirer.sample.complex;
import io.github.dsheirer.sample.Listener;
public interface IReusableByteBufferProvider
public interface IComplexSamplesProvider
{
/**
* Adds the listener to receive complex buffer samples
*/
void setBufferListener(Listener<ReusableByteBuffer> listener);
void addBufferListener(Listener<ComplexSamples> listener);
/**
* Removes the listener from receiving complex buffer samples
*/
void removeBufferListener(Listener<ReusableByteBuffer> listener);
void removeBufferListener(Listener<ComplexSamples> listener);
/**
* Indicates if there are any complex buffer listeners registered

View File

@@ -0,0 +1,29 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.complex;
import io.github.dsheirer.sample.Listener;
/**
* Interface for listener of interleaved complex samples
*/
public interface IInterleavedComplexSamplesListener
{
Listener<InterleavedComplexSamples> getReusableComplexBufferListener();
}

View File

@@ -0,0 +1,42 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.complex;
import io.github.dsheirer.sample.Listener;
/**
* Interface for a provider of interleaved complex samples
*/
public interface IInterleavedComplexSamplesProvider
{
/**
* Adds the listener to receive interleaved complex samples
*/
void addBufferListener(Listener<InterleavedComplexSamples> listener);
/**
* Removes the listener from receiving interleaved complex samples
*/
void removeBufferListener(Listener<InterleavedComplexSamples> listener);
/**
* Indicates if there are any complex buffer listeners registered
*/
boolean hasBufferListeners();
}

View File

@@ -0,0 +1,46 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.complex;
import io.github.dsheirer.sample.buffer.ITimestamped;
/**
* Complex samples array that incorporates a timestamp for the first complex sample.
*/
public record InterleavedComplexSamples(float[] samples, long timestamp) implements ITimestamped
{
/**
* Converts this interleaved complex samples to de-interleaved.
* @return de-interleaved samples.
*/
public ComplexSamples toDeinterleaved()
{
float[] i = new float[samples().length / 2];
float[] q = new float[samples().length / 2];
for(int x = 0; x < i.length; x++)
{
i[x] = samples()[x / 2];
q[x] = samples()[x / 2 + 1];
}
return new ComplexSamples(i, q);
}
}

View File

@@ -1,33 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package io.github.dsheirer.sample.complex;
/**
* Poison Pill class to kill a processing thread. Where a thread accepts
* ComplexSample objects, send this class to the thread as a sentinal value
* to kill the processing thread.
*/
public class ThreadKiller extends Complex
{
private static final long serialVersionUID = 1L;
public ThreadKiller()
{
super( 0, 0 );
}
}

View File

@@ -1,75 +0,0 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.sample.decimator;
import io.github.dsheirer.sample.complex.ComplexSampleListener;
/**
* Decimator for complex samples
*/
public class ComplexDecimator implements ComplexSampleListener
{
private ComplexSampleListener mListener;
private int mCounter = 0;
private int mDecimationRate;
/**
* Constructs a new Decimator object with the specified decimation rate.
*/
public ComplexDecimator(int rate)
{
mDecimationRate = rate;
}
public void dispose()
{
mListener = null;
}
/**
* Sets a new decimation rate. Rate can be changed after this object
* is constructed.
*/
public synchronized void setDecimationRate(int rate)
{
mDecimationRate = rate;
}
/**
* Receives samples allowing only 1 of every (rate) sample to go on
* to the registered listener
*/
@Override
public void receive(float i, float q)
{
mCounter++;
if(mCounter >= mDecimationRate)
{
mListener.receive(i, q);
mCounter = 0;
}
}
/**
* Sets the decimated output listener
*/
public void setListener(ComplexSampleListener listener)
{
mListener = listener;
}
}

View File

@@ -1,72 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package io.github.dsheirer.sample.decimator;
import io.github.dsheirer.sample.Listener;
public class Decimator<T> implements Listener<T>
{
private int mDecimationFactor;
private int mDecimationPointer = 0;
private Listener<T> mListener;
/**
* Constructs the class to consume samples and decimate them by the
* DECIMATIONFACTOR.
*
* @param decimationFactor - number of samples to decimate. Provides one
* sample to registered listeners and discards the next decimationFactor - 1
* samples.
*/
public Decimator( int decimationFactor )
{
mDecimationFactor = decimationFactor;
}
/**
* Receives and broadcasts a sample and then discards the next
* decimationFactor -1 samples
*/
public void receive( T t )
{
if( mDecimationPointer == 0 )
{
if( mListener != null )
{
mListener.receive( t );
}
}
mDecimationPointer++;
//Reset the decimation pointer once it equals the decimation factor
if( mDecimationPointer == mDecimationFactor )
{
mDecimationPointer = 0;
}
}
/**
* Adds a sample listener to receive samples
*/
public void setListener( Listener<T> listener )
{
mListener = listener;
}
}

View File

@@ -1,58 +0,0 @@
package io.github.dsheirer.sample.decimator;
import io.github.dsheirer.sample.real.RealSampleListener;
public class RealDecimator implements RealSampleListener
{
private RealSampleListener mListener;
private int mCounter = 0;
private int mDecimationRate;
/**
* Constructs a new Decimator object with the specified decimation rate.
*/
public RealDecimator( int rate )
{
mDecimationRate = rate;
}
public void dispose()
{
mListener = null;
}
/**
* Sets a new decimation rate. Rate can be changed after this object
* is constructed.
*/
public synchronized void setDecimationRate( int rate )
{
mDecimationRate = rate;
}
/**
* Receives samples allowing only 1 of every (rate) sample to go on
* to the registered listener
*/
@Override
public void receive( float sample )
{
mCounter++;
if( mCounter >= mDecimationRate )
{
mListener.receive( sample );
mCounter = 0;
}
}
/**
* Sets the decimated output listener
*/
public void setListener( RealSampleListener listener )
{
mListener = listener;
}
}

View File

@@ -0,0 +1,26 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.real;
import io.github.dsheirer.sample.Listener;
public interface IRealBufferListener
{
Listener<float[]> getBufferListener();
}

View File

@@ -0,0 +1,34 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.sample.real;
import io.github.dsheirer.sample.Listener;
public interface IRealBufferProvider
{
/**
* Adds the listener to receive complex buffer samples
*/
void setBufferListener(Listener<float[]> listener);
/**
* Removes the listener from receiving complex buffer samples
*/
void removeBufferListener();
}

View File

@@ -1,76 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package io.github.dsheirer.sample.real;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Broadcasts a received float sample to multiple listeners
*/
public class RealSampleBroadcaster implements RealSampleListener
{
private CopyOnWriteArrayList<RealSampleListener> mListeners =
new CopyOnWriteArrayList<RealSampleListener>();
@Override
public void receive( float sample )
{
broadcast( sample );
}
/**
* Clear listeners to prepare for garbage collection
*/
public void dispose()
{
mListeners.clear();
}
public boolean hasListeners()
{
return !mListeners.isEmpty();
}
public int getListenerCount()
{
return mListeners.size();
}
public void addListener( RealSampleListener listener )
{
mListeners.add( listener );
}
public void removeListener( RealSampleListener listener )
{
mListeners.remove( listener );
}
public void clear()
{
mListeners.clear();
}
public void broadcast( float sample )
{
for( RealSampleListener listener: mListeners )
{
listener.receive( sample );
}
}
}

View File

@@ -1,8 +0,0 @@
package io.github.dsheirer.sample.real;
public interface RealSampleProvider
{
public void setListener( RealSampleListener listener );
public void removeListener( RealSampleListener listener );
}