mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#39 WIP - adds support for timestamped complex buffers so that decoded
messages and events can be accurately timestamped, and more importantly, allocated traffic channels can request to be pre-loaded with buffered samples to ensure that traffic channels don't miss any samples that are currently missed due processing latency inherent in current buffer sizing. This should reduce the clipping of starting messages or voice on the traffic channels. Restructures processing path for tuner frequency and sample rate change events. The tuner now embeds these events in the sequence of buffers that are generated immediately after the event occurs since those are the buffers that will be impacted by the change. Previously, the change event would be broadcast throughout the system and would impact any buffered/cached buffers. This also reduces the number of source event change listeners that have to be managed. Note: this change is not yet implemented in all tuners ... only in the TestTuner implementation. Updates the polyphase channelizer to now buffer channel results and then dispatch the buffer to the channel output processors to improve efficiency of using float arrays versus single value complex float processing.
This commit is contained in:
@@ -197,12 +197,13 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
}
|
||||
|
||||
/**
|
||||
* Usable half bandwidth - total bandwidth minus unusable space at either end of the spectrum, minus the blocked
|
||||
* center bandwidth.
|
||||
* Usable half bandwidth - total bandwidth minus unusable space at either end of the spectrum.
|
||||
*
|
||||
* Note: this does not account for any DC spike protected frequency region at the center of the tuner
|
||||
*/
|
||||
public int getUsableHalfBandwidth()
|
||||
{
|
||||
return (int)(getUsableBandwidth() / 2) - mMiddleUnusableHalfBandwidth;
|
||||
return (int)(getUsableBandwidth() / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -77,10 +77,8 @@ public class TunerChannel implements Comparable<TunerChannel>
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Channel: ");
|
||||
sb.append(getMinFrequency());
|
||||
sb.append("-");
|
||||
sb.append(getMaxFrequency());
|
||||
sb.append("Channel:").append(getFrequency()).append(" (").append(getMinFrequency());
|
||||
sb.append("-").append(getMaxFrequency()).append(")");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
|
||||
|
||||
if(mProducerSourceEventListener != null)
|
||||
{
|
||||
mProducerSourceEventListener.receive(SourceEvent.startSampleStream(this));
|
||||
mProducerSourceEventListener.receive(SourceEvent.startSampleStreamRequest(this));
|
||||
}
|
||||
|
||||
mBufferProcessor.start();
|
||||
@@ -136,7 +136,7 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
|
||||
{
|
||||
if(mProducerSourceEventListener != null)
|
||||
{
|
||||
mProducerSourceEventListener.receive(SourceEvent.stopSampleStream(this));
|
||||
mProducerSourceEventListener.receive(SourceEvent.stopSampleStreamRequest(this));
|
||||
mProducerSourceEventListener.receive(SourceEvent.sourceDisposeRequest(this));
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ public class TunerSourceManager extends AbstractSourceManager
|
||||
public TunerSourceManager(TunerController tunerController)
|
||||
{
|
||||
mTunerController = tunerController;
|
||||
//Register to receive frequency and sample rate notifications
|
||||
mTunerController.addListener(this);
|
||||
|
||||
mPolyphaseChannelManager = new PolyphaseChannelManager(tunerController);
|
||||
//Register to receive channel count change notifications for rebroadcasting
|
||||
@@ -166,6 +164,13 @@ public class TunerSourceManager extends AbstractSourceManager
|
||||
return currentCenterFrequency;
|
||||
}
|
||||
|
||||
long channelSetBandwidth = getChannelSetBandwidth(channels);
|
||||
|
||||
if(channelSetBandwidth > mTunerController.getUsableBandwidth())
|
||||
{
|
||||
throw new IllegalArgumentException("Channel set bandwidth is greater than tuner's available bandwidth");
|
||||
}
|
||||
|
||||
long bestIntegralFrequency = getIntegralFrequency(channels);
|
||||
|
||||
//See if we can reuse the current frequency
|
||||
@@ -175,32 +180,42 @@ public class TunerSourceManager extends AbstractSourceManager
|
||||
return currentCenterFrequency;
|
||||
}
|
||||
|
||||
int integralHalfChannelCount = (int)(mTunerController.getUsableHalfBandwidth() /
|
||||
mPolyphaseChannelManager.getChannelBandwidth()) + 1;
|
||||
//Start by placing the first channel at the minimum location within the tuner bandwidth
|
||||
double usableHalfBandwidth = mTunerController.getUsableHalfBandwidth();
|
||||
double startFrequency = channels.first().getMinFrequency() + usableHalfBandwidth;
|
||||
|
||||
int integralHalfBandwidth = (int)(integralHalfChannelCount * mPolyphaseChannelManager.getChannelBandwidth());
|
||||
//Align the test frequency to the next greater integral frequency placing to place the first channel at the
|
||||
// minimum extreme of the available bandwidth as a starting location
|
||||
double delta = Math.abs(startFrequency - bestIntegralFrequency);
|
||||
double adjustment = delta % mPolyphaseChannelManager.getChannelBandwidth();
|
||||
|
||||
long minimumFrequency = channels.last().getFrequency() - integralHalfBandwidth;
|
||||
long testFrequency = channels.first().getFrequency() + integralHalfBandwidth;
|
||||
startFrequency += adjustment;
|
||||
|
||||
if(isValidCenterFrequency(channels,testFrequency))
|
||||
long availableTestBandwidth = mTunerController.getUsableBandwidth() - channelSetBandwidth;
|
||||
int availableTestChannels = (int)(availableTestBandwidth / mPolyphaseChannelManager.getChannelBandwidth()) + 1;
|
||||
|
||||
if(isValidCenterFrequency(channels,(long)startFrequency))
|
||||
{
|
||||
return testFrequency;
|
||||
return (long)startFrequency;
|
||||
}
|
||||
|
||||
while(testFrequency >= minimumFrequency)
|
||||
int currentChannel = 1;
|
||||
|
||||
while(currentChannel <= availableTestChannels)
|
||||
{
|
||||
testFrequency -= (long)mPolyphaseChannelManager.getChannelBandwidth();
|
||||
long testFrequency = (long)(startFrequency - (currentChannel * mPolyphaseChannelManager.getChannelBandwidth()));
|
||||
|
||||
if(isValidCenterFrequency(channels, testFrequency))
|
||||
{
|
||||
return testFrequency;
|
||||
}
|
||||
|
||||
currentChannel++;
|
||||
}
|
||||
|
||||
//If we can't find an integral center frequency, choose a frequency that will fit the channels
|
||||
minimumFrequency = channels.last().getFrequency() - mTunerController.getUsableHalfBandwidth();
|
||||
testFrequency = channels.first().getMinFrequency() + mTunerController.getUsableHalfBandwidth();
|
||||
long testFrequency = channels.first().getMinFrequency() + mTunerController.getUsableHalfBandwidth();
|
||||
long minimumFrequency = testFrequency - availableTestBandwidth;
|
||||
|
||||
if(isValidCenterFrequency(channels,testFrequency))
|
||||
{
|
||||
@@ -220,6 +235,11 @@ public class TunerSourceManager extends AbstractSourceManager
|
||||
throw new IllegalArgumentException("Can't calculate valid center frequency for the channel set");
|
||||
}
|
||||
|
||||
private long getChannelSetBandwidth(SortedSet<TunerChannel> channels)
|
||||
{
|
||||
return channels.last().getMaxFrequency() - channels.first().getMinFrequency();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the candidate center frequency is valid to ensure that all of the channels fit within the tuner's
|
||||
* bandwidth and avoid any center DC spike blocked frequency region
|
||||
@@ -406,12 +426,6 @@ public class TunerSourceManager extends AbstractSourceManager
|
||||
//Rebroadcast this event to any registered listeners (ie tuner and tuner controller)
|
||||
broadcast(sourceEvent);
|
||||
break;
|
||||
case NOTIFICATION_FREQUENCY_CHANGE:
|
||||
mPolyphaseChannelManager.process(sourceEvent);
|
||||
break;
|
||||
case NOTIFICATION_SAMPLE_RATE_CHANGE:
|
||||
mPolyphaseChannelManager.process(sourceEvent);
|
||||
break;
|
||||
default:
|
||||
mLog.info("Received an unrecognized source event: " + sourceEvent);
|
||||
break;
|
||||
|
||||
@@ -21,10 +21,14 @@ package io.github.dsheirer.source.tuner.test;
|
||||
import io.github.dsheirer.dsp.mixer.LowPhaseNoiseOscillator;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.ComplexBuffer;
|
||||
import io.github.dsheirer.sample.complex.TimestampedComplexBuffer;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -38,6 +42,7 @@ public class SampleGenerator
|
||||
private long mInterval;
|
||||
private int mSamplesPerInterval;
|
||||
private ScheduledFuture<?> mScheduledFuture;
|
||||
private Queue<SourceEvent> mSourceEventQueue = new ConcurrentLinkedQueue();
|
||||
|
||||
/**
|
||||
* Generates complex sample buffers at the specified sample rate with a unity gain tone at the specified
|
||||
@@ -79,6 +84,16 @@ public class SampleGenerator
|
||||
this(sampleRate, frequency, interval, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues the source event for inclusion in the complex buffers generated by this sample generator. Source events
|
||||
* are queued FIFO and embedded one at a time in the subsequent buffers produced by this generator.
|
||||
* @param sourceEvent to queue
|
||||
*/
|
||||
public void queue(SourceEvent sourceEvent)
|
||||
{
|
||||
mSourceEventQueue.offer(sourceEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the number of complex samples generated per interval
|
||||
*/
|
||||
@@ -170,7 +185,7 @@ public class SampleGenerator
|
||||
if(mListener != null)
|
||||
{
|
||||
float[] samples = mOscillator.generate(mSamplesPerInterval);
|
||||
mListener.receive(new ComplexBuffer(samples));
|
||||
mListener.receive(new TimestampedComplexBuffer(samples, mSourceEventQueue.poll()));
|
||||
|
||||
if(mSweepUpdateInterval != 0)
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ public class TestTuner extends Tuner
|
||||
*/
|
||||
public TestTunerController getTunerController()
|
||||
{
|
||||
return (TestTunerController)getTunerController();
|
||||
return (TestTunerController)super.getTunerController();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,6 +20,7 @@ package io.github.dsheirer.source.tuner.test;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.ComplexBuffer;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.TunerController;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
@@ -33,9 +34,9 @@ public class TestTunerController extends TunerController
|
||||
public static final long MINIMUM_FREQUENCY = 1000000l; //1 MHz
|
||||
public static final long MAXIMUM_FREQUENCY = 10000000000l; //10 GHz
|
||||
public static final long DEFAULT_CENTER_FREQUENCY = 100000000; //100 MHz
|
||||
public static final int DEFAULT_SAMPLE_RATE = 50000; //25 kHz
|
||||
public static final int DC_NOISE_BANDWIDTH = 5000; // +/-5 kHz
|
||||
public static final double USABLE_BANDWIDTH = 0.90; //90% usable bandwidth - 5% unusable on either end
|
||||
public static final int DEFAULT_SAMPLE_RATE = 100000; //25 kHz
|
||||
public static final int DC_NOISE_BANDWIDTH = 0; // +/-5 kHz
|
||||
public static final double USABLE_BANDWIDTH = 1.00; //100% usable bandwidth
|
||||
public static final int SPECTRAL_FRAME_RATE = 20;
|
||||
public static final long SAMPLE_GENERATION_INTERVAL = 1000 / SPECTRAL_FRAME_RATE;
|
||||
|
||||
@@ -129,6 +130,7 @@ public class TestTunerController extends TunerController
|
||||
{
|
||||
long frequency = toneFrequency - mReferenceFrequency;
|
||||
mSampleGenerator.setFrequency(frequency);
|
||||
mSampleGenerator.queue(SourceEvent.frequencyChange(frequency));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,5 +157,6 @@ public class TestTunerController extends TunerController
|
||||
public void setSampleRate(int sampleRate) throws SourceException
|
||||
{
|
||||
mFrequencyController.setSampleRate(sampleRate);
|
||||
mSampleGenerator.queue(SourceEvent.sampleRateChange(sampleRate));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user