Implements a complex sample delay buffer integrated into the tuner

channel manager so that channel source requests can have a start
timestamp specified and the channel can be preloaded with samples from
the delay buffer.  If no timestamp is specified, current time is assumed.

This feature requires corresponding updates to any decoder that wishes
to implement the timestamped sample preloading functionality.
This commit is contained in:
Denny
2018-09-09 08:46:21 -04:00
parent bb0bcd3198
commit 570321da3e
21 changed files with 632 additions and 98 deletions

View File

@@ -17,6 +17,7 @@ package io.github.dsheirer.source.tuner.manager;
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableComplexDelayBuffer;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.TunerController;
@@ -38,12 +39,14 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
{
private final static Logger mLog = LoggerFactory.getLogger(HeterodyneChannelSourceManager.class);
private final static int OBJECTIVE_CHANNEL_SAMPLE_RATE = 30000;
private final static int OBJECTIVE_CHANNEL_SAMPLE_RATE = 25000;
private final static int DELAY_BUFFER_DURATION_MILLISECONDS = 2000;
private List<CICTunerChannelSource> mChannelSources = new CopyOnWriteArrayList<>();
private SortedSet<TunerChannel> mTunerChannels = new TreeSet<>();
private TunerController mTunerController;
private ChannelSourceEventProcessor mChannelSourceEventProcessor = new ChannelSourceEventProcessor();
private ReusableComplexDelayBuffer mSampleDelayBuffer;
public HeterodyneChannelSourceManager(TunerController tunerController)
{
@@ -136,6 +139,12 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
case NOTIFICATION_FREQUENCY_CHANGE:
//Tuner center frequency has changed - update channels
updateTunerFrequency(tunerSourceEvent.getValue().longValue());
//Clear the delay buffer since any delayed samples will be centered on the previous frequency
if(mSampleDelayBuffer != null)
{
mSampleDelayBuffer.clear();
}
break;
case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE:
//The tuner is self-correcting for PPM error - relay to channels
@@ -182,6 +191,39 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
}
}
/**
* Creates a complex sample delay buffer and registers it with the tuner controller to start the flow
* of complex sample buffers from the tuner.
*/
private void startDelayBuffer()
{
if(mSampleDelayBuffer == null)
{
int delayBufferSize = (int)(DELAY_BUFFER_DURATION_MILLISECONDS / mTunerController.getBufferDuration());
mSampleDelayBuffer = new ReusableComplexDelayBuffer(delayBufferSize, mTunerController.getBufferDuration());
mLog.debug("Created/registered complex sample delay buffer of size [" + delayBufferSize +
"] buffers and delay duration [" + DELAY_BUFFER_DURATION_MILLISECONDS +
"ms] for tuner controller [" + mTunerController.getClass().getName() +
"] with buffer duration [" + mTunerController.getBufferDuration() + "]");
mTunerController.addBufferListener(mSampleDelayBuffer);
}
}
/**
* Deregisters the complex sample delay buffer and disposes of any queued reusable buffers.
*/
private void stopDelayBuffer()
{
if(mSampleDelayBuffer != null && !mSampleDelayBuffer.hasListeners())
{
mTunerController.removeBufferListener(mSampleDelayBuffer);
mSampleDelayBuffer.dispose();
mSampleDelayBuffer = null;
}
}
/**
* Processes channel source events
*/
@@ -195,13 +237,20 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
case REQUEST_START_SAMPLE_STREAM:
if(sourceEvent.getSource() instanceof CICTunerChannelSource)
{
mTunerController.addBufferListener((CICTunerChannelSource)sourceEvent.getSource());
startDelayBuffer();
//The start sample stream request contains a start timestamp and the delay buffer
//will preload the channel with delayed sample buffers that either contain the
//timestamp or occur later/newer than the timestamp.
mSampleDelayBuffer.addListener((CICTunerChannelSource)sourceEvent.getSource(),
sourceEvent.getValue().longValue());
}
break;
case REQUEST_STOP_SAMPLE_STREAM:
if(sourceEvent.getSource() instanceof CICTunerChannelSource)
{
mTunerController.removeBufferListener((CICTunerChannelSource)sourceEvent.getSource());
mSampleDelayBuffer.removeListener((CICTunerChannelSource)sourceEvent.getSource());
stopDelayBuffer();
}
break;
case REQUEST_SOURCE_DISPOSE: