mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#39 WIP polyphase channelizers.
This commit is contained in:
@@ -94,7 +94,7 @@ public abstract class AbstractComplexPolyphaseChannelizer implements Listener<Co
|
||||
{
|
||||
for(PolyphaseChannelSource channel : mChannels)
|
||||
{
|
||||
channel.processChannels(channels);
|
||||
channel.receiveChannelResults(channels);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import sample.Listener;
|
||||
import source.ISourceEventProcessor;
|
||||
import source.Source;
|
||||
import source.SourceEvent;
|
||||
import source.SourceException;
|
||||
import source.tuner.Tuner;
|
||||
import source.tuner.TunerChannel;
|
||||
|
||||
@@ -44,6 +45,7 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
|
||||
private List<PolyphaseChannelSource> mChannelSources = new CopyOnWriteArrayList<>();
|
||||
private ChannelIndexCalculator mChannelIndexCalculator;
|
||||
private ComplexPolyphaseChannelizerM2 mPolyphaseChannelizer;
|
||||
private ChannelSourceEventListener mChannelSourceEventListener = new ChannelSourceEventListener();
|
||||
|
||||
/**
|
||||
* Polyphase Channel Manager is a DDC channel manager for a tuner. This class provides DDC polyphase channel
|
||||
@@ -101,7 +103,7 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
|
||||
|
||||
if(outputProcessor != null)
|
||||
{
|
||||
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, this);
|
||||
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, this, CHANNEL_SAMPLE_RATE);
|
||||
mChannelSources.add(channelSource);
|
||||
}
|
||||
}
|
||||
@@ -162,45 +164,23 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes source events from the tuner or from any of the child polyphase channel sources.
|
||||
* Processes source event notifications from the tuner or source event requests from any of the child polyphase
|
||||
* channel sources.
|
||||
*/
|
||||
@Override
|
||||
public void process(SourceEvent sourceEvent)
|
||||
{
|
||||
switch(sourceEvent.getEvent())
|
||||
{
|
||||
case REQUEST_START_SAMPLE_STREAM:
|
||||
if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource)
|
||||
{
|
||||
startChannelSource((PolyphaseChannelSource)sourceEvent.getSource());
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Request to start sample stream for unrecognized source: " +
|
||||
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
|
||||
}
|
||||
//TODO: add the channel and register for samples from the tuner
|
||||
break;
|
||||
case REQUEST_STOP_SAMPLE_STREAM:
|
||||
if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource)
|
||||
{
|
||||
stopChannelSource((PolyphaseChannelSource)sourceEvent.getSource());
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Request to stop sample stream for unrecognized source: " +
|
||||
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
|
||||
}
|
||||
break;
|
||||
case NOTIFICATION_FREQUENCY_CHANGE:
|
||||
mChannelIndexCalculator.setCenterFrequency(sourceEvent.getValue().longValue());
|
||||
updateOutputProcessors();
|
||||
updateOutputProcessors(sourceEvent);
|
||||
break;
|
||||
case NOTIFICATION_SAMPLE_RATE_CHANGE:
|
||||
int sampleRate = sourceEvent.getValue().intValue();
|
||||
int channelCount = sampleRate / mChannelIndexCalculator.getChannelBandwidth();
|
||||
mChannelIndexCalculator.setChannelCount(channelCount);
|
||||
updateOutputProcessors();
|
||||
updateOutputProcessors(sourceEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -218,12 +198,28 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
|
||||
* Updates each of the output processors for any changes in the tuner's center frequency or sample rate, which
|
||||
* would cause the output processors to change the polyphase channelizer results channel(s) that the processor is
|
||||
* consuming
|
||||
*
|
||||
* @param sourceEvent that requires an update to the output processors
|
||||
*/
|
||||
private void updateOutputProcessors()
|
||||
private void updateOutputProcessors(SourceEvent sourceEvent)
|
||||
{
|
||||
for(PolyphaseChannelSource channelSource: mChannelSources)
|
||||
{
|
||||
updateOutputProcessor(channelSource);
|
||||
|
||||
//Notify the channel source that the frequency or sample rate has changed so that it can reset
|
||||
//any frequency correction value.
|
||||
if(sourceEvent != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
channelSource.process(sourceEvent);
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error while notifying polyphase channel source of a source event", se);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,4 +254,43 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
|
||||
stopChannelSource(channelSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class for handling requests for start/stop sample stream from polyphase channel sources
|
||||
*/
|
||||
private class ChannelSourceEventListener implements Listener<SourceEvent>
|
||||
{
|
||||
@Override
|
||||
public void receive(SourceEvent sourceEvent)
|
||||
{
|
||||
switch(sourceEvent.getEvent())
|
||||
{
|
||||
case REQUEST_START_SAMPLE_STREAM:
|
||||
if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource)
|
||||
{
|
||||
startChannelSource((PolyphaseChannelSource)sourceEvent.getSource());
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Request to start sample stream for unrecognized source: " +
|
||||
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
|
||||
}
|
||||
break;
|
||||
case REQUEST_STOP_SAMPLE_STREAM:
|
||||
if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource)
|
||||
{
|
||||
stopChannelSource((PolyphaseChannelSource)sourceEvent.getSource());
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Request to stop sample stream for unrecognized source: " +
|
||||
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLog.error("Received unrecognized source event from polyphase channel source [" +
|
||||
sourceEvent.getEvent() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,27 +23,33 @@ import dsp.filter.channelizer.output.IPolyphaseChannelOutputProcessor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sample.Listener;
|
||||
import sample.OverflowableTransferQueue;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import sample.real.IOverflowListener;
|
||||
import sample.complex.ComplexToComplexBufferAssembler;
|
||||
import source.ComplexSource;
|
||||
import source.ISourceEventProcessor;
|
||||
import source.Source;
|
||||
import source.SourceEvent;
|
||||
import source.SourceException;
|
||||
import source.tuner.ComplexChannelSource;
|
||||
import source.tuner.TunerChannel;
|
||||
|
||||
public class PolyphaseChannelSource extends ComplexChannelSource
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PolyphaseChannelSource extends ComplexSource implements ISourceEventProcessor
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class);
|
||||
|
||||
private TunerChannel mTunerChannel;
|
||||
private IPolyphaseChannelOutputProcessor mPolyphaseChannelOutputProcessor;
|
||||
private long mTunerCenterFrequency;
|
||||
private int mUpstreamSampleRate;
|
||||
private int mChannelSampleRate;
|
||||
private int mChannelCount;
|
||||
private int mOutputFrequencyCorrection;
|
||||
private ChannelBufferProcessor mProcessor = new ChannelBufferProcessor();
|
||||
|
||||
private DownstreamSourceEventHandler mDownstreamSourceEventHandler = new DownstreamSourceEventHandler();
|
||||
private UpstreamSourceEventHandler mUpstreamSourceEventHandler;
|
||||
|
||||
private ComplexToComplexBufferAssembler mComplexBufferAssembler = new ComplexToComplexBufferAssembler(2500);
|
||||
private IPolyphaseChannelOutputProcessor mPolyphaseChannelOutputProcessor;
|
||||
private ChannelResultsProcessor mChannelResultsProcessor;
|
||||
private ScheduledFuture<?> mChannelResultsProcessorFuture;
|
||||
|
||||
/**
|
||||
* Polyphase channelizer channel implementation. Adapts the channel array output samples from the polyphase
|
||||
@@ -52,47 +58,116 @@ public class PolyphaseChannelSource extends ComplexChannelSource
|
||||
*
|
||||
* @param tunerChannel - requested output tuner channel frequency and bandwidth.
|
||||
* @param outputProcessor - to process polyphase channelizer channel results into a channel stream
|
||||
* @param upstreamSourceEventProcessor to handle requests for start/stop sample flow and frequency corrections
|
||||
* @param externalEventListener to process sample stream start/stop requests
|
||||
*/
|
||||
public PolyphaseChannelSource(TunerChannel tunerChannel, IPolyphaseChannelOutputProcessor outputProcessor,
|
||||
ISourceEventProcessor upstreamSourceEventProcessor)
|
||||
Listener<SourceEvent> externalEventListener, int channelSampleRate)
|
||||
{
|
||||
super(upstreamSourceEventProcessor);
|
||||
|
||||
mTunerChannel = tunerChannel;
|
||||
mPolyphaseChannelOutputProcessor = outputProcessor;
|
||||
mUpstreamSourceEventHandler = new UpstreamSourceEventHandler(externalEventListener);;
|
||||
mChannelSampleRate = channelSampleRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel output processor used by this channel source.
|
||||
* Registers the listener to receive complex sample buffers from this channel source
|
||||
*/
|
||||
public IPolyphaseChannelOutputProcessor getPolyphaseChannelOutputProcessor()
|
||||
@Override
|
||||
public void setListener(Listener<ComplexBuffer> listener)
|
||||
{
|
||||
return mPolyphaseChannelOutputProcessor;
|
||||
mComplexBufferAssembler.setListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the output processor for this channel source
|
||||
* Removes the listener from receiving complex sample buffers from this channel source
|
||||
*/
|
||||
public void setPolyphaseChannelOutputProcessor(IPolyphaseChannelOutputProcessor outputProcessor)
|
||||
@Override
|
||||
public void removeListener(Listener<ComplexBuffer> listener)
|
||||
{
|
||||
synchronized(mPolyphaseChannelOutputProcessor)
|
||||
mComplexBufferAssembler.setListener(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts scheduled thread pool processing of the inbound polyphase channel results buffer to produce channelized
|
||||
* complex sample buffer output for broadcast to a registered complex sample buffer listener.
|
||||
*
|
||||
* @param executor to use when scheduling a buffer processor
|
||||
*/
|
||||
@Override
|
||||
public void start(ScheduledExecutorService executor)
|
||||
{
|
||||
if(mUpstreamSourceEventHandler != null)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor = outputProcessor;
|
||||
mUpstreamSourceEventHandler.broadcast(SourceEvent.startSampleStream(PolyphaseChannelSource.this));
|
||||
}
|
||||
|
||||
mChannelResultsProcessor = new ChannelResultsProcessor();
|
||||
|
||||
if(mChannelResultsProcessorFuture != null)
|
||||
{
|
||||
mChannelResultsProcessorFuture.cancel(true);
|
||||
mLog.error("An existing channel results processor scheduled future was cancelled");
|
||||
}
|
||||
|
||||
//Schedule the results processor to run every 100 milliseconds
|
||||
mChannelResultsProcessorFuture =
|
||||
executor.scheduleAtFixedRate(mChannelResultsProcessor, 0, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tuner channel sourced by this channel source.
|
||||
* Stops scheduled thread pool processing of the inbound polyphase channel results buffer to produce channelized
|
||||
* complex sample buffer output for broadcast to a registered complex sample buffer listener.
|
||||
*/
|
||||
public TunerChannel getTunerChannel()
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
return mTunerChannel;
|
||||
if(mUpstreamSourceEventHandler != null)
|
||||
{
|
||||
mUpstreamSourceEventHandler.broadcast(SourceEvent.stopSampleStream(PolyphaseChannelSource.this));
|
||||
}
|
||||
|
||||
if(mChannelResultsProcessorFuture != null)
|
||||
{
|
||||
mChannelResultsProcessorFuture.cancel(true);
|
||||
mChannelResultsProcessorFuture = null;
|
||||
}
|
||||
|
||||
mChannelResultsProcessor = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Module-level ISourceEventListener interface implementation for processing downstream events
|
||||
*/
|
||||
@Override
|
||||
public Listener<SourceEvent> getSourceEventListener()
|
||||
{
|
||||
return mDownstreamSourceEventHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process source event notifications received from upstream source provider and source event requests
|
||||
* received from downstream consumers.
|
||||
* Module-level ISourceEventProvider interface implementation, sets the listener to receive downstream module
|
||||
* level source events from this source
|
||||
*/
|
||||
@Override
|
||||
public void setSourceEventListener(Listener<SourceEvent> listener)
|
||||
{
|
||||
mDownstreamSourceEventHandler.setDownstreamEventListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module-level ISourceEventProvider interface implementation, removes the listener from receiving downstream module
|
||||
* level source events from this source
|
||||
*/
|
||||
@Override
|
||||
public void removeSourceEventListener()
|
||||
{
|
||||
mDownstreamSourceEventHandler.removeDownstreamEventListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* ISourceEventProcessor implementation for processing source events from the external polyphase channel manager
|
||||
* and/or tuner providing samples to the polphase channelizer implementation.
|
||||
*
|
||||
* @param sourceEvent containing a request or notification
|
||||
* @throws SourceException if there are any errors while processing the source event
|
||||
@@ -100,184 +175,141 @@ public class PolyphaseChannelSource extends ComplexChannelSource
|
||||
@Override
|
||||
public void process(SourceEvent sourceEvent) throws SourceException
|
||||
{
|
||||
switch(sourceEvent.getEvent())
|
||||
mUpstreamSourceEventHandler.receive(sourceEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel output processor used by this channel source to convert polyphase channel results into a specific
|
||||
* channel complex buffer output stream.
|
||||
*/
|
||||
public IPolyphaseChannelOutputProcessor getPolyphaseChannelOutputProcessor()
|
||||
{
|
||||
return mPolyphaseChannelOutputProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets/updates the output processor for this channel source, replacing the existing output processor.
|
||||
*/
|
||||
public void setPolyphaseChannelOutputProcessor(IPolyphaseChannelOutputProcessor outputProcessor)
|
||||
{
|
||||
//Lock on the channel output processor to block the channel results processor thread from servicing the
|
||||
//channel output processor's buffer while we change out the processors.
|
||||
synchronized(mPolyphaseChannelOutputProcessor)
|
||||
{
|
||||
case NOTIFICATION_CHANNEL_SAMPLE_RATE_CHANGE:
|
||||
setChannelSampleRate(sourceEvent.getValue().intValue());
|
||||
break;
|
||||
case NOTIFICATION_FREQUENCY_CHANGE:
|
||||
mTunerCenterFrequency = sourceEvent.getValue().longValue();
|
||||
updateInputConfiguration();
|
||||
break;
|
||||
case NOTIFICATION_SAMPLE_RATE_CHANGE:
|
||||
setUpstreamSampleRate(sourceEvent.getValue().intValue());
|
||||
updateInputConfiguration();
|
||||
break;
|
||||
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
|
||||
setFrequencyCorrection(sourceEvent.getValue().intValue());
|
||||
break;
|
||||
IPolyphaseChannelOutputProcessor existingProcessor = mPolyphaseChannelOutputProcessor;
|
||||
|
||||
//Swap out the processor so that incoming samples can accumulate in the new channel output processor
|
||||
mPolyphaseChannelOutputProcessor = outputProcessor;
|
||||
|
||||
//Fully process the residual channel results buffer of the previous channel output processor
|
||||
if(existingProcessor != null)
|
||||
{
|
||||
existingProcessor.processChannelResults(mComplexBufferAssembler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the array samples for each of the output channels from the polyphase channelizer.
|
||||
* @param channelsBuffer
|
||||
* Tuner channel that is being sourced by this polyphase channel source.
|
||||
*/
|
||||
public void processChannels(float[] channelsBuffer)
|
||||
public TunerChannel getTunerChannel()
|
||||
{
|
||||
mProcessor.process(channelsBuffer);
|
||||
return mTunerChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates current processing configuration any time there is a change in tuner center frequency
|
||||
* or tuner sample rate.
|
||||
* Primary method for receiving channel results output from a polyphase channelizer. The results array will be
|
||||
* queued for processing to extract the target channel samples, process them for frequency correction and/or
|
||||
* channel aggregation, and dispatch the results to the downstream sample listener/consumer.
|
||||
*
|
||||
* @param channelResultsBuffer containing the polyphase channelizer output for a single complex sample period.
|
||||
*/
|
||||
private void updateInputConfiguration()
|
||||
public void receiveChannelResults(float[] channelResultsBuffer)
|
||||
{
|
||||
|
||||
mPolyphaseChannelOutputProcessor.receiveChannelResults(channelResultsBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downstream sample rate
|
||||
*
|
||||
* @return sample rate in Hertz
|
||||
* @throws SourceException if this method is accessed before the channel count is established
|
||||
* @throws SourceException never
|
||||
*/
|
||||
@Override
|
||||
public int getSampleRate() throws SourceException
|
||||
{
|
||||
if(mChannelSampleRate > 0)
|
||||
{
|
||||
return mChannelSampleRate;
|
||||
}
|
||||
|
||||
throw new SourceException("Channel sample rate has not been set.");
|
||||
return mChannelSampleRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the downstream channel sample rate. Since oversampling may occur in the upstream channelizer, we can't
|
||||
* rely on simply dividing the upstream sample rate by the number of channels. Thus, we required the upstream
|
||||
* channelizer to tell us the exact channel sample rate.
|
||||
* Center tuned frequency for this channel.
|
||||
*
|
||||
* @param channelSampleRate in hertz
|
||||
* @return frequency in hertz.
|
||||
* @throws SourceException never
|
||||
*/
|
||||
private void setChannelSampleRate(int channelSampleRate)
|
||||
{
|
||||
mChannelSampleRate = channelSampleRate;
|
||||
|
||||
//Translate the upstream channel sample rate event into a downstream sample rate change event
|
||||
broadcast(SourceEvent.sampleRateChange(mChannelSampleRate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input sample rate and broadcasts a source change event to the downstream listener
|
||||
* @param sampleRate from the upstream source
|
||||
*/
|
||||
private void setUpstreamSampleRate(int sampleRate)
|
||||
{
|
||||
mUpstreamSampleRate = sampleRate;
|
||||
|
||||
try
|
||||
{
|
||||
broadcast(SourceEvent.sampleRateChange(getSampleRate()));
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Upstream sample rate changed - couldn't broadcast channel sample rate change event " +
|
||||
"to downstream channel", se);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the polyphase channel count.
|
||||
* @param channelCount number of channels being processed by the polyphase channelizer
|
||||
*/
|
||||
private void setChannelCount(int channelCount)
|
||||
{
|
||||
mChannelCount = channelCount;
|
||||
|
||||
try
|
||||
{
|
||||
broadcast(SourceEvent.sampleRateChange(getSampleRate()));
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Polyphase channel count changed - couldn't broadcast channel sample rate change event " +
|
||||
"to downstream channel", se);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFrequency() throws SourceException
|
||||
{
|
||||
return mTunerChannel.getFrequency();
|
||||
}
|
||||
|
||||
private void setFrequencyCorrection(int correction)
|
||||
{
|
||||
//TODO:
|
||||
mLog.debug("Request for frequency correction: " + correction + " -- not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeartbeatListener(Listener<Heartbeat> listener)
|
||||
{
|
||||
mLog.info("Polyphase channel source received request to set heartbeat listener");
|
||||
//TODO: implement this
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHeartbeatListener()
|
||||
{
|
||||
mLog.info("Polyphase channel source received request to remove heartbeat listener");
|
||||
//TODO: implement this
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
|
||||
mLog.info("Polyphase channel source received request to reset");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
|
||||
mLog.info("Polyphase channel source received request to dispose");
|
||||
}
|
||||
|
||||
public class ChannelBufferProcessor implements Runnable
|
||||
/**
|
||||
* Adjusts the frequency correction value that is being applied to the channelized output stream by the
|
||||
* polyphase channel output processor.
|
||||
* @param value to apply for frequency correction in hertz
|
||||
*/
|
||||
private void setFrequencyCorrection(long value)
|
||||
{
|
||||
private OverflowableTransferQueue<float[]> mComplexChannelBuffers;
|
||||
|
||||
public ChannelBufferProcessor()
|
||||
if(mPolyphaseChannelOutputProcessor != null)
|
||||
{
|
||||
mComplexChannelBuffers = new OverflowableTransferQueue<>(500, 10);
|
||||
mComplexChannelBuffers.setOverflowListener(new IOverflowListener()
|
||||
{
|
||||
@Override
|
||||
public void sourceOverflow(boolean overflow)
|
||||
{
|
||||
broadcastOverflowState(overflow);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the polyphase analysis filter's channel output for post channelizer mixing, filtering
|
||||
* and resampling operations and buffers output for dispatch to downstream processing.
|
||||
*
|
||||
* @param channelsBuffer to enqueue for processing
|
||||
*/
|
||||
public void process(float[] channelsBuffer)
|
||||
{
|
||||
mComplexChannelBuffers.offer(channelsBuffer);
|
||||
mPolyphaseChannelOutputProcessor.setFrequencyCorrection(value);
|
||||
mDownstreamSourceEventHandler.broadcast(SourceEvent.frequencyCorrectionChange(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runnable for processing the incoming polyphase channelizer results queue and distributing the channel
|
||||
* results to the registered complex sample listener
|
||||
*/
|
||||
public class ChannelResultsProcessor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO: implement distribution to each channel
|
||||
//Lock on the output processor so that it can't be changed out in the middle of processing
|
||||
synchronized(mPolyphaseChannelOutputProcessor)
|
||||
{
|
||||
mPolyphaseChannelOutputProcessor.processChannelResults(mComplexBufferAssembler);
|
||||
}
|
||||
}
|
||||
catch(Throwable throwable)
|
||||
{
|
||||
@@ -285,4 +317,99 @@ public class PolyphaseChannelSource extends ComplexChannelSource
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UpstreamSourceEventHandler implements Listener<SourceEvent>
|
||||
{
|
||||
private Listener<SourceEvent> mExternalSourceEventListener;
|
||||
|
||||
/**
|
||||
* Upstream source event processor for handling any source events generated by the polyphase channelizer that
|
||||
* provides this polyphase channel source.
|
||||
*
|
||||
* @param externalSourceEventListener to receive requests for start/stop sample streams
|
||||
*/
|
||||
public UpstreamSourceEventHandler(Listener<SourceEvent> externalSourceEventListener)
|
||||
{
|
||||
mExternalSourceEventListener = externalSourceEventListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the request source event to the external source event listener. This listener is normally the
|
||||
* polyphase channel manager.
|
||||
*
|
||||
* @param sourceEvent to broadcast
|
||||
*/
|
||||
public void broadcast(SourceEvent sourceEvent)
|
||||
{
|
||||
if(mExternalSourceEventListener != null)
|
||||
{
|
||||
mExternalSourceEventListener.receive(sourceEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process source events received from the upstream (ie tuner or channelizer) sample provider.
|
||||
* @param sourceEvent to process
|
||||
*/
|
||||
@Override
|
||||
public void receive(SourceEvent sourceEvent)
|
||||
{
|
||||
switch(sourceEvent.getEvent())
|
||||
{
|
||||
//Reset frequency correction anytime the upstream frequency or sample rate changes so that the
|
||||
//downstream modules can reset and recalculate any needed frequency correction
|
||||
case NOTIFICATION_FREQUENCY_CHANGE:
|
||||
case NOTIFICATION_SAMPLE_RATE_CHANGE:
|
||||
setFrequencyCorrection(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downstream source event handler implements the ISourceEventProvider and ISourceEventProcessor interfaces on
|
||||
* for this module to handle broadcasting source events to other downstream modules and receiving requests from
|
||||
* any downstream modules.
|
||||
*/
|
||||
public class DownstreamSourceEventHandler implements Listener<SourceEvent>
|
||||
{
|
||||
private Listener<SourceEvent> mDownstreamEventListener;
|
||||
|
||||
public void broadcast(SourceEvent sourceEvent)
|
||||
{
|
||||
if(mDownstreamEventListener != null)
|
||||
{
|
||||
mDownstreamEventListener.receive(sourceEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the downstream event listener to receive source events from this channel source module
|
||||
* @param listener to receive events
|
||||
*/
|
||||
public void setDownstreamEventListener(Listener<SourceEvent> listener)
|
||||
{
|
||||
mDownstreamEventListener = listener;
|
||||
}
|
||||
|
||||
public void removeDownstreamEventListener()
|
||||
{
|
||||
mDownstreamEventListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process source events received from the downstream channel modules.
|
||||
* @param sourceEvent to process
|
||||
*/
|
||||
@Override
|
||||
public void receive(SourceEvent sourceEvent)
|
||||
{
|
||||
switch(sourceEvent.getEvent())
|
||||
{
|
||||
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
|
||||
setFrequencyCorrection(sourceEvent.getValue().intValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,26 @@
|
||||
package dsp.filter.channelizer.output;
|
||||
|
||||
import dsp.mixer.Oscillator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sample.OverflowableTransferQueue;
|
||||
import sample.complex.Complex;
|
||||
import sample.complex.ComplexSampleListener;
|
||||
import sample.real.IOverflowListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputProcessor
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelOutputProcessor.class);
|
||||
|
||||
private static final int OVERFLOW_THRESHOLD = 25000 * 3; //25 kHz * 3 seconds
|
||||
private static final int RESET_THRESHOLD = (int)(OVERFLOW_THRESHOLD * .25); //reset at 25%
|
||||
|
||||
private OverflowableTransferQueue<float[]> mChannelResultsQueue;
|
||||
private List<float[]> mChannelResultsToProcess = new ArrayList<>();
|
||||
|
||||
private int mInputChannelCount;
|
||||
private Oscillator mFrequencyCorrectionMixer;
|
||||
|
||||
@@ -38,6 +54,67 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP
|
||||
{
|
||||
mInputChannelCount = inputChannelCount;
|
||||
mFrequencyCorrectionMixer = new Oscillator(0, sampleRate);
|
||||
mChannelResultsQueue = new OverflowableTransferQueue<>(OVERFLOW_THRESHOLD, RESET_THRESHOLD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveChannelResults(float[] channelResults)
|
||||
{
|
||||
mChannelResultsQueue.offer(channelResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes all enqueued polyphase channelizer results until the internal queue is empty
|
||||
* @param listener to receive the processed channel results
|
||||
*/
|
||||
@Override
|
||||
public void processChannelResults(ComplexSampleListener listener)
|
||||
{
|
||||
try
|
||||
{
|
||||
mChannelResultsQueue.drainTo(mChannelResultsToProcess, 1000);
|
||||
|
||||
while(!mChannelResultsToProcess.isEmpty())
|
||||
{
|
||||
for(float[] channelResults: mChannelResultsToProcess)
|
||||
{
|
||||
process(channelResults, listener);
|
||||
}
|
||||
|
||||
mChannelResultsToProcess.clear();
|
||||
mChannelResultsQueue.drainTo(mChannelResultsToProcess, 1000);
|
||||
}
|
||||
}
|
||||
catch(Throwable throwable)
|
||||
{
|
||||
mLog.error("Error while processing polyphase channel samples", throwable);
|
||||
mChannelResultsToProcess.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-class implementation to process one polyphase channelizer result array.
|
||||
* @param channelResult to process
|
||||
* @param listener to receive the channel complex sample
|
||||
*/
|
||||
public abstract void process(float[] channelResult, ComplexSampleListener listener);
|
||||
|
||||
|
||||
/**
|
||||
* Sets the overflow listener to monitor the internal channelizer channel results queue overflow state
|
||||
*/
|
||||
public void setOverflowListener(IOverflowListener listener)
|
||||
{
|
||||
mChannelResultsQueue.setOverflowListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the overflow listener from monitoring the internal channelizer channel results queue overflow state
|
||||
*/
|
||||
public void removeOverflowListener(IOverflowListener listener)
|
||||
{
|
||||
mChannelResultsQueue.setOverflowListener(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,19 +18,24 @@
|
||||
******************************************************************************/
|
||||
package dsp.filter.channelizer.output;
|
||||
|
||||
import sample.complex.IComplexSampleListener;
|
||||
import sample.complex.ComplexSampleListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IPolyphaseChannelOutputProcessor
|
||||
{
|
||||
/**
|
||||
* Process the channel output channel results from the polyphase channelizer filter
|
||||
* Receive and enqueue output results from the polyphase analysis channelizer
|
||||
* @param channelResults to enqueue
|
||||
*/
|
||||
void receiveChannelResults(float[] channelResults);
|
||||
|
||||
/**
|
||||
* Process the channel output channel results queue and deliver the output to the listener
|
||||
*
|
||||
* @param channels to process
|
||||
* @param listener to receive the processed channel results
|
||||
*/
|
||||
void process(float[] channels, IComplexSampleListener listener);
|
||||
void processChannelResults(ComplexSampleListener listener);
|
||||
|
||||
/**
|
||||
* Specifies the frequency correction (+/-) that should be applied to samples extracted from the polyphase
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
******************************************************************************/
|
||||
package dsp.filter.channelizer.output;
|
||||
|
||||
import sample.complex.IComplexSampleListener;
|
||||
import sample.complex.ComplexSampleListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -65,7 +65,7 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor
|
||||
* @param listener to receive the extracted, frequency-translated channel results
|
||||
*/
|
||||
@Override
|
||||
public void process(float[] channels, IComplexSampleListener listener)
|
||||
public void process(float[] channels, ComplexSampleListener listener)
|
||||
{
|
||||
if(channels.length < mChannelOffset + 1)
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
******************************************************************************/
|
||||
package dsp.filter.channelizer.output;
|
||||
|
||||
import sample.complex.IComplexSampleListener;
|
||||
import sample.complex.ComplexSampleListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -70,7 +70,7 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
|
||||
* @param listener to receive the extracted, frequency-translated channel results
|
||||
*/
|
||||
@Override
|
||||
public void process(float[] channels, IComplexSampleListener listener)
|
||||
public void process(float[] channels, ComplexSampleListener listener)
|
||||
{
|
||||
// if(channels.length < mChannelOffset + 1)
|
||||
// {
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 sample.complex;
|
||||
|
||||
public interface IComplexSampleListener
|
||||
{
|
||||
public void receive(float i, float q);
|
||||
}
|
||||
Reference in New Issue
Block a user