mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#1563 Updates RSP tuner to produce sample buffers with optimal power-of-2 length to enable downstream vectorized operations. (#1583)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -262,7 +262,14 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
|
||||
{
|
||||
if(mSampleDelayBuffer == null)
|
||||
{
|
||||
int delayBufferSize = (int)(DELAY_BUFFER_DURATION_MILLISECONDS / mTunerController.getBufferDuration());
|
||||
long bufferDuration = mTunerController.getBufferDuration();
|
||||
|
||||
if(bufferDuration <= 0)
|
||||
{
|
||||
bufferDuration = 1;
|
||||
}
|
||||
|
||||
int delayBufferSize = (int)(DELAY_BUFFER_DURATION_MILLISECONDS / bufferDuration);
|
||||
mSampleDelayBuffer = new NativeSampleDelayBuffer(delayBufferSize, mTunerController.getBufferDuration());
|
||||
mTunerController.addBufferListener(mSampleDelayBuffer);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -23,8 +23,6 @@ import io.github.dsheirer.buffer.AbstractNativeBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import java.util.Iterator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Native buffer implementation for RSP tuner I/Q sample buffers.
|
||||
@@ -33,7 +31,6 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
public class RspNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(RspNativeBuffer.class);
|
||||
private static final float SAMPLE_TO_FLOAT = 1.0f / 32768.0f;
|
||||
private short[] mISamples;
|
||||
private short[] mQSamples;
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.source.tuner.sdrplay;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Native buffer factory for SDRPlay RSP tuners.
|
||||
*
|
||||
* The SDRPlay API automatically changes the length of sample buffers according to the sample rate. This causes
|
||||
* problems for down-stream processing components that may be optimized for vector operations and depend on the
|
||||
* sample arrays being a power-of-2 length. This class repackages the incoming sample stream into arrays of
|
||||
* power-of-2 length.
|
||||
*/
|
||||
public class RspNativeBufferFactory
|
||||
{
|
||||
private RspSampleRate mRspSampleRate;
|
||||
private short[] mIResidual = new short[0];
|
||||
private short[] mQResidual = new short[0];
|
||||
private long mResidualTimestamp = System.currentTimeMillis();
|
||||
private int mIncomingBufferLength = 0;
|
||||
private int mOptimalBufferLength = 128;
|
||||
private float mSamplesPerMillisecond;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
* @param sampleRate of the tuner
|
||||
*/
|
||||
public RspNativeBufferFactory(RspSampleRate sampleRate)
|
||||
{
|
||||
setSampleRate(sampleRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or update the sample rate.
|
||||
* @param sampleRate to set.
|
||||
*/
|
||||
public void setSampleRate(RspSampleRate sampleRate)
|
||||
{
|
||||
mRspSampleRate = sampleRate;
|
||||
mSamplesPerMillisecond = sampleRate.getSamplesPerMillisecond();
|
||||
}
|
||||
|
||||
/**
|
||||
* Repackages the samples into optimal length buffers and returns zero or more RSP native buffers.
|
||||
* @param i samples
|
||||
* @param q samples
|
||||
* @param timestamp of samples
|
||||
* @return zero or more repackaged RSP native buffers
|
||||
*/
|
||||
public List<RspNativeBuffer> get(short[] i, short[] q, long timestamp)
|
||||
{
|
||||
updateBufferLength(i.length);
|
||||
|
||||
short[] iCombined = new short[mIResidual.length + i.length];
|
||||
System.arraycopy(mIResidual, 0, iCombined, 0, mIResidual.length);
|
||||
System.arraycopy(i, 0, iCombined, mIResidual.length, i.length);
|
||||
|
||||
short[] qCombined = new short[mQResidual.length + q.length];
|
||||
System.arraycopy(mQResidual, 0, qCombined, 0, mQResidual.length);
|
||||
System.arraycopy(q, 0, qCombined, mQResidual.length, q.length);
|
||||
|
||||
if(iCombined.length < mOptimalBufferLength)
|
||||
{
|
||||
mIResidual = iCombined;
|
||||
mQResidual = qCombined;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<RspNativeBuffer> buffers = new ArrayList<>();
|
||||
|
||||
while(iCombined.length >= mOptimalBufferLength)
|
||||
{
|
||||
short[] iOptimal = Arrays.copyOf(iCombined, mOptimalBufferLength);
|
||||
iCombined = Arrays.copyOfRange(iCombined, mOptimalBufferLength, iCombined.length);
|
||||
|
||||
short[] qOptimal = Arrays.copyOf(qCombined, mOptimalBufferLength);
|
||||
qCombined = Arrays.copyOfRange(qCombined, mOptimalBufferLength, qCombined.length);
|
||||
|
||||
RspNativeBuffer buffer = new RspNativeBuffer(iOptimal, qOptimal, mResidualTimestamp, mSamplesPerMillisecond);
|
||||
buffers.add(buffer);
|
||||
mResidualTimestamp += (long)(mOptimalBufferLength / mSamplesPerMillisecond);
|
||||
}
|
||||
|
||||
mIResidual = iCombined;
|
||||
mQResidual = qCombined;
|
||||
|
||||
//For simplicity, just update the residual timestamp to be the timestamp for this latest update
|
||||
mResidualTimestamp = timestamp;
|
||||
|
||||
return buffers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the optimal native buffer length based on the incoming buffer size. Optimal length is a power-of-2
|
||||
* value that is closest to the buffer length to minimize the quantity of residual samples from each arriving buffer.
|
||||
* @param length
|
||||
*/
|
||||
private void updateBufferLength(int length)
|
||||
{
|
||||
if(mIncomingBufferLength != length)
|
||||
{
|
||||
int optimal = 128;
|
||||
|
||||
while((optimal * 2) < length)
|
||||
{
|
||||
optimal *= 2;
|
||||
}
|
||||
|
||||
mOptimalBufferLength = optimal;
|
||||
mIncomingBufferLength = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,6 +156,15 @@ public enum RspSampleRate
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of samples per millisecond.
|
||||
* @return samples per millisecond.
|
||||
*/
|
||||
public float getSamplesPerMillisecond()
|
||||
{
|
||||
return getEffectiveSampleRate() / 1000.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bandwidth entry
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,7 @@ import io.github.dsheirer.source.tuner.sdrplay.api.parameter.event.EventType;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.event.GainCallbackParameters;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.event.PowerOverloadCallbackParameters;
|
||||
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.event.RspDuoModeCallbackParameters;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -48,6 +49,7 @@ public abstract class RspTunerController<I extends IControlRsp> extends TunerCon
|
||||
protected static final long MAXIMUM_FREQUENCY = 2_000_000_000;
|
||||
protected static final int MIDDLE_UNUSABLE_BANDWIDTH = 0;
|
||||
private I mControlRsp;
|
||||
private RspNativeBufferFactory mNativeBufferFactory = new RspNativeBufferFactory(RspSampleRate.RATE_8_000);
|
||||
|
||||
/**
|
||||
* Abstract tuner controller class. The tuner controller manages frequency bandwidth and currently tuned channels
|
||||
@@ -116,9 +118,15 @@ public abstract class RspTunerController<I extends IControlRsp> extends TunerCon
|
||||
public void processStream(short[] inphase, short[] quadrature,
|
||||
StreamCallbackParameters parameters, boolean reset)
|
||||
{
|
||||
//RSP I/Q sample buffers are small and don't currently get fragmented by the RspNativeBuffer, so we don't
|
||||
//calculate the sub-buffer samples per millisecond value -- just use a constant value of 0.
|
||||
mNativeBufferBroadcaster.broadcast(new RspNativeBuffer(inphase, quadrature, System.currentTimeMillis(), 0.0f));
|
||||
//The native buffer factory repackages the samples into buffers with the largest power-of-2 length that is
|
||||
//smaller than the incoming buffer length and no smaller than 128 to ensure that down-stream vector optimized
|
||||
//functions can process the data.
|
||||
List<RspNativeBuffer> buffers = mNativeBufferFactory.get(inphase, quadrature, System.currentTimeMillis());
|
||||
|
||||
for(RspNativeBuffer buffer: buffers)
|
||||
{
|
||||
mNativeBufferBroadcaster.broadcast(buffer);
|
||||
}
|
||||
|
||||
if(reset)
|
||||
{
|
||||
@@ -272,6 +280,7 @@ public abstract class RspTunerController<I extends IControlRsp> extends TunerCon
|
||||
|
||||
//Update the usable bandwidth based on the sample rate and filtered bandwidth
|
||||
setUsableBandwidthPercentage(rspSampleRate.getUsableBandwidth());
|
||||
mNativeBufferFactory.setSampleRate(rspSampleRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user