mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
##2544 bladeRF supports up to 122 MHz sample rate. Adds new 2048 and 4096 half band decimation filters. (#2545)
Co-authored-by: denny <dennis.sheirer@gmail.com>
This commit is contained in:
@@ -23,7 +23,14 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sample rates offered for bladeRF tuners. Each is a multiple of the 25 kHz polyphase channel spacing.
|
||||
* Sample rates offered for bladeRF tuners, up to the 122.88 MHz maximum of the bladeRF 2.0. Use getRates() to get the
|
||||
* rates supported by a specific device, since the bladeRF 1.0, USB 2.0 connections and older FPGA images have lower
|
||||
* maximums.
|
||||
*
|
||||
* Rates above 61.44 MHz use the AD9361 oversample feature, which requires 8-bit samples.
|
||||
*
|
||||
* Each rate is a multiple of the 25 kHz polyphase channel spacing, except for the 61.44 and 122.88 MHz rates. The
|
||||
* channelizer uses slightly wider channels at those rates.
|
||||
*/
|
||||
public enum BladeRfSampleRate
|
||||
{
|
||||
@@ -36,7 +43,21 @@ public enum BladeRfSampleRate
|
||||
RATE_15_0(15_000_000, "15.00 MHz"),
|
||||
RATE_20_0(20_000_000, "20.00 MHz"),
|
||||
RATE_25_0(25_000_000, "25.00 MHz"),
|
||||
RATE_30_0(30_000_000, "30.00 MHz");
|
||||
RATE_30_0(30_000_000, "30.00 MHz"),
|
||||
RATE_35_0(35_000_000, "35.00 MHz"),
|
||||
RATE_40_0(40_000_000, "40.00 MHz"),
|
||||
RATE_45_0(45_000_000, "45.00 MHz"),
|
||||
RATE_50_0(50_000_000, "50.00 MHz"),
|
||||
RATE_55_0(55_000_000, "55.00 MHz"),
|
||||
RATE_60_0(60_000_000, "60.00 MHz"),
|
||||
RATE_61_44(61_440_000, "61.44 MHz"),
|
||||
RATE_70_0(70_000_000, "70.00 MHz (8-bit)"),
|
||||
RATE_80_0(80_000_000, "80.00 MHz (8-bit)"),
|
||||
RATE_90_0(90_000_000, "90.00 MHz (8-bit)"),
|
||||
RATE_100_0(100_000_000, "100.00 MHz (8-bit)"),
|
||||
RATE_110_0(110_000_000, "110.00 MHz (8-bit)"),
|
||||
RATE_120_0(120_000_000, "120.00 MHz (8-bit)"),
|
||||
RATE_122_88(122_880_000, "122.88 MHz (8-bit)");
|
||||
|
||||
public static final BladeRfSampleRate DEFAULT = RATE_10_0;
|
||||
private final int mRate;
|
||||
@@ -56,6 +77,14 @@ public enum BladeRfSampleRate
|
||||
return mRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this rate requires the oversample feature and 8-bit samples.
|
||||
*/
|
||||
public boolean isOversampled()
|
||||
{
|
||||
return mRate > RATE_61_44.mRate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
@@ -63,7 +92,7 @@ public enum BladeRfSampleRate
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample rates within the device maximum, which is the lower of the hardware limit and what the USB link sustains.
|
||||
* Sample rates within the device maximum, which is the lower of what the hardware, FPGA image and USB link support.
|
||||
* @param maximumRate in Hertz
|
||||
* @return rates, lowest to highest
|
||||
*/
|
||||
|
||||
@@ -30,6 +30,8 @@ import io.github.dsheirer.source.tuner.TunerClass;
|
||||
public class BladeRfTuner extends Tuner
|
||||
{
|
||||
private static final int BITS_PER_SAMPLE = 32;
|
||||
private static final double SAMPLE_SIZE_12_BIT = 12.0;
|
||||
private static final double SAMPLE_SIZE_8_BIT = 8.0;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
@@ -72,12 +74,13 @@ public class BladeRfTuner extends Tuner
|
||||
@Override
|
||||
public double getSampleSize()
|
||||
{
|
||||
return 12.0;
|
||||
BladeRfSampleRate rate = getController().getBladeRfSampleRate();
|
||||
return rate != null && rate.isOversampled() ? SAMPLE_SIZE_8_BIT : SAMPLE_SIZE_12_BIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumUSBBitsPerSecond()
|
||||
{
|
||||
return BladeRfSampleRate.RATE_30_0.getRate() * BITS_PER_SAMPLE;
|
||||
return BladeRfSampleRate.RATE_61_44.getRate() * BITS_PER_SAMPLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +28,17 @@ import io.github.dsheirer.bladerf.Channel;
|
||||
import io.github.dsheirer.bladerf.ChannelLayout;
|
||||
import io.github.dsheirer.bladerf.DeviceInfo;
|
||||
import io.github.dsheirer.bladerf.DeviceSpeed;
|
||||
import io.github.dsheirer.bladerf.Feature;
|
||||
import io.github.dsheirer.bladerf.GainMode;
|
||||
import io.github.dsheirer.bladerf.GainModeInfo;
|
||||
import io.github.dsheirer.bladerf.Range;
|
||||
import io.github.dsheirer.bladerf.SampleBuffer;
|
||||
import io.github.dsheirer.bladerf.SampleFormat;
|
||||
import io.github.dsheirer.bladerf.StreamConfig;
|
||||
import io.github.dsheirer.bladerf.Version;
|
||||
import io.github.dsheirer.buffer.FloatNativeBuffer;
|
||||
import io.github.dsheirer.buffer.INativeBuffer;
|
||||
import io.github.dsheirer.buffer.SignedByteNativeBuffer;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
||||
@@ -62,6 +65,12 @@ import org.usb4java.LibUsb;
|
||||
*
|
||||
* Only the first receive channel (RX1 connector) is used. The bladeRF 2.0 has a second receive channel, but it shares
|
||||
* the local oscillator and sample rate with the first, so it couldn't be tuned as an independent tuner.
|
||||
*
|
||||
* Sample rates above 61.44 MHz use the AD9361 oversample feature with 8-bit samples. These are only offered with FPGA
|
||||
* version 0.15.0 or later, because starting an 8-bit sample stream on an older FPGA crashes in libbladeRF.
|
||||
*
|
||||
* The device is reopened when changing between oversampled and normal sample rates. Disabling the oversample feature
|
||||
* doesn't fully restore the RFIC. The device reports the correct settings, but streams at the wrong rate or times out.
|
||||
*/
|
||||
public class BladeRfTunerController extends TunerController
|
||||
{
|
||||
@@ -75,10 +84,16 @@ public class BladeRfTunerController extends TunerController
|
||||
private static final Channel CHANNEL = Channel.RX0;
|
||||
private static final float SAMPLE_SCALE = 1.0f / 2048.0f;
|
||||
private static final SampleFormat SAMPLE_FORMAT = SampleFormat.SC16_Q11;
|
||||
private static final SampleFormat OVERSAMPLE_SAMPLE_FORMAT = SampleFormat.SC8_Q7;
|
||||
private static final int OVERSAMPLE_MINIMUM_FPGA_MAJOR = 0;
|
||||
private static final int OVERSAMPLE_MINIMUM_FPGA_MINOR = 15;
|
||||
private static final int BUFFER_SAMPLE_COUNT = 65536;
|
||||
private static final StreamConfig STREAM_CONFIG = new StreamConfig(32, BUFFER_SAMPLE_COUNT, 16);
|
||||
private static final StreamConfig OVERSAMPLE_STREAM_CONFIG = new StreamConfig(64, BUFFER_SAMPLE_COUNT, 32);
|
||||
private static final long USB2_MAXIMUM_BYTES_PER_SECOND = 32_000_000L;
|
||||
private static final double MAXIMUM_USABLE_BANDWIDTH_PERCENT = 0.90;
|
||||
//Measured with a carrier swept across the band at 122.88 MHz. The analog filter is 3 dB down at about +/- 33 MHz.
|
||||
private static final double OVERSAMPLE_USABLE_BANDWIDTH_HZ = 66_000_000.0;
|
||||
private static final long STREAM_CHECK_INTERVAL_SECONDS = 1;
|
||||
|
||||
private final int mBus;
|
||||
@@ -89,6 +104,7 @@ public class BladeRfTunerController extends TunerController
|
||||
private ScheduledFuture<?> mStreamCheck;
|
||||
private List<BladeRfSampleRate> mSampleRates = List.of();
|
||||
private BladeRfSampleRate mSampleRate;
|
||||
private boolean mOversampled;
|
||||
private List<GainModeInfo> mGainModes = List.of();
|
||||
private Range mGainRange;
|
||||
private long mMinimumTunableFrequency = MINIMUM_TUNABLE_FREQUENCY_HZ;
|
||||
@@ -136,6 +152,8 @@ public class BladeRfTunerController extends TunerController
|
||||
try
|
||||
{
|
||||
mDevice = BladeRf.open(findDevice());
|
||||
mSampleRate = null;
|
||||
mOversampled = false;
|
||||
|
||||
if(!mDevice.isFpgaConfigured())
|
||||
{
|
||||
@@ -270,12 +288,69 @@ public class BladeRfTunerController extends TunerController
|
||||
|
||||
if(device.speed() != DeviceSpeed.SUPER)
|
||||
{
|
||||
maximum = Math.min(maximum, USB2_MAXIMUM_BYTES_PER_SECOND / SAMPLE_FORMAT.bytesPerSample());
|
||||
return Math.min(maximum, USB2_MAXIMUM_BYTES_PER_SECOND / SAMPLE_FORMAT.bytesPerSample());
|
||||
}
|
||||
|
||||
if(isOversampleSupported(device.fpgaVersion()))
|
||||
{
|
||||
maximum = Math.max(maximum, getOversampleMaximumSampleRate(device));
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.info("bladeRF sample rates limited to [" + maximum + "] Hz - FPGA version [" + device.fpgaVersion() +
|
||||
"] doesn't support 8-bit samples. Upgrade to FPGA version 0.15.0 or later for higher sample rates.");
|
||||
}
|
||||
|
||||
return maximum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the FPGA version supports the 8-bit samples that are required for the oversample feature.
|
||||
* @param fpga version
|
||||
* @return true if supported
|
||||
*/
|
||||
static boolean isOversampleSupported(Version fpga)
|
||||
{
|
||||
return fpga.major() > OVERSAMPLE_MINIMUM_FPGA_MAJOR ||
|
||||
(fpga.major() == OVERSAMPLE_MINIMUM_FPGA_MAJOR && fpga.minor() >= OVERSAMPLE_MINIMUM_FPGA_MINOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Usable portion of an oversampled rate, which is limited by the analog filter that stays in the signal path.
|
||||
* @param rate that is oversampled
|
||||
* @return usable bandwidth percentage
|
||||
*/
|
||||
static double getOversampleUsableBandwidthPercentage(BladeRfSampleRate rate)
|
||||
{
|
||||
return Math.min(MAXIMUM_USABLE_BANDWIDTH_PERCENT, OVERSAMPLE_USABLE_BANDWIDTH_HZ / rate.getRate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Highest sample rate that the device reports while the oversample feature is enabled.
|
||||
* @return maximum rate, or zero if the device doesn't have the oversample feature, such as the bladeRF 1.0.
|
||||
*/
|
||||
private static long getOversampleMaximumSampleRate(BladeRfDevice device) throws BladeRfException
|
||||
{
|
||||
try
|
||||
{
|
||||
device.enableFeature(Feature.OVERSAMPLE, true);
|
||||
}
|
||||
catch(BladeRfException e)
|
||||
{
|
||||
mLog.info("bladeRF oversample feature isn't available - " + e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return device.sampleRateRange(CHANNEL).max();
|
||||
}
|
||||
finally
|
||||
{
|
||||
device.enableFeature(Feature.DEFAULT, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the libbladeRF description of the device at this controller's USB bus and port.
|
||||
*/
|
||||
@@ -450,11 +525,33 @@ public class BladeRfTunerController extends TunerController
|
||||
|
||||
try
|
||||
{
|
||||
if(rate.isOversampled() != mOversampled)
|
||||
{
|
||||
reopenDevice();
|
||||
|
||||
if(rate.isOversampled())
|
||||
{
|
||||
//Bandwidth has to be set first since it can't be reliably set with oversample enabled.
|
||||
mDevice.setBandwidth(CHANNEL, mDevice.bandwidthRange(CHANNEL).max());
|
||||
mDevice.enableFeature(Feature.OVERSAMPLE, true);
|
||||
mOversampled = true;
|
||||
}
|
||||
}
|
||||
|
||||
mDevice.setSampleRate(CHANNEL, rate.getRate());
|
||||
long requested = (long)(rate.getRate() * MAXIMUM_USABLE_BANDWIDTH_PERCENT);
|
||||
long bandwidth = mDevice.setBandwidth(CHANNEL, requested);
|
||||
setUsableBandwidthPercentage(Math.min(MAXIMUM_USABLE_BANDWIDTH_PERCENT,
|
||||
(double)bandwidth / rate.getRate()));
|
||||
|
||||
if(rate.isOversampled())
|
||||
{
|
||||
setUsableBandwidthPercentage(getOversampleUsableBandwidthPercentage(rate));
|
||||
}
|
||||
else
|
||||
{
|
||||
long requested = (long)(rate.getRate() * MAXIMUM_USABLE_BANDWIDTH_PERCENT);
|
||||
long bandwidth = mDevice.setBandwidth(CHANNEL, requested);
|
||||
setUsableBandwidthPercentage(Math.min(MAXIMUM_USABLE_BANDWIDTH_PERCENT,
|
||||
(double)bandwidth / rate.getRate()));
|
||||
}
|
||||
|
||||
mSampleRate = rate;
|
||||
mSamplesPerMillisecond = rate.getRate() / 1000.0f;
|
||||
mFrequencyController.setSampleRate(rate.getRate());
|
||||
@@ -465,7 +562,7 @@ public class BladeRfTunerController extends TunerController
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(streaming)
|
||||
if(streaming && mDevice != null)
|
||||
{
|
||||
startStream();
|
||||
}
|
||||
@@ -478,6 +575,33 @@ public class BladeRfTunerController extends TunerController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes and reopens the device and restores the settings that were reset, except for the sample rate and bandwidth
|
||||
* which the caller sets. The sample stream must be stopped.
|
||||
*/
|
||||
private void reopenDevice() throws SourceException, BladeRfException
|
||||
{
|
||||
long frequency = mFrequencyController.getTunedFrequency();
|
||||
BladeRfDevice device = mDevice;
|
||||
mDevice = null;
|
||||
mOversampled = false;
|
||||
closeDevice(device, false);
|
||||
|
||||
mDevice = BladeRf.open(findDevice());
|
||||
mDevice.setFrequency(CHANNEL, frequency);
|
||||
mDevice.setGainMode(CHANNEL, mGainMode);
|
||||
|
||||
if(mGainMode == GainMode.MANUAL)
|
||||
{
|
||||
mDevice.setGain(CHANNEL, mGain);
|
||||
}
|
||||
|
||||
if(mBiasT)
|
||||
{
|
||||
mDevice.setBiasTee(CHANNEL, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gain modes supported by the device.
|
||||
*/
|
||||
@@ -679,7 +803,9 @@ public class BladeRfTunerController extends TunerController
|
||||
{
|
||||
try
|
||||
{
|
||||
mStream = mDevice.startRx(ChannelLayout.RX_X1, SAMPLE_FORMAT, STREAM_CONFIG, this::receive);
|
||||
mStream = mOversampled ?
|
||||
mDevice.startRx(ChannelLayout.RX_X1, OVERSAMPLE_SAMPLE_FORMAT, OVERSAMPLE_STREAM_CONFIG, this::receive) :
|
||||
mDevice.startRx(ChannelLayout.RX_X1, SAMPLE_FORMAT, STREAM_CONFIG, this::receive);
|
||||
mStreamCheck = ThreadPool.SCHEDULED.scheduleAtFixedRate(this::checkStream, STREAM_CHECK_INTERVAL_SECONDS,
|
||||
STREAM_CHECK_INTERVAL_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
@@ -746,7 +872,16 @@ public class BladeRfTunerController extends TunerController
|
||||
{
|
||||
try
|
||||
{
|
||||
broadcast(new FloatNativeBuffer(scale(buffer.copyShorts()), System.currentTimeMillis(), mSamplesPerMillisecond));
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
if(buffer.format() == OVERSAMPLE_SAMPLE_FORMAT)
|
||||
{
|
||||
broadcast(new SignedByteNativeBuffer(buffer.copyBytes(), timestamp, 0.0f, 0.0f, mSamplesPerMillisecond));
|
||||
}
|
||||
else
|
||||
{
|
||||
broadcast(new FloatNativeBuffer(scale(buffer.copyShorts()), timestamp, mSamplesPerMillisecond));
|
||||
}
|
||||
}
|
||||
catch(RuntimeException e)
|
||||
{
|
||||
|
||||
@@ -90,7 +90,7 @@ public class BladeRfTunerEditor extends TunerEditor<BladeRfTuner, BladeRfTunerCo
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]", "[][][][][][][][][][grow]"));
|
||||
setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]", "[][][][][][][][][][][][grow]"));
|
||||
|
||||
add(new JLabel("Tuner:"));
|
||||
add(getTunerIdLabel());
|
||||
@@ -101,7 +101,7 @@ public class BladeRfTunerEditor extends TunerEditor<BladeRfTuner, BladeRfTunerCo
|
||||
|
||||
add(getButtonPanel(), "span,align left");
|
||||
|
||||
add(new JSeparator(), "span,growx,push");
|
||||
add(new JSeparator(), "span,growx");
|
||||
|
||||
add(new JLabel("Frequency (MHz):"));
|
||||
add(getFrequencyPanel(), "wrap");
|
||||
@@ -112,7 +112,7 @@ public class BladeRfTunerEditor extends TunerEditor<BladeRfTuner, BladeRfTunerCo
|
||||
add(new JLabel());
|
||||
add(getBiasTCheckBox(), "wrap");
|
||||
|
||||
add(new JSeparator(), "span,growx,push");
|
||||
add(new JSeparator(), "span,growx");
|
||||
add(new JLabel("Gain Control"), "wrap");
|
||||
|
||||
add(new JLabel("Mode:"));
|
||||
@@ -121,6 +121,9 @@ public class BladeRfTunerEditor extends TunerEditor<BladeRfTuner, BladeRfTunerCo
|
||||
add(new JLabel("Gain:"));
|
||||
add(getGainSlider());
|
||||
add(getGainValueLabel());
|
||||
|
||||
//Empty row that takes all extra vertical space so that the controls stay stacked at the top of the editor.
|
||||
add(new JLabel(), "span,pushy");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,6 +46,16 @@ public class HalfBandTunerChannelSource<T extends INativeBuffer> extends TunerCh
|
||||
//Maximum number of filled buffers for the blocking queue
|
||||
private static final int BUFFER_MAX_CAPACITY = 600;
|
||||
|
||||
//Decimation rate where a 2048 sample fragment produces too few samples for the consumer, so the decimated samples
|
||||
//are assembled into longer buffers. Decoders apply additional decimation that requires a buffer length that's a
|
||||
//multiple of their decimation.
|
||||
private static final int ASSEMBLED_OUTPUT_DECIMATION = 1024;
|
||||
private static final int ASSEMBLED_OUTPUT_LENGTH = 64;
|
||||
|
||||
//Decimation rate where sample fragments can be too short for the filter, which requires a buffer length that's a
|
||||
//multiple of the decimation rate, so the mixed samples are assembled before decimation.
|
||||
private static final int ASSEMBLED_INPUT_DECIMATION = 2048;
|
||||
|
||||
private final Dispatcher<T> mBufferDispatcher;
|
||||
private final ComplexMixer mFrequencyCorrectionMixer;
|
||||
private final IRealDecimationFilter mIDecimationFilter;
|
||||
@@ -59,6 +69,15 @@ public class HalfBandTunerChannelSource<T extends INativeBuffer> extends TunerCh
|
||||
private final long mFrequencyOffset;
|
||||
private volatile long mFrequencyCorrection;
|
||||
private final AtomicBoolean mPendingMixerUpdate = new AtomicBoolean();
|
||||
private final boolean mAssembleOutput;
|
||||
private final float[] mInputI;
|
||||
private final float[] mInputQ;
|
||||
private int mInputPointer;
|
||||
private long mInputTimestamp;
|
||||
private float[] mOutputI;
|
||||
private float[] mOutputQ;
|
||||
private int mOutputPointer;
|
||||
private long mOutputTimestamp;
|
||||
|
||||
/**
|
||||
* Constructs a frequency translating and CIC decimating channel source.
|
||||
@@ -87,6 +106,9 @@ public class HalfBandTunerChannelSource<T extends INativeBuffer> extends TunerCh
|
||||
|
||||
//The decimation filters copy the mixed samples, unless there's no decimation and the mixed samples are the output
|
||||
mReuseMixerOutput = decimation > 1;
|
||||
mAssembleOutput = decimation >= ASSEMBLED_OUTPUT_DECIMATION;
|
||||
mInputI = decimation >= ASSEMBLED_INPUT_DECIMATION ? new float[decimation] : null;
|
||||
mInputQ = decimation >= ASSEMBLED_INPUT_DECIMATION ? new float[decimation] : null;
|
||||
|
||||
//Set dispatcher to process 1/10 of estimated sample arrival rate, 20 times per second (up to 200% per interval)
|
||||
mBufferDispatcher = new Dispatcher(threadName, 50, getHeartbeatManager(), BUFFER_MAX_CAPACITY);
|
||||
@@ -242,23 +264,113 @@ public class HalfBandTunerChannelSource<T extends INativeBuffer> extends TunerCh
|
||||
}
|
||||
|
||||
ComplexSamples basebanded = mix(iterator.next());
|
||||
float[] i = mIDecimationFilter.decimateReal(basebanded.i());
|
||||
float[] q = mQDecimationFilter.decimateReal(basebanded.q());
|
||||
|
||||
try
|
||||
if(mInputI != null)
|
||||
{
|
||||
mSamplesListener.receive(new ComplexSamples(i, q, basebanded.timestamp()));
|
||||
assembleInput(basebanded);
|
||||
}
|
||||
catch(Throwable t)
|
||||
else
|
||||
{
|
||||
//The listener can be made null and cause the error - only log if we have a non-null listener
|
||||
if(mSamplesListener != null)
|
||||
{
|
||||
mLog.error("Error dispatching complex samples to listener [" + mSamplesListener + "]");
|
||||
}
|
||||
decimate(basebanded.i(), basebanded.q(), basebanded.timestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the mixed samples into buffers of the decimation rate length and decimates each full buffer.
|
||||
* @param samples that were mixed, which are copied since the mixer output arrays can be reused.
|
||||
*/
|
||||
private void assembleInput(ComplexSamples samples)
|
||||
{
|
||||
int pointer = 0;
|
||||
|
||||
while(pointer < samples.i().length)
|
||||
{
|
||||
if(mInputPointer == 0)
|
||||
{
|
||||
mInputTimestamp = samples.timestamp();
|
||||
}
|
||||
|
||||
int length = Math.min(samples.i().length - pointer, mInputI.length - mInputPointer);
|
||||
System.arraycopy(samples.i(), pointer, mInputI, mInputPointer, length);
|
||||
System.arraycopy(samples.q(), pointer, mInputQ, mInputPointer, length);
|
||||
pointer += length;
|
||||
mInputPointer += length;
|
||||
|
||||
if(mInputPointer == mInputI.length)
|
||||
{
|
||||
mInputPointer = 0;
|
||||
decimate(mInputI, mInputQ, mInputTimestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decimates the samples and sends them to the listener.
|
||||
*/
|
||||
private void decimate(float[] i, float[] q, long timestamp)
|
||||
{
|
||||
output(mIDecimationFilter.decimateReal(i), mQDecimationFilter.decimateReal(q), timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the decimated samples to the listener, assembling them into larger buffers at the maximum decimation.
|
||||
* @param i decimated samples
|
||||
* @param q decimated samples
|
||||
* @param timestamp of the first sample
|
||||
*/
|
||||
private void output(float[] i, float[] q, long timestamp)
|
||||
{
|
||||
if(!mAssembleOutput)
|
||||
{
|
||||
dispatch(new ComplexSamples(i, q, timestamp));
|
||||
return;
|
||||
}
|
||||
|
||||
int pointer = 0;
|
||||
|
||||
while(pointer < i.length)
|
||||
{
|
||||
if(mOutputPointer == 0)
|
||||
{
|
||||
//New arrays each time since the assembled arrays are passed to the consumer
|
||||
mOutputI = new float[ASSEMBLED_OUTPUT_LENGTH];
|
||||
mOutputQ = new float[ASSEMBLED_OUTPUT_LENGTH];
|
||||
mOutputTimestamp = timestamp;
|
||||
}
|
||||
|
||||
int length = Math.min(i.length - pointer, ASSEMBLED_OUTPUT_LENGTH - mOutputPointer);
|
||||
System.arraycopy(i, pointer, mOutputI, mOutputPointer, length);
|
||||
System.arraycopy(q, pointer, mOutputQ, mOutputPointer, length);
|
||||
pointer += length;
|
||||
mOutputPointer += length;
|
||||
|
||||
if(mOutputPointer == ASSEMBLED_OUTPUT_LENGTH)
|
||||
{
|
||||
mOutputPointer = 0;
|
||||
dispatch(new ComplexSamples(mOutputI, mOutputQ, mOutputTimestamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the samples to the listener.
|
||||
*/
|
||||
private void dispatch(ComplexSamples samples)
|
||||
{
|
||||
try
|
||||
{
|
||||
mSamplesListener.receive(samples);
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
//The listener can be made null and cause the error - only log if we have a non-null listener
|
||||
if(mSamplesListener != null)
|
||||
{
|
||||
mLog.error("Error dispatching complex samples to listener [" + mSamplesListener + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user