mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Memory/CPU performance improvement and Audio/Mixer source restructuring
Converted to use float primitives and to keep samples in buffer format until the sample rate is decimated to 48kHz. Added new non-recursive, CIC decimation filter -- untested Updated sound card/mixer input source management. Now testing for mono/stereo support for each card and limiting channel choices accordingly. Separated the mixersource into a ComplexMixer and and SimplexMixerSource. The ComplexMixer is used for sound card based tuners like the funcube. The SimplexMixerSource is used for demodulated sound card inputs. Interfaces for sample handling - standardized on using 'Complex' to indicate I/Q samples and 'Simplex' to indicate demodulated float samples. The processing chain now accepts either a decimated (48k) list of complex samples from a tuner channel source, or a 48k simplex buffer (array of demodulated float samples). Resolved issue with HackRF tuner not applying tuner configuration information.
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
******************************************************************************/
|
||||
package audio;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import util.Oscillator;
|
||||
import dsp.filter.Filters;
|
||||
import dsp.filter.FloatFIRFilter;
|
||||
@@ -26,11 +26,11 @@ import dsp.filter.FloatFIRFilter;
|
||||
* Applies audio inversion (or un-inversion) to a stream of float audio samples
|
||||
* by multiplying each successive sample by either a 1 or a -1 value.
|
||||
*/
|
||||
public class AudioInverter implements Listener<Float>
|
||||
public class AudioInverter implements SimplexSampleListener
|
||||
{
|
||||
private Oscillator mSineWaveGenerator;
|
||||
private FloatFIRFilter mPostInversionLowPassFilter;
|
||||
private Listener<Float> mListener;
|
||||
private SimplexSampleListener mListener;
|
||||
|
||||
public AudioInverter( int inversionFrequency, int sampleRate )
|
||||
{
|
||||
@@ -47,13 +47,13 @@ public class AudioInverter implements Listener<Float>
|
||||
this( frequency.getFrequency(), sampleRate );
|
||||
}
|
||||
|
||||
public void setListener( Listener<Float> listener )
|
||||
public void setListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
//Multiply the sample by the folding frequency and then send it to
|
||||
//the low pass filter
|
||||
@@ -68,10 +68,10 @@ public class AudioInverter implements Listener<Float>
|
||||
* Simple class to receive the output of the FIR low pass filter and then
|
||||
* send it to the registered listener(s)
|
||||
*/
|
||||
class FilteredSampleProcessor implements Listener<Float>
|
||||
class FilteredSampleProcessor implements SimplexSampleListener
|
||||
{
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
|
||||
@@ -27,10 +27,10 @@ import javax.sound.sampled.SourceDataLine;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import buffer.FloatSampleBufferAssembler;
|
||||
|
||||
public class AudioOutput implements Listener<Float>,
|
||||
public class AudioOutput implements SimplexSampleListener,
|
||||
SquelchListener,
|
||||
AudioTypeListener
|
||||
{
|
||||
@@ -88,7 +88,7 @@ public class AudioOutput implements Listener<Float>,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
float outputSample = mAdapter.get( sample );
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import gui.SDRTrunk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
@@ -40,13 +39,13 @@ import record.wave.FloatWaveRecorder;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import sample.simplex.SimplexBuffer;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.ComplexSource;
|
||||
import source.FloatArraySource;
|
||||
import source.FloatSource;
|
||||
import source.SimplexSource;
|
||||
import source.Source;
|
||||
import source.SourceException;
|
||||
import source.config.SourceConfigMixer;
|
||||
import source.tuner.FrequencyChangeListener;
|
||||
import source.tuner.Tuner;
|
||||
import source.tuner.TunerChannelSource;
|
||||
import alias.AliasList;
|
||||
@@ -86,14 +85,13 @@ public class ProcessingChain implements Listener<Message>
|
||||
private ScheduledFuture<?> mProcessorTask;
|
||||
private AliasList mAliasList;
|
||||
|
||||
private ComplexListener mComplexListener = new ComplexListener();
|
||||
private LinkedTransferQueue<List<ComplexSample>> mComplexBuffer =
|
||||
private ComplexSampleReceiver mComplexReceiver = new ComplexSampleReceiver();
|
||||
private LinkedTransferQueue<List<ComplexSample>> mComplexQueue =
|
||||
new LinkedTransferQueue<List<ComplexSample>>();
|
||||
|
||||
private FloatListener mFloatListener = new FloatListener();
|
||||
private FloatArrayListener mFloatArrayListener = new FloatArrayListener();
|
||||
private LinkedTransferQueue<Float> mFloatBuffer =
|
||||
new LinkedTransferQueue<Float>();
|
||||
private SimplexSampleReceiver mSimplexReceiver = new SimplexSampleReceiver();
|
||||
private LinkedTransferQueue<SimplexBuffer> mSimplexQueue =
|
||||
new LinkedTransferQueue<SimplexBuffer>();
|
||||
|
||||
protected Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
|
||||
|
||||
@@ -118,19 +116,14 @@ public class ProcessingChain implements Listener<Message>
|
||||
return mAFC;
|
||||
}
|
||||
|
||||
public Listener<List<ComplexSample>> getComplexListener()
|
||||
public Listener<List<ComplexSample>> getComplexReceiver()
|
||||
{
|
||||
return mComplexListener;
|
||||
return mComplexReceiver;
|
||||
}
|
||||
|
||||
public Listener<Float> getFloatListener()
|
||||
public Listener<SimplexBuffer> getSimplexReceiver()
|
||||
{
|
||||
return mFloatListener;
|
||||
}
|
||||
|
||||
public Listener<Float[]> getFloatArrayListener()
|
||||
{
|
||||
return mFloatArrayListener;
|
||||
return (Listener<SimplexBuffer>)mSimplexReceiver;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
@@ -248,16 +241,12 @@ public class ProcessingChain implements Listener<Message>
|
||||
{
|
||||
if( mSource instanceof ComplexSource )
|
||||
{
|
||||
((ComplexSource)mSource).removeListener( this.getComplexListener() );
|
||||
((ComplexSource)mSource).removeListener( this.getComplexReceiver() );
|
||||
}
|
||||
else if( mSource instanceof FloatSource )
|
||||
else if( mSource instanceof SimplexSource )
|
||||
{
|
||||
((FloatSource)mSource).removeListener( this.getFloatListener() );
|
||||
}
|
||||
else if( mSource instanceof FloatArraySource )
|
||||
{
|
||||
((FloatArraySource)mSource)
|
||||
.removeListener( this.getFloatArrayListener() );
|
||||
((SimplexSource)mSource)
|
||||
.removeListener( this.getSimplexReceiver() );
|
||||
}
|
||||
|
||||
if( mProcessorTask != null )
|
||||
@@ -299,7 +288,7 @@ public class ProcessingChain implements Listener<Message>
|
||||
50, TimeUnit.MILLISECONDS );
|
||||
|
||||
((ComplexSource)mSource)
|
||||
.setListener( this.getComplexListener() );
|
||||
.setListener( this.getComplexReceiver() );
|
||||
}
|
||||
catch( RejectedExecutionException ree )
|
||||
{
|
||||
@@ -310,40 +299,18 @@ public class ProcessingChain implements Listener<Message>
|
||||
mSource.dispose();
|
||||
}
|
||||
}
|
||||
else if( mSource instanceof FloatSource )
|
||||
else if( mSource instanceof SimplexSource )
|
||||
{
|
||||
try
|
||||
{
|
||||
mProcessorTask =
|
||||
mResourceManager.getThreadPoolManager()
|
||||
.schedule( ThreadType.DECODER,
|
||||
new FloatProcessor(),
|
||||
new SimplexProcessor(),
|
||||
50, TimeUnit.MILLISECONDS );
|
||||
|
||||
((FloatSource)mSource)
|
||||
.setListener( this.getFloatListener() );
|
||||
}
|
||||
catch( RejectedExecutionException ree )
|
||||
{
|
||||
mLog.error( getLogPrefix() + "ProcessingChain - "
|
||||
+ "error scheduling float sample processing "
|
||||
+ "thread", ree );
|
||||
|
||||
mSource.dispose();
|
||||
}
|
||||
}
|
||||
else if( mSource instanceof FloatArraySource )
|
||||
{
|
||||
try
|
||||
{
|
||||
mProcessorTask =
|
||||
mResourceManager.getThreadPoolManager()
|
||||
.schedule( ThreadType.DECODER,
|
||||
new FloatProcessor(),
|
||||
50, TimeUnit.MILLISECONDS );
|
||||
|
||||
((FloatArraySource)mSource)
|
||||
.setListener( this.getFloatArrayListener() );
|
||||
((SimplexSource)mSource)
|
||||
.setListener( this.getSimplexReceiver() );
|
||||
}
|
||||
catch( RejectedExecutionException ree )
|
||||
{
|
||||
@@ -394,7 +361,7 @@ public class ProcessingChain implements Listener<Message>
|
||||
"-" + mChannel.getName();
|
||||
|
||||
mAudioOutput = new AudioOutput( threadName );
|
||||
addFloatListener( mAudioOutput );
|
||||
addSimplexListener( mAudioOutput );
|
||||
|
||||
/* Automatic frequency control */
|
||||
if( mAFC != null )
|
||||
@@ -415,7 +382,7 @@ public class ProcessingChain implements Listener<Message>
|
||||
tcs.addListener( mAFC );
|
||||
|
||||
/* Add AFC as listener to the decoder */
|
||||
mDecoder.addUnfilteredFloatListener( mAFC );
|
||||
mDecoder.addUnfilteredSimplexSampleListener( mAFC );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -525,7 +492,7 @@ public class ProcessingChain implements Listener<Message>
|
||||
mLog.info( getLogPrefix() + "- started audio recording [" +
|
||||
recorder.getFileName() + "]" );
|
||||
|
||||
mDecoder.addFloatListener( (FloatWaveRecorder)recorder );
|
||||
mDecoder.addSimplexSampleListener( (FloatWaveRecorder)recorder );
|
||||
}
|
||||
else if( recorder instanceof ComplexWaveRecorder )
|
||||
{
|
||||
@@ -620,22 +587,32 @@ public class ProcessingChain implements Listener<Message>
|
||||
|
||||
}
|
||||
|
||||
public void addFloatListener( Listener<Float> listener )
|
||||
/**
|
||||
* Adds the listener to receive demodulated samples from the decoder
|
||||
*/
|
||||
public void addSimplexListener( SimplexSampleListener listener )
|
||||
{
|
||||
if( mDecoder != null )
|
||||
{
|
||||
mDecoder.addFloatListener( listener );
|
||||
mDecoder.addSimplexSampleListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
public void removeFloatListener( Listener<Float> listener )
|
||||
/**
|
||||
* Removes the listener from receiving demodulated samples from the decoder
|
||||
*/
|
||||
public void removeSimplexListener( SimplexSampleListener listener )
|
||||
{
|
||||
if( mDecoder != null )
|
||||
{
|
||||
mDecoder.removeFloatListener( listener );
|
||||
mDecoder.removeSimplexListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to receive a copy of complex (I/Q) baseband samples
|
||||
* received by this processing chain
|
||||
*/
|
||||
public void addComplexListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
if( mDecoder != null )
|
||||
@@ -649,6 +626,10 @@ public class ProcessingChain implements Listener<Message>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving a copy of complex (I/Q) baseband
|
||||
* samples received by this processing chain
|
||||
*/
|
||||
public void removeComplexListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
if( mDecoder != null )
|
||||
@@ -662,11 +643,17 @@ public class ProcessingChain implements Listener<Message>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the listener to receive messages
|
||||
*/
|
||||
public void addListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all listeners in the list to receive messages
|
||||
*/
|
||||
public void addListeners( List<Listener<Message>> listeners )
|
||||
{
|
||||
for( Listener<Message> listener: listeners )
|
||||
@@ -675,43 +662,47 @@ public class ProcessingChain implements Listener<Message>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving messages
|
||||
*/
|
||||
public void removeListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Runnable float sample processing task. Schedule this task to run 20
|
||||
* times a second when we're receiving complex samples from our source.
|
||||
* Schedule execution via the thread pool manager, obtainable from the
|
||||
* resource manager,
|
||||
* Manages the simplex sample queue and distributes samples to the decoder.
|
||||
*/
|
||||
private class FloatProcessor implements Runnable
|
||||
private class SimplexProcessor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
List<Float> samples = new ArrayList<Float>();
|
||||
List<SimplexBuffer> sampleBuffers = new ArrayList<SimplexBuffer>();
|
||||
|
||||
mFloatBuffer.drainTo( samples, 48000 );
|
||||
mSimplexQueue.drainTo( sampleBuffers, 4 );
|
||||
|
||||
for( Float sample: samples )
|
||||
for( SimplexBuffer sampleBuffer: sampleBuffers )
|
||||
{
|
||||
if( mDecoder != null )
|
||||
{
|
||||
mDecoder.getFloatReceiver().receive( sample );
|
||||
float[] samples = sampleBuffer.getSamples();
|
||||
|
||||
for( float sample: samples )
|
||||
{
|
||||
mDecoder.getSimplexReceiver().receive( sample );
|
||||
}
|
||||
}
|
||||
|
||||
sampleBuffer.dispose();
|
||||
}
|
||||
|
||||
samples.clear();
|
||||
sampleBuffers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runnable complex sample processing task. Schedule this task to run 20
|
||||
* times a second when we're receiving complex samples from our source.
|
||||
* Schedule execution via the thread pool manager, obtainable from the
|
||||
* resource manager,
|
||||
* Manages the complex sample queue and distributes samples to the decoder.
|
||||
*/
|
||||
private class ComplexProcessor implements Runnable
|
||||
{
|
||||
@@ -721,7 +712,7 @@ public class ProcessingChain implements Listener<Message>
|
||||
List<List<ComplexSample>> sampleSets =
|
||||
new ArrayList<List<ComplexSample>>();
|
||||
|
||||
mComplexBuffer.drainTo( sampleSets, 16 );
|
||||
mComplexQueue.drainTo( sampleSets, 16 );
|
||||
|
||||
for( List<ComplexSample> samples: sampleSets )
|
||||
{
|
||||
@@ -740,30 +731,29 @@ public class ProcessingChain implements Listener<Message>
|
||||
}
|
||||
}
|
||||
|
||||
public class ComplexListener implements Listener<List<ComplexSample>>
|
||||
/**
|
||||
* Internal listener to receive complex (I/Q) baseband samples. Places
|
||||
* received samples into the queue managed by the ComplexProcessor.
|
||||
*/
|
||||
public class ComplexSampleReceiver implements Listener<List<ComplexSample>>
|
||||
{
|
||||
@Override
|
||||
public void receive( List<ComplexSample> samples )
|
||||
{
|
||||
mComplexBuffer.add( samples );
|
||||
mComplexQueue.add( samples );
|
||||
}
|
||||
}
|
||||
|
||||
public class FloatListener implements Listener<Float>
|
||||
|
||||
/**
|
||||
* Internal listener to receive simplex (ie demodulated) samples. Places
|
||||
* received samples into the queue managed by the SimplexProcessor
|
||||
*/
|
||||
public class SimplexSampleReceiver implements Listener<SimplexBuffer>
|
||||
{
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( SimplexBuffer samples )
|
||||
{
|
||||
mFloatBuffer.add( sample );
|
||||
}
|
||||
}
|
||||
|
||||
public class FloatArrayListener implements Listener<Float[]>
|
||||
{
|
||||
@Override
|
||||
public void receive( Float[] samples )
|
||||
{
|
||||
mFloatBuffer.addAll( Arrays.asList( samples ) );
|
||||
mSimplexQueue.add( samples );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,12 @@ package decode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import dsp.afc.AutomaticFrequencyControl;
|
||||
import message.Message;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import sample.simplex.SimplexSampleBroadcaster;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import eventlog.MessageEventLogger;
|
||||
|
||||
@@ -32,25 +33,36 @@ public abstract class Decoder implements Listener<Message>
|
||||
{
|
||||
private MessageEventLogger mEventLogger;
|
||||
private Broadcaster<Message> mMessageBroadcaster = new Broadcaster<Message>();
|
||||
private Broadcaster<Float> mFloatBroadcaster = new Broadcaster<Float>();
|
||||
private SimplexSampleBroadcaster mSimplexBroadcaster = new SimplexSampleBroadcaster();
|
||||
private Broadcaster<ComplexSample> mComplexBroadcaster =
|
||||
new Broadcaster<ComplexSample>();
|
||||
|
||||
protected SampleType mSourceSampleType;
|
||||
protected ArrayList<Decoder> mAuxiliaryDecoders = new ArrayList<Decoder>();
|
||||
|
||||
|
||||
/**
|
||||
* Abstract decoder class.
|
||||
*
|
||||
* @param sampleType - complex (I/Q) or simplex (demodulated) sample type.
|
||||
* Each decoder instance will be configured to receive either complex or
|
||||
* simplex samples. When configured to receive complex samples, the
|
||||
* decoder will implement a demodulator component and feed the output from
|
||||
* that demodulator back onto itself via the simplex sample interface, so
|
||||
* that any added auxiliary decoders will then receive the demodulated
|
||||
* output stream.
|
||||
*/
|
||||
public Decoder( SampleType sampleType )
|
||||
{
|
||||
mSourceSampleType = sampleType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a float listener interface for connecting this decoder to a
|
||||
* float stream provider
|
||||
* Returns a simplex (ie demodulated) sample listener interface for
|
||||
* connecting this decoder to a simplex sample stream provider
|
||||
*/
|
||||
public Listener<Float> getFloatReceiver()
|
||||
public SimplexSampleListener getSimplexReceiver()
|
||||
{
|
||||
return (Listener<Float>)mFloatBroadcaster;
|
||||
return mSimplexBroadcaster;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,16 +73,12 @@ public abstract class Decoder implements Listener<Message>
|
||||
{
|
||||
return (Listener<ComplexSample>)mComplexBroadcaster;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the primary decoder type for this decoder
|
||||
*/
|
||||
public abstract DecoderType getType();
|
||||
|
||||
public void addAuxiliaryDecoder( Decoder decoder )
|
||||
{
|
||||
mAuxiliaryDecoders.add( decoder );
|
||||
mFloatBroadcaster.addListener( decoder.getFloatReceiver() );
|
||||
decoder.addMessageListener( this );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cleanup method. Invoke this method after stop and before delete.
|
||||
*/
|
||||
@@ -85,30 +93,66 @@ public abstract class Decoder implements Listener<Message>
|
||||
|
||||
mMessageBroadcaster.clear();
|
||||
mComplexBroadcaster.clear();
|
||||
mFloatBroadcaster.clear();
|
||||
mSimplexBroadcaster.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the auxiliary decoder (piggyback) to this decoder.
|
||||
*
|
||||
* Note: we assume that the auxiliary decoder is designed to receive
|
||||
* demodulated samples, thus we automatically register the aux decoder to
|
||||
* receive the demodulated sample stream.
|
||||
*
|
||||
* Registers this decoder to receive the message output stream from the
|
||||
* auxiliary decoder, so that those messages can be echoed and included in
|
||||
* the consolidated message stream to all message listeners registered on
|
||||
* the primary decoder.
|
||||
*/
|
||||
public void addAuxiliaryDecoder( Decoder decoder )
|
||||
{
|
||||
mAuxiliaryDecoders.add( decoder );
|
||||
mSimplexBroadcaster.addListener( decoder.getSimplexReceiver() );
|
||||
decoder.addMessageListener( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all auxiliary decoders that have been added to this
|
||||
* processing chain
|
||||
*/
|
||||
public List<Decoder> getAuxiliaryDecoders()
|
||||
{
|
||||
return mAuxiliaryDecoders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the MessageEventLogger for this processing chain
|
||||
*/
|
||||
public void setEventLogger( MessageEventLogger eventLogger )
|
||||
{
|
||||
mEventLogger = eventLogger;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the MessageEventLogger for this processing chain
|
||||
*/
|
||||
public MessageEventLogger getEventLogger()
|
||||
{
|
||||
return mEventLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main receiver method for all demodulators to send their decoded messages
|
||||
* so that they will be broadcast to all registered listeners
|
||||
*/
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
send( message );
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the message to all registered listeners
|
||||
*/
|
||||
public void send( Message message )
|
||||
{
|
||||
if( mMessageBroadcaster != null )
|
||||
@@ -116,34 +160,57 @@ public abstract class Decoder implements Listener<Message>
|
||||
mMessageBroadcaster.receive( message );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a listener to receiving decoded messages from all attached decoders
|
||||
*/
|
||||
public void addMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mMessageBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving decoded messages from all attached
|
||||
* decoders
|
||||
*/
|
||||
public void removeMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mMessageBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
public void addFloatListener( Listener<Float> listener )
|
||||
{
|
||||
mFloatBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public abstract void addUnfilteredFloatListener( Listener<Float> listener );
|
||||
/**
|
||||
* Adds a simplex sample listener to receive unfiltered demodulated samples
|
||||
*/
|
||||
public abstract void addUnfilteredSimplexSampleListener( SimplexSampleListener listener );
|
||||
|
||||
public void removeFloatListener( Listener<Float> listener )
|
||||
/**
|
||||
* Adds a simplex sample listener to receive demodulated samples
|
||||
*/
|
||||
public void addSimplexSampleListener( SimplexSampleListener listener )
|
||||
{
|
||||
mFloatBroadcaster.removeListener( listener );
|
||||
mSimplexBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the simplex sample listener from receiving demodulated samples
|
||||
*/
|
||||
public void removeSimplexListener( SimplexSampleListener listener )
|
||||
{
|
||||
mSimplexBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a complex (I/Q) sample listener to receive copy of the inbound
|
||||
* complex sample stream
|
||||
*/
|
||||
public void addComplexListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
mComplexBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the complex (I/Q) sample listener from receiving samples
|
||||
*/
|
||||
public void removeComplexListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
mComplexBroadcaster.removeListener( listener );
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package decode.am;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import decode.Decoder;
|
||||
import decode.DecoderType;
|
||||
@@ -61,7 +61,7 @@ public class AMDecoder extends Decoder
|
||||
|
||||
mDCRemovalFilter.setListener( mLowPassFilter );
|
||||
|
||||
mLowPassFilter.setListener( this.getFloatReceiver() );
|
||||
mLowPassFilter.setListener( this.getSimplexReceiver() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class AMDecoder extends Decoder
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUnfilteredFloatListener( Listener<Float> listener )
|
||||
public void addUnfilteredSimplexSampleListener( SimplexSampleListener listener )
|
||||
{
|
||||
mDemodulator.addListener( listener );
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import bits.MessageFramer;
|
||||
@@ -75,7 +76,7 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable
|
||||
|
||||
mDecimationFilter = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
addFloatListener( mDecimationFilter );
|
||||
addSimplexSampleListener( mDecimationFilter );
|
||||
|
||||
mBandPassFilter = new FloatFIRFilter(
|
||||
Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 );
|
||||
@@ -115,9 +116,9 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable
|
||||
* Returns a float listener interface for connecting this decoder to a
|
||||
* float stream provider
|
||||
*/
|
||||
public Listener<Float> getFloatReceiver()
|
||||
public SimplexSampleListener getSimplexReceiver()
|
||||
{
|
||||
return (Listener<Float>)mDecimationFilter;
|
||||
return (SimplexSampleListener)mDecimationFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,7 +148,7 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable
|
||||
{
|
||||
case INSTRUMENT_INPUT:
|
||||
FloatTap inputTap = (FloatTap)tap;
|
||||
addFloatListener( inputTap );
|
||||
addSimplexSampleListener( inputTap );
|
||||
break;
|
||||
case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD:
|
||||
FloatTap bpTap = (FloatTap)tap;
|
||||
@@ -171,7 +172,7 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable
|
||||
{
|
||||
case INSTRUMENT_INPUT:
|
||||
FloatTap inputTap = (FloatTap)tap;
|
||||
removeFloatListener( inputTap );
|
||||
removeSimplexListener( inputTap );
|
||||
break;
|
||||
case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD:
|
||||
mBandPassFilter.setListener( mFSKDecoder );
|
||||
@@ -183,7 +184,7 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUnfilteredFloatListener( Listener<Float> listener )
|
||||
public void addUnfilteredSimplexSampleListener( SimplexSampleListener listener )
|
||||
{
|
||||
//Not implemented
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import message.MessageDirection;
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import bits.MessageFramer;
|
||||
@@ -81,7 +81,7 @@ public class LTRNetDecoder extends Decoder implements Instrumentable
|
||||
mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO );
|
||||
mDemodulator.addListener( mDCRemovalFilter );
|
||||
|
||||
mDCRemovalFilter.setListener( this.getFloatReceiver() );
|
||||
mDCRemovalFilter.setListener( this.getSimplexReceiver() );
|
||||
}
|
||||
|
||||
mLTRFSKDecoder = new LTRFSKDecoder();
|
||||
@@ -96,7 +96,7 @@ public class LTRNetDecoder extends Decoder implements Instrumentable
|
||||
}
|
||||
else
|
||||
{
|
||||
this.addFloatListener( mLTRFSKDecoder );
|
||||
this.addSimplexSampleListener( mLTRFSKDecoder );
|
||||
}
|
||||
|
||||
if( direction == MessageDirection.OSW )
|
||||
@@ -151,7 +151,7 @@ public class LTRNetDecoder extends Decoder implements Instrumentable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUnfilteredFloatListener( Listener<Float> listener )
|
||||
public void addUnfilteredSimplexSampleListener( SimplexSampleListener listener )
|
||||
{
|
||||
if( mDemodulator != null )
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package decode.ltrstandard;
|
||||
|
||||
import message.MessageDirection;
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import bits.MessageFramer;
|
||||
@@ -73,7 +73,7 @@ public class LTRStandardDecoder extends Decoder
|
||||
mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO );
|
||||
mDemodulator.addListener( mDCRemovalFilter );
|
||||
|
||||
mDCRemovalFilter.setListener( this.getFloatReceiver() );
|
||||
mDCRemovalFilter.setListener( this.getSimplexReceiver() );
|
||||
}
|
||||
|
||||
mLTRFSKDecoder = new LTRFSKDecoder();
|
||||
@@ -88,7 +88,7 @@ public class LTRStandardDecoder extends Decoder
|
||||
}
|
||||
else
|
||||
{
|
||||
this.addFloatListener( mLTRFSKDecoder );
|
||||
this.addSimplexSampleListener( mLTRFSKDecoder );
|
||||
}
|
||||
|
||||
if( direction == MessageDirection.OSW )
|
||||
@@ -118,7 +118,7 @@ public class LTRStandardDecoder extends Decoder
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUnfilteredFloatListener( Listener<Float> listener )
|
||||
public void addUnfilteredSimplexSampleListener( SimplexSampleListener listener )
|
||||
{
|
||||
if( mDemodulator != null )
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ import instrument.tap.stream.FloatTap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import bits.MessageFramer;
|
||||
@@ -79,7 +79,7 @@ public class MDCDecoder extends Decoder implements Instrumentable
|
||||
/* Decimation filter: 48000 / 2 = 24000 */
|
||||
mDecimationFilter = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
addFloatListener( mDecimationFilter );
|
||||
addSimplexSampleListener( mDecimationFilter );
|
||||
|
||||
/* Bandpass filter - predefined */
|
||||
mBandPassFilter = new FloatFIRFilter(
|
||||
@@ -154,7 +154,7 @@ public class MDCDecoder extends Decoder implements Instrumentable
|
||||
switch( tap.getName() )
|
||||
{
|
||||
case INSTRUMENT_INPUT:
|
||||
addFloatListener( (FloatTap)tap );
|
||||
addSimplexSampleListener( (FloatTap)tap );
|
||||
break;
|
||||
case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD:
|
||||
/* wire the tap between the bandpass filter and the demod */
|
||||
@@ -186,7 +186,7 @@ public class MDCDecoder extends Decoder implements Instrumentable
|
||||
switch( tap.getName() )
|
||||
{
|
||||
case INSTRUMENT_INPUT:
|
||||
removeFloatListener( (FloatTap)tap );
|
||||
removeSimplexListener( (FloatTap)tap );
|
||||
break;
|
||||
case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD:
|
||||
mBandPassFilter.setListener( mFSKDecoder );
|
||||
@@ -202,7 +202,7 @@ public class MDCDecoder extends Decoder implements Instrumentable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUnfilteredFloatListener( Listener<Float> listener )
|
||||
public void addUnfilteredSimplexSampleListener( SimplexSampleListener listener )
|
||||
{
|
||||
//Not implemented
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import bits.MessageFramer;
|
||||
@@ -106,7 +106,7 @@ public class MPT1327Decoder extends Decoder implements Instrumentable
|
||||
if( mNBFMDemodulator != null )
|
||||
{
|
||||
mNBFMDemodulator.addListener( mDCRemovalFilter );
|
||||
mDCRemovalFilter.setListener( this.getFloatReceiver() );
|
||||
mDCRemovalFilter.setListener( this.getSimplexReceiver() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ public class MPT1327Decoder extends Decoder implements Instrumentable
|
||||
/* Decimation filter - 48000 / 2 = 24000 output */
|
||||
mDecimationFilter = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
addFloatListener( mDecimationFilter );
|
||||
addSimplexSampleListener( mDecimationFilter );
|
||||
|
||||
/* Low pass filter: 2kHz to pass the 1200 & 1800 Hz FSK */
|
||||
mLowPassFilter = new FloatFIRFilter(
|
||||
@@ -169,7 +169,7 @@ public class MPT1327Decoder extends Decoder implements Instrumentable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUnfilteredFloatListener( Listener<Float> listener )
|
||||
public void addUnfilteredSimplexSampleListener( SimplexSampleListener listener )
|
||||
{
|
||||
if( mNBFMDemodulator != null )
|
||||
{
|
||||
@@ -256,7 +256,7 @@ public class MPT1327Decoder extends Decoder implements Instrumentable
|
||||
}
|
||||
else
|
||||
{
|
||||
addFloatListener( inputTap );
|
||||
addSimplexSampleListener( inputTap );
|
||||
}
|
||||
break;
|
||||
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
|
||||
@@ -292,7 +292,7 @@ public class MPT1327Decoder extends Decoder implements Instrumentable
|
||||
}
|
||||
else
|
||||
{
|
||||
removeFloatListener( inputTap );
|
||||
removeSimplexListener( inputTap );
|
||||
}
|
||||
break;
|
||||
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
******************************************************************************/
|
||||
package decode.nbfm;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import decode.Decoder;
|
||||
import decode.DecoderType;
|
||||
import dsp.afc.AutomaticFrequencyControl;
|
||||
import dsp.filter.DCRemovalFilter2;
|
||||
import dsp.nbfm.FilteringNBFMDemodulator;
|
||||
|
||||
@@ -67,7 +66,7 @@ public class NBFMConventionalDecoder extends Decoder
|
||||
* Route the demodulated, filtered samples back to this class to send
|
||||
* to all registered listeners
|
||||
*/
|
||||
mDCRemovalFilter.setListener( this.getFloatReceiver() );
|
||||
mDCRemovalFilter.setListener( this.getSimplexReceiver() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +77,7 @@ public class NBFMConventionalDecoder extends Decoder
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUnfilteredFloatListener( Listener<Float> listener )
|
||||
public void addUnfilteredSimplexSampleListener( SimplexSampleListener listener )
|
||||
{
|
||||
if( mDemodulator != null )
|
||||
{
|
||||
|
||||
@@ -24,12 +24,11 @@ import instrument.tap.stream.FloatTap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import decode.Decoder;
|
||||
import decode.DecoderType;
|
||||
import dsp.afc.AutomaticFrequencyControl;
|
||||
import dsp.filter.DCRemovalFilter2;
|
||||
import dsp.fsk.C4FMDecoder;
|
||||
import dsp.nbfm.FilteringNBFMDemodulator;
|
||||
@@ -84,12 +83,12 @@ public class P25Decoder extends Decoder implements Instrumentable
|
||||
* Route the demodulated, filtered samples back to this class to send
|
||||
* to all registered listeners
|
||||
*/
|
||||
mDCRemovalFilter.setListener( this.getFloatReceiver() );
|
||||
mDCRemovalFilter.setListener( this.getSimplexReceiver() );
|
||||
|
||||
}
|
||||
|
||||
mC4FMDecoder = new C4FMDecoder();
|
||||
addFloatListener( mC4FMDecoder );
|
||||
addSimplexSampleListener( mC4FMDecoder );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,8 +120,8 @@ public class P25Decoder extends Decoder implements Instrumentable
|
||||
{
|
||||
case TAP_DEMOD_TO_DECODER:
|
||||
FloatTap demodTap = (FloatTap)tap;
|
||||
removeFloatListener( mC4FMDecoder );
|
||||
addFloatListener( demodTap );
|
||||
removeSimplexListener( mC4FMDecoder );
|
||||
addSimplexSampleListener( demodTap );
|
||||
demodTap.setListener( mC4FMDecoder );
|
||||
break;
|
||||
}
|
||||
@@ -137,17 +136,16 @@ public class P25Decoder extends Decoder implements Instrumentable
|
||||
{
|
||||
case TAP_DEMOD_TO_DECODER:
|
||||
FloatTap demodTap = (FloatTap)tap;
|
||||
removeFloatListener( demodTap );
|
||||
removeSimplexListener( demodTap );
|
||||
demodTap.removeListener( mC4FMDecoder );
|
||||
addFloatListener( mC4FMDecoder );
|
||||
addSimplexSampleListener( mC4FMDecoder );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUnfilteredFloatListener( Listener<Float> listener )
|
||||
public void addUnfilteredSimplexSampleListener( SimplexSampleListener listener )
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,14 +23,13 @@ import instrument.tap.Tap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import bits.MessageFramer;
|
||||
import bits.SyncPattern;
|
||||
import decode.Decoder;
|
||||
import decode.DecoderType;
|
||||
import dsp.afc.AutomaticFrequencyControl;
|
||||
import dsp.filter.DCRemovalFilter2;
|
||||
import dsp.fsk.LTRFSKDecoder;
|
||||
import dsp.nbfm.FilteringNBFMDemodulator;
|
||||
@@ -79,7 +78,7 @@ public class PassportDecoder extends Decoder implements Instrumentable
|
||||
*/
|
||||
mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO );
|
||||
mNBFMDemodulator.addListener( mDCRemovalFilter );
|
||||
mDCRemovalFilter.setListener( this.getFloatReceiver() );
|
||||
mDCRemovalFilter.setListener( this.getSimplexReceiver() );
|
||||
}
|
||||
|
||||
mPassportFSKDecoder =
|
||||
@@ -95,7 +94,7 @@ public class PassportDecoder extends Decoder implements Instrumentable
|
||||
}
|
||||
else
|
||||
{
|
||||
addFloatListener( mPassportFSKDecoder );
|
||||
addSimplexSampleListener( mPassportFSKDecoder );
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +141,7 @@ public class PassportDecoder extends Decoder implements Instrumentable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUnfilteredFloatListener( Listener<Float> listener )
|
||||
public void addUnfilteredSimplexSampleListener( SimplexSampleListener listener )
|
||||
{
|
||||
if( mNBFMDemodulator != null )
|
||||
{
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package dsp.afc;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import source.tuner.FrequencyChangeEvent;
|
||||
import source.tuner.FrequencyChangeListener;
|
||||
import source.tuner.FrequencyChangeEvent.Attribute;
|
||||
import source.tuner.FrequencyChangeListener;
|
||||
import buffer.FloatAveragingBuffer;
|
||||
|
||||
public class AutomaticFrequencyControl implements Listener<Float>,
|
||||
public class AutomaticFrequencyControl implements SimplexSampleListener,
|
||||
FrequencyChangeListener
|
||||
{
|
||||
private static float LARGE_ERROR = 10000.0f;
|
||||
@@ -65,11 +65,11 @@ public class AutomaticFrequencyControl implements Listener<Float>,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
if( mMode == Mode.FAST )
|
||||
{
|
||||
float average = mBuffer.get( sample.floatValue() );
|
||||
float average = mBuffer.get( sample );
|
||||
|
||||
mSampleCounter++;
|
||||
|
||||
@@ -88,7 +88,7 @@ public class AutomaticFrequencyControl implements Listener<Float>,
|
||||
{
|
||||
mSkipCounter = 0;
|
||||
|
||||
float average = mBuffer.get( sample.floatValue() );
|
||||
float average = mBuffer.get( sample );
|
||||
|
||||
mSampleCounter++;
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package dsp.am;
|
||||
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import sample.simplex.SimplexSampleBroadcaster;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import dsp.filter.ComplexFIRFilter;
|
||||
import dsp.filter.FilterFactory;
|
||||
import dsp.filter.Window.WindowType;
|
||||
|
||||
public class AMDemodulator implements Listener<ComplexSample>
|
||||
{
|
||||
private Broadcaster<Float> mBroadcaster = new Broadcaster<Float>();
|
||||
private SimplexSampleBroadcaster mBroadcaster = new SimplexSampleBroadcaster();
|
||||
private ComplexFIRFilter mIQFilter;
|
||||
|
||||
public AMDemodulator()
|
||||
@@ -34,7 +35,7 @@ public class AMDemodulator implements Listener<ComplexSample>
|
||||
/**
|
||||
* Adds a listener to receive demodulated samples
|
||||
*/
|
||||
public void addListener( Listener<Float> listener )
|
||||
public void addListener( SimplexSampleListener listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
@@ -42,7 +43,7 @@ public class AMDemodulator implements Listener<ComplexSample>
|
||||
/**
|
||||
* Removes a listener from receiving demodulated samples
|
||||
*/
|
||||
public void removeListener( Listener<Float> listener )
|
||||
public void removeListener( SimplexSampleListener listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
368
src/dsp/filter/ComplexPrimeCICDecimate.java
Normal file
368
src/dsp/filter/ComplexPrimeCICDecimate.java
Normal file
@@ -0,0 +1,368 @@
|
||||
package dsp.filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import sample.complex.ComplexSample;
|
||||
import sample.complex.ComplexSampleListener;
|
||||
import sample.decimator.ComplexDecimator;
|
||||
import dsp.filter.Window.WindowType;
|
||||
|
||||
public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
|
||||
{
|
||||
public static int[] PRIMES = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
|
||||
59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,
|
||||
157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,
|
||||
257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,
|
||||
367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,
|
||||
467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,
|
||||
599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691 };
|
||||
|
||||
private ArrayList<DecimatingStage> mDecimatingStages =
|
||||
new ArrayList<DecimatingStage>();
|
||||
|
||||
private DecimatingStage mFirstDecimatingStage;
|
||||
|
||||
public Broadcaster<ComplexSample> mBroadcaster =
|
||||
new Broadcaster<ComplexSample>();
|
||||
|
||||
public Output mOutput;
|
||||
/**
|
||||
* Non-Recursive Prime-Factor CIC Filter with float sample array inputs and
|
||||
* decimated, single paired i/q float sample output.
|
||||
*
|
||||
* Implements the CIC filter described in Understanding Digital Signal
|
||||
* Processing, 3e, Lyons, on page 769. This filter is comprised of multiple
|
||||
* decimating stages each with a prime factor decimation rate. Multiple
|
||||
* stages are cascaded to achieve the overall decimation rate.
|
||||
*
|
||||
* This filter supports a maximum decimation rate of 700. This filter can
|
||||
* be adapted to higher decimation rates by adding additional prime factors
|
||||
* to the PRIMES array.
|
||||
*
|
||||
* @param decimation - overall decimation rate
|
||||
* @param order - filter order
|
||||
*/
|
||||
public ComplexPrimeCICDecimate( int decimation, int order )
|
||||
{
|
||||
assert( decimation <= 700 );
|
||||
|
||||
List<Integer> stageSizes = getPrimeFactors( decimation );
|
||||
|
||||
for( int x = 0; x < stageSizes.size(); x++ )
|
||||
{
|
||||
DecimatingStage stage = new DecimatingStage( stageSizes.get( x ), order );
|
||||
|
||||
mDecimatingStages.add( stage );
|
||||
|
||||
if( x == 0 )
|
||||
{
|
||||
/* Reference to first stage -- will receive all samples */
|
||||
mFirstDecimatingStage = stage;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wire the current stage to the previous stage */
|
||||
mDecimatingStages.get( x - 1 ).addListener( stage );
|
||||
}
|
||||
}
|
||||
|
||||
mOutput = new Output( 48000, stageSizes.size(), decimation );
|
||||
|
||||
mDecimatingStages.get( mDecimatingStages.size() - 1 )
|
||||
.addListener( mOutput );
|
||||
|
||||
mOutput.setListener( mBroadcaster );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
for( DecimatingStage stage: mDecimatingStages )
|
||||
{
|
||||
stage.dispose();
|
||||
}
|
||||
|
||||
mDecimatingStages.clear();
|
||||
|
||||
mBroadcaster.dispose();
|
||||
|
||||
mBroadcaster = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to receive the output of this CIC decimation filter
|
||||
*/
|
||||
public void addListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes listener from output of this CIC decimation filter
|
||||
*/
|
||||
public void removeListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the prime factors of the decimation rate up to a maximum
|
||||
* decimation rate of 700. If you wish to have decimation rates higher
|
||||
* than 700, then add additional prime factors to the PRIMES array.
|
||||
*
|
||||
* @param decimation - integral decimation rate
|
||||
* @return - ordered list (smallest to largest) of prime factors
|
||||
*/
|
||||
public static List<Integer> getPrimeFactors( int decimation )
|
||||
{
|
||||
ArrayList<Integer> stages = new ArrayList<Integer>();
|
||||
|
||||
int pointer = 0;
|
||||
|
||||
while( decimation > 0 && pointer < PRIMES.length )
|
||||
{
|
||||
int prime = PRIMES[ pointer ];
|
||||
|
||||
if( decimation % prime == 0 )
|
||||
{
|
||||
stages.add( prime );
|
||||
|
||||
decimation /= prime;
|
||||
}
|
||||
else
|
||||
{
|
||||
pointer++;
|
||||
}
|
||||
}
|
||||
|
||||
return stages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary input method for receiving sample arrays composed as I,Q,I,Q, etc.
|
||||
*/
|
||||
@Override
|
||||
public void receive( ComplexBuffer sampleArray )
|
||||
{
|
||||
float[] samples = sampleArray.getSamples();
|
||||
|
||||
for( int x = 0; x < samples.length; x += 2 )
|
||||
{
|
||||
mFirstDecimatingStage.receive( samples[ x ], samples[ x + 1 ] );
|
||||
}
|
||||
|
||||
samples = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decimating stage combines multiple CIC stages with a decimator. The
|
||||
* number of stages is indicated by the order value and the size indicates
|
||||
* the decimation rate of this stage.
|
||||
*/
|
||||
public class DecimatingStage implements ComplexSampleListener
|
||||
{
|
||||
private ArrayList<Stage> mStages = new ArrayList<Stage>();
|
||||
private Stage mFirstStage;
|
||||
private ComplexDecimator mDecimator;
|
||||
|
||||
public DecimatingStage( int size, int order )
|
||||
{
|
||||
for( int x = 0; x < order; x++ )
|
||||
{
|
||||
Stage stage = new Stage( size );
|
||||
|
||||
mStages.add( stage );
|
||||
|
||||
if( x == 0 )
|
||||
{
|
||||
mFirstStage = stage;
|
||||
}
|
||||
else
|
||||
{
|
||||
mStages.get( x - 1 ).addListener( stage );
|
||||
}
|
||||
}
|
||||
|
||||
mDecimator = new ComplexDecimator( size );
|
||||
|
||||
mStages.get( mStages.size() - 1 ).addListener( mDecimator );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
for( Stage stage: mStages )
|
||||
{
|
||||
stage.dispose();
|
||||
}
|
||||
|
||||
mStages.clear();
|
||||
mDecimator.dispose();
|
||||
mDecimator = null;
|
||||
mFirstStage = null;
|
||||
mStages = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( float i, float q )
|
||||
{
|
||||
mFirstStage.receive( i, q );
|
||||
}
|
||||
|
||||
public void addListener( ComplexSampleListener listener )
|
||||
{
|
||||
mDecimator.setListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Single non-decimating CIC stage component. Uses a circular buffer
|
||||
* internally to implement the stage so that stage size has essentially
|
||||
* no impact on the performance of the stage
|
||||
*/
|
||||
public class Stage implements ComplexSampleListener
|
||||
{
|
||||
private ComplexSampleListener mListener;
|
||||
|
||||
private float[] mISamples;
|
||||
private float[] mQSamples;
|
||||
|
||||
private float mISum;
|
||||
private float mQSum;
|
||||
|
||||
private int mSamplePointer;
|
||||
private int mSize;
|
||||
|
||||
public Stage( int size )
|
||||
{
|
||||
mSize = size;
|
||||
mISamples = new float[ size ];
|
||||
mQSamples = new float[ size ];
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
mISamples = null;
|
||||
mQSamples = null;
|
||||
}
|
||||
|
||||
public void receive( float i, float q )
|
||||
{
|
||||
/* Subtract the oldest sample and add back in the newest */
|
||||
mISum = mISum - mISamples[ mSamplePointer ] + i;
|
||||
mQSum = mQSum - mISamples[ mSamplePointer ] + q;
|
||||
|
||||
/* Overwrite the oldest sample with the newest */
|
||||
mISamples[ mSamplePointer ] = i;
|
||||
mQSamples[ mSamplePointer ] = q;
|
||||
|
||||
mSamplePointer++;
|
||||
|
||||
if( mSamplePointer >= mSize )
|
||||
{
|
||||
mSamplePointer = 0;
|
||||
}
|
||||
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( mISum, mQSum );
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener( ComplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output adapter - applies gain correction, applies cleanup filter, casts
|
||||
* double-valued samples into float-valued complex sample and sends the
|
||||
* result to the registered listener.
|
||||
*/
|
||||
public class Output implements ComplexSampleListener
|
||||
{
|
||||
private int mStageCount;
|
||||
private int mDecimation;
|
||||
private double mGain = 1.0d;
|
||||
private ComplexFilter mCleanupFilter;
|
||||
|
||||
public Output( int outputSampleRate, int stageCount, int decimation )
|
||||
{
|
||||
mStageCount = stageCount;
|
||||
mDecimation = decimation;
|
||||
mCleanupFilter = new ComplexFIRFilter( FilterFactory
|
||||
.getCICCleanupFilter( outputSampleRate,
|
||||
stageCount,
|
||||
WindowType.BLACKMAN ), 1.0d );
|
||||
|
||||
setGain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Receiver method for the output adapter to receive a filtered,
|
||||
* decimated sample, apply gain correction, apply cleanup filtering
|
||||
* and output the sample values as a complex sample.
|
||||
*/
|
||||
@Override
|
||||
public void receive( float left, float right )
|
||||
{
|
||||
mCleanupFilter.receive(
|
||||
new ComplexSample( (float)( left * mGain ),
|
||||
(float)( right * mGain ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new decimation rate
|
||||
*/
|
||||
public void setDecimationRate( int rate )
|
||||
{
|
||||
mDecimation = rate;
|
||||
setGain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to receive output samples
|
||||
*/
|
||||
public void setListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
mCleanupFilter.setListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving output samples
|
||||
*/
|
||||
public void removeListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
mCleanupFilter.setListener( null );
|
||||
}
|
||||
|
||||
public Listener<ComplexSample> getListener()
|
||||
{
|
||||
return mCleanupFilter.getListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the gain correction factor for output samples.
|
||||
*
|
||||
* The CIC decimation filter induces significant gain as a factor of
|
||||
* delay, decimation rate and stage count. So, we apply a gain reduction
|
||||
* to offset the added gain estimate:
|
||||
*
|
||||
* gain = ( decimation * delay ) to power of stageCount
|
||||
*
|
||||
* Delay is ONE for the Comb used in this filter.
|
||||
*/
|
||||
private void setGain()
|
||||
{
|
||||
/* decimation is NOT multiplied by delay since delay is 1 */
|
||||
double gain = Math.pow( (double)mDecimation, (double)mStageCount );
|
||||
|
||||
mGain = 10.0d / gain;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,16 +18,17 @@
|
||||
package dsp.filter;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
/**
|
||||
* Implements the real-time general DC removal algorithm described on page
|
||||
* 762, figure 13-62d, in Understanding Digital Signal Processing 3e, Lyons.
|
||||
*/
|
||||
public class DCRemovalFilter implements Listener<Float>
|
||||
public class DCRemovalFilter implements SimplexSampleListener
|
||||
{
|
||||
private double mAlpha;
|
||||
private float mPrevious;
|
||||
private Listener<Float> mListener;
|
||||
private SimplexSampleListener mListener;
|
||||
|
||||
public DCRemovalFilter( double alpha )
|
||||
{
|
||||
@@ -40,7 +41,7 @@ public class DCRemovalFilter implements Listener<Float>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
send( (float)( sample + ( mPrevious * mAlpha ) - mPrevious ) );
|
||||
|
||||
@@ -58,7 +59,7 @@ public class DCRemovalFilter implements Listener<Float>
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<Float> listener )
|
||||
public void setListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@@ -18,12 +18,13 @@
|
||||
package dsp.filter;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
public class DCRemovalFilter2 implements Listener<Float>
|
||||
public class DCRemovalFilter2 implements SimplexSampleListener
|
||||
{
|
||||
private double mAverage;
|
||||
private double mRatio;
|
||||
private Listener<Float> mListener;
|
||||
private SimplexSampleListener mListener;
|
||||
|
||||
public DCRemovalFilter2( double ratio )
|
||||
{
|
||||
@@ -31,7 +32,7 @@ public class DCRemovalFilter2 implements Listener<Float>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
mAverage += mRatio * ( (double)sample - mAverage );
|
||||
|
||||
@@ -49,7 +50,7 @@ public class DCRemovalFilter2 implements Listener<Float>
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<Float> listener )
|
||||
public void setListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package dsp.filter;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
public class DCRemovalFilter3 implements Listener<Float>
|
||||
public class DCRemovalFilter3 implements SimplexSampleListener
|
||||
{
|
||||
private float mAlpha;
|
||||
private float mPreviousInput = 0.0f;
|
||||
private float mPreviousOutput = 0.0f;
|
||||
|
||||
private Listener<Float> mListener;
|
||||
private SimplexSampleListener mListener;
|
||||
|
||||
/**
|
||||
* IIR single-pole DC removal filter, as described by J M de Freitas in
|
||||
@@ -24,7 +24,7 @@ public class DCRemovalFilter3 implements Listener<Float>
|
||||
mAlpha = alpha;
|
||||
}
|
||||
@Override
|
||||
public void receive( Float currentInput )
|
||||
public void receive( float currentInput )
|
||||
{
|
||||
float currentOutput = ( currentInput - mPreviousInput ) +
|
||||
( mAlpha * mPreviousOutput );
|
||||
@@ -38,7 +38,7 @@ public class DCRemovalFilter3 implements Listener<Float>
|
||||
mPreviousOutput = currentOutput;
|
||||
}
|
||||
|
||||
public void setListener( Listener<Float> listener )
|
||||
public void setListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
******************************************************************************/
|
||||
package dsp.filter;
|
||||
|
||||
import gui.SDRTrunk;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@@ -19,11 +19,11 @@ package dsp.filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
public class FloatFIRFilter implements Listener<Float>
|
||||
public class FloatFIRFilter implements SimplexSampleListener
|
||||
{
|
||||
private Listener<Float> mListener;
|
||||
private SimplexSampleListener mListener;
|
||||
private ArrayList<Float> mBuffer;
|
||||
private int mBufferSize = 1; //Temporary initial value
|
||||
private int mBufferPointer = 0;
|
||||
@@ -61,12 +61,12 @@ public class FloatFIRFilter implements Listener<Float>
|
||||
return mCoefficients.length;
|
||||
}
|
||||
|
||||
public void setListener( Listener<Float> listener )
|
||||
public void setListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void receive( Float newSample )
|
||||
public void receive( float newSample )
|
||||
{
|
||||
send( get( newSample ) );
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ package dsp.filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
public class FloatHalfBandFilter implements Listener<Float>
|
||||
public class FloatHalfBandFilter implements SimplexSampleListener
|
||||
{
|
||||
private Listener<Float> mListener;
|
||||
private SimplexSampleListener mListener;
|
||||
private ArrayList<Float> mBuffer;
|
||||
private int mBufferSize = 1; //Temporary initial value
|
||||
private int mBufferPointer = 0;
|
||||
@@ -75,7 +75,7 @@ public class FloatHalfBandFilter implements Listener<Float>
|
||||
* Calculate the filtered value by applying the coefficients against
|
||||
* the complex samples in mBuffer
|
||||
*/
|
||||
public void receive( Float newSample )
|
||||
public void receive( float newSample )
|
||||
{
|
||||
//Add the new sample to the buffer
|
||||
mBuffer.set( mBufferPointer, newSample );
|
||||
@@ -188,7 +188,7 @@ public class FloatHalfBandFilter implements Listener<Float>
|
||||
/**
|
||||
* Registers a listener for filtered samples
|
||||
*/
|
||||
public void setListener( Listener<Float> listener )
|
||||
public void setListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ package dsp.filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
public class FloatHalfBandNoDecimateFilter implements Listener<Float>
|
||||
public class FloatHalfBandNoDecimateFilter implements SimplexSampleListener
|
||||
{
|
||||
private Listener<Float> mListener;
|
||||
private SimplexSampleListener mListener;
|
||||
private ArrayList<Float> mBuffer;
|
||||
private int mBufferSize = 1; //Temporary initial value
|
||||
private int mBufferPointer = 0;
|
||||
@@ -73,7 +73,7 @@ public class FloatHalfBandNoDecimateFilter implements Listener<Float>
|
||||
* Calculate the filtered value by applying the coefficients against
|
||||
* the complex samples in mBuffer
|
||||
*/
|
||||
public void receive( Float newSample )
|
||||
public void receive( float newSample )
|
||||
{
|
||||
//Add the new sample to the buffer
|
||||
mBuffer.set( mBufferPointer, newSample );
|
||||
@@ -178,7 +178,7 @@ public class FloatHalfBandNoDecimateFilter implements Listener<Float>
|
||||
/**
|
||||
* Registers a listener for filtered samples
|
||||
*/
|
||||
public void setListener( Listener<Float> listener )
|
||||
public void setListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
package dsp.filter;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
public class SquaringFilter implements Listener<Float>
|
||||
public class SquaringFilter implements SimplexSampleListener
|
||||
{
|
||||
private Listener<Boolean> mListener;
|
||||
|
||||
@@ -31,7 +32,7 @@ public class SquaringFilter implements Listener<Float>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
|
||||
@@ -7,9 +7,9 @@ import instrument.tap.stream.FloatTap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
public class C4FMDecoder implements Listener<Float>, Instrumentable
|
||||
public class C4FMDecoder implements SimplexSampleListener, Instrumentable
|
||||
{
|
||||
private final String TAP_C4FM_INPUT = "C4FM Input";
|
||||
private ArrayList<Tap> mTaps;
|
||||
@@ -56,7 +56,7 @@ public class C4FMDecoder implements Listener<Float>, Instrumentable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float t )
|
||||
public void receive( float t )
|
||||
{
|
||||
if( mInputTap != null )
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import buffer.BooleanAveragingBuffer;
|
||||
import dsp.fsk.SymbolEvent.Shift;
|
||||
|
||||
@@ -49,7 +50,7 @@ import dsp.fsk.SymbolEvent.Shift;
|
||||
* Implements instrumentable interface, so that slice events can be received
|
||||
* externally to analyze decoder performance.
|
||||
*/
|
||||
public class FSK2Decoder implements Instrumentable, Listener<Float>
|
||||
public class FSK2Decoder implements Instrumentable, SimplexSampleListener
|
||||
{
|
||||
public enum Output{ NORMAL, INVERTED };
|
||||
|
||||
@@ -93,7 +94,7 @@ public class FSK2Decoder implements Instrumentable, Listener<Float>
|
||||
* Primary sample input
|
||||
*/
|
||||
@Override
|
||||
public void receive( Float floatSample )
|
||||
public void receive( float floatSample )
|
||||
{
|
||||
/* Square the sample. Greater than zero is a 1 (true) and less than
|
||||
* zero is a 0 (false) */
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import dsp.Slicer;
|
||||
import dsp.Slicer.Output;
|
||||
import dsp.filter.DCRemovalFilter3;
|
||||
@@ -36,7 +37,7 @@ import dsp.filter.FloatHalfBandNoDecimateFilter;
|
||||
import dsp.filter.LTRPulseShapingFilter;
|
||||
import dsp.filter.SquaringFilter;
|
||||
|
||||
public class LTRFSKDecoder implements Listener<Float>, Instrumentable
|
||||
public class LTRFSKDecoder implements SimplexSampleListener, Instrumentable
|
||||
{
|
||||
private FloatHalfBandFilter mHBFilter1;
|
||||
private FloatHalfBandFilter mHBFilter2;
|
||||
@@ -104,7 +105,7 @@ public class LTRFSKDecoder implements Listener<Float>, Instrumentable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
mHBFilter1.receive( sample );
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ package dsp.nbfm;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
public class FMDiscriminator implements Listener<ComplexSample>
|
||||
{
|
||||
private Listener<Float> mListener;
|
||||
private SimplexSampleListener mListener;
|
||||
private ComplexSample mPreviousSample = new ComplexSample( 0.0f, 0.0f );
|
||||
private double mGain;
|
||||
|
||||
@@ -93,7 +94,7 @@ public class FMDiscriminator implements Listener<ComplexSample>
|
||||
mPreviousSample = currentSample;
|
||||
}
|
||||
|
||||
public void setListener( Listener<Float> listener )
|
||||
public void setListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
******************************************************************************/
|
||||
package dsp.nbfm;
|
||||
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import sample.simplex.SimplexSampleBroadcaster;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import dsp.filter.ComplexFIRFilter;
|
||||
import dsp.filter.FilterFactory;
|
||||
import dsp.filter.FloatFIRFilter;
|
||||
@@ -27,7 +28,7 @@ import dsp.filter.Window.WindowType;
|
||||
|
||||
public class FilteringNBFMDemodulator implements Listener<ComplexSample>
|
||||
{
|
||||
private Broadcaster<Float> mBroadcaster = new Broadcaster<Float>();
|
||||
private SimplexSampleBroadcaster mBroadcaster = new SimplexSampleBroadcaster();
|
||||
private ComplexFIRFilter mIQFilter;
|
||||
private FloatFIRFilter mAudioFilter;
|
||||
private FMDiscriminator mDiscriminator;
|
||||
@@ -77,7 +78,7 @@ public class FilteringNBFMDemodulator implements Listener<ComplexSample>
|
||||
/**
|
||||
* Adds a listener to receive demodulated samples
|
||||
*/
|
||||
public void addListener( Listener<Float> listener )
|
||||
public void addListener( SimplexSampleListener listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
@@ -85,7 +86,7 @@ public class FilteringNBFMDemodulator implements Listener<ComplexSample>
|
||||
/**
|
||||
* Removes a listener from receiving demodulated samples
|
||||
*/
|
||||
public void removeListener( Listener<Float> listener )
|
||||
public void removeListener( SimplexSampleListener listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
@@ -17,16 +17,17 @@
|
||||
******************************************************************************/
|
||||
package dsp.nbfm;
|
||||
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import sample.simplex.SimplexSampleBroadcaster;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import dsp.filter.ComplexFIRFilter;
|
||||
import dsp.filter.FilterFactory;
|
||||
import dsp.filter.Window.WindowType;
|
||||
|
||||
public class NBFMDemodulator implements Listener<ComplexSample>
|
||||
{
|
||||
private Broadcaster<Float> mBroadcaster = new Broadcaster<Float>();
|
||||
private SimplexSampleBroadcaster mBroadcaster = new SimplexSampleBroadcaster();
|
||||
private ComplexFIRFilter mIQFilter;
|
||||
private FMDiscriminator mDiscriminator;
|
||||
|
||||
@@ -77,7 +78,7 @@ public class NBFMDemodulator implements Listener<ComplexSample>
|
||||
/**
|
||||
* Adds a listener to receive demodulated samples
|
||||
*/
|
||||
public void addListener( Listener<Float> listener )
|
||||
public void addListener( SimplexSampleListener listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
@@ -85,7 +86,7 @@ public class NBFMDemodulator implements Listener<ComplexSample>
|
||||
/**
|
||||
* Removes a listener from receiving demodulated samples
|
||||
*/
|
||||
public void removeListener( Listener<Float> listener )
|
||||
public void removeListener( SimplexSampleListener listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import properties.SystemProperties;
|
||||
import source.tuner.Tuner;
|
||||
import source.tuner.TunerSelectionListener;
|
||||
import spectrum.SpectralDisplayPanel;
|
||||
import util.TimeStamp;
|
||||
@@ -144,15 +145,31 @@ public class SDRTrunk
|
||||
//init() the controller to load tuners and playlists
|
||||
mControllerPanel.getController().init();
|
||||
|
||||
//Fire first tuner selection so it is displayed in the spectrum/waterfall
|
||||
mControllerPanel.getController().fireFirstTunerSelected();
|
||||
|
||||
/**
|
||||
* Setup main JFrame window
|
||||
*/
|
||||
mMainGui.setTitle( "SDRTrunk" );
|
||||
mMainGui.setBounds( 100, 100, 1280, 800 );
|
||||
mMainGui.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
||||
|
||||
mControllerPanel.getController().addListener( new TunerSelectionListener()
|
||||
{
|
||||
@Override
|
||||
public void tunerSelected( Tuner tuner )
|
||||
{
|
||||
if( tuner != null )
|
||||
{
|
||||
mMainGui.setTitle( "SDRTrunk - " + tuner.getName() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mMainGui.setTitle( "SDRTrunk" );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
//Fire first tuner selection so it is displayed in the spectrum/waterfall
|
||||
mControllerPanel.getController().fireFirstTunerSelected();
|
||||
|
||||
mSpectralPanel.setPreferredSize( new Dimension( 1280, 400 ) );
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ public class DecoderViewFrame extends JInternalFrame
|
||||
else if( source.getSampleType() == SampleType.FLOAT )
|
||||
{
|
||||
FloatWaveSource fws = (FloatWaveSource)mWaveSource;
|
||||
fws.setListener( mDecoder.getFloatReceiver() );
|
||||
fws.setListener( mDecoder.getSimplexReceiver() );
|
||||
}
|
||||
|
||||
mDecoder.addMessageListener( this );
|
||||
|
||||
@@ -19,11 +19,11 @@ package instrument.tap.stream;
|
||||
|
||||
import instrument.tap.TapListener;
|
||||
import instrument.tap.TapType;
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
public class FloatTap extends StreamTap implements Listener<Float>
|
||||
public class FloatTap extends StreamTap implements SimplexSampleListener
|
||||
{
|
||||
private Listener<Float> mListener;
|
||||
private SimplexSampleListener mListener;
|
||||
|
||||
public FloatTap( String name,
|
||||
int delay,
|
||||
@@ -33,7 +33,7 @@ public class FloatTap extends StreamTap implements Listener<Float>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
@@ -46,12 +46,12 @@ public class FloatTap extends StreamTap implements Listener<Float>
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<Float> listener )
|
||||
public void setListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void removeListener( Listener<Float> listener )
|
||||
public void removeListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
@@ -28,14 +28,14 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import record.Recorder;
|
||||
import record.RecorderType;
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import util.waveaudio.WaveWriter;
|
||||
import buffer.FloatSampleBufferAssembler;
|
||||
|
||||
/**
|
||||
* Threaded WAVE audio recorder for recording mono float samples to a wave file
|
||||
*/
|
||||
public class FloatWaveRecorder extends Recorder implements Listener<Float>
|
||||
public class FloatWaveRecorder extends Recorder implements SimplexSampleListener
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( FloatWaveRecorder.class );
|
||||
@@ -129,7 +129,7 @@ public class FloatWaveRecorder extends Recorder implements Listener<Float>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
mCurrentBuffer.put( sample );
|
||||
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
* 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 source.mixer;
|
||||
package sample.adapter;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import source.Source.SampleType;
|
||||
import source.mixer.MixerChannel;
|
||||
|
||||
/**
|
||||
* Converts 16-bit little endian byte data into an array of floats
|
||||
@@ -39,9 +39,9 @@ public class ChannelShortAdapter extends SampleAdapter
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float[] convert( byte[] samples )
|
||||
public float[] convert( byte[] samples )
|
||||
{
|
||||
Float[] processed = new Float[ samples.length / 4 ];
|
||||
float[] processed = new float[ samples.length / 4 ];
|
||||
|
||||
int pointer = 0;
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
* 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;
|
||||
package sample.adapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import source.mixer.MixerChannel;
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
* 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;
|
||||
package sample.adapter;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
|
||||
public class FloatArrayToComplexAdapter implements Listener<Float[]>
|
||||
@@ -15,8 +15,7 @@
|
||||
* 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 source.mixer;
|
||||
|
||||
package sample.adapter;
|
||||
|
||||
/*
|
||||
* Adapter to convert byte data received from a data stream into float[]
|
||||
@@ -24,5 +23,5 @@ package source.mixer;
|
||||
*/
|
||||
public abstract class SampleAdapter
|
||||
{
|
||||
public abstract Float[] convert( byte[] samples );
|
||||
public abstract float[] convert( byte[] samples );
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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 source.mixer;
|
||||
package sample.adapter;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
@@ -28,9 +28,9 @@ public class ShortAdapter extends SampleAdapter
|
||||
private ByteOrder mByteOrder = ByteOrder.LITTLE_ENDIAN;
|
||||
|
||||
@Override
|
||||
public Float[] convert( byte[] samples )
|
||||
public float[] convert( byte[] samples )
|
||||
{
|
||||
Float[] processed = new Float[ samples.length / 2 ];
|
||||
float[] processed = new float[ samples.length / 2 ];
|
||||
|
||||
int pointer = 0;
|
||||
|
||||
42
src/sample/complex/ComplexBuffer.java
Normal file
42
src/sample/complex/ComplexBuffer.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package sample.complex;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ComplexBuffer
|
||||
{
|
||||
private float[] mSamples;
|
||||
|
||||
/**
|
||||
* Wrapper around float array containing interleaved I/Q samples
|
||||
*/
|
||||
public ComplexBuffer( float[] samples )
|
||||
{
|
||||
mSamples = samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a deep copy of the buffer
|
||||
*/
|
||||
public ComplexBuffer copyOf()
|
||||
{
|
||||
float[] copy = Arrays.copyOf( mSamples, mSamples.length );
|
||||
|
||||
return new ComplexBuffer( copy );
|
||||
}
|
||||
|
||||
/**
|
||||
* Interleaved I/Q float sample array
|
||||
*/
|
||||
public float[] getSamples()
|
||||
{
|
||||
return mSamples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup method to nullify all data and references
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mSamples = null;
|
||||
}
|
||||
}
|
||||
76
src/sample/complex/ComplexSampleBroadcaster.java
Normal file
76
src/sample/complex/ComplexSampleBroadcaster.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Broadcasts received float samples to multiple listeners
|
||||
*/
|
||||
public class ComplexSampleBroadcaster implements ComplexSampleListener
|
||||
{
|
||||
private CopyOnWriteArrayList<ComplexSampleListener> mListeners =
|
||||
new CopyOnWriteArrayList<ComplexSampleListener>();
|
||||
|
||||
@Override
|
||||
public void receive( float i, float q )
|
||||
{
|
||||
broadcast( i, q );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear listeners to prepare for garbage collection
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mListeners.clear();
|
||||
}
|
||||
|
||||
public boolean hasListeners()
|
||||
{
|
||||
return !mListeners.isEmpty();
|
||||
}
|
||||
|
||||
public int getListenerCount()
|
||||
{
|
||||
return mListeners.size();
|
||||
}
|
||||
|
||||
public void addListener( ComplexSampleListener listener )
|
||||
{
|
||||
mListeners.add( listener );
|
||||
}
|
||||
|
||||
public void removeListener( ComplexSampleListener listener )
|
||||
{
|
||||
mListeners.remove( listener );
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
mListeners.clear();
|
||||
}
|
||||
|
||||
public void broadcast( float i, float q )
|
||||
{
|
||||
for( ComplexSampleListener listener: mListeners )
|
||||
{
|
||||
listener.receive( i, q );
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/sample/complex/ComplexSampleListener.java
Normal file
6
src/sample/complex/ComplexSampleListener.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package sample.complex;
|
||||
|
||||
public interface ComplexSampleListener
|
||||
{
|
||||
public void receive( float i, float q );
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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;
|
||||
|
||||
@Deprecated
|
||||
public class RTLComplexSample extends ComplexSample
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static float[] mValues;
|
||||
|
||||
/**
|
||||
* Creates a lookup table that converts the 8-bit valued range from 0 - 255
|
||||
* into scaled float values of -128 to 0 to 127
|
||||
*/
|
||||
static
|
||||
{
|
||||
mValues = new float[ 256 ];
|
||||
|
||||
float scale = (float)( 1.0f / 127.0f );
|
||||
|
||||
for( int x = 0; x < 256; x++ )
|
||||
{
|
||||
mValues[ x ] = (float)( x - 128 ) * scale;
|
||||
}
|
||||
|
||||
mValues[ 128 ] = 0.0000001f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a complex sample from the sample format produced by the
|
||||
* RTL-2832 chip, where the byte values run from 0 - 255, but represent
|
||||
* the value -127 to 127
|
||||
*
|
||||
* @param left
|
||||
* @param right
|
||||
*/
|
||||
public RTLComplexSample( byte left, byte right )
|
||||
{
|
||||
super( mValues[ left & 0xFF ], mValues[ right & 0xFF ] );
|
||||
}
|
||||
}
|
||||
58
src/sample/decimator/ComplexDecimator.java
Normal file
58
src/sample/decimator/ComplexDecimator.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package sample.decimator;
|
||||
|
||||
import sample.complex.ComplexSampleListener;
|
||||
|
||||
|
||||
public class ComplexDecimator implements ComplexSampleListener
|
||||
{
|
||||
private ComplexSampleListener mListener;
|
||||
private int mCounter = 0;
|
||||
private int mDecimationRate;
|
||||
|
||||
/**
|
||||
* Constructs a new Decimator object with the specified decimation rate.
|
||||
*/
|
||||
public ComplexDecimator( int rate )
|
||||
{
|
||||
mDecimationRate = rate;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new decimation rate. Rate can be changed after this object
|
||||
* is constructed.
|
||||
*/
|
||||
public synchronized void setDecimationRate( int rate )
|
||||
{
|
||||
mDecimationRate = rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives samples allowing only 1 of every (rate) sample to go on
|
||||
* to the registered listener
|
||||
*/
|
||||
@Override
|
||||
public void receive( float i, float q )
|
||||
{
|
||||
mCounter++;
|
||||
|
||||
if( mCounter >= mDecimationRate )
|
||||
{
|
||||
mListener.receive( i, q );
|
||||
|
||||
mCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the decimated output listener
|
||||
*/
|
||||
public void setListener( ComplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,9 @@
|
||||
* 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;
|
||||
package sample.decimator;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
|
||||
public class Decimator<T> implements Listener<T>
|
||||
58
src/sample/decimator/SimplexDecimator.java
Normal file
58
src/sample/decimator/SimplexDecimator.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package sample.decimator;
|
||||
|
||||
import sample.complex.ComplexSampleListener;
|
||||
|
||||
|
||||
public class SimplexDecimator implements ComplexSampleListener
|
||||
{
|
||||
private ComplexSampleListener mListener;
|
||||
private int mCounter = 0;
|
||||
private int mDecimationRate;
|
||||
|
||||
/**
|
||||
* Constructs a new Decimator object with the specified decimation rate.
|
||||
*/
|
||||
public SimplexDecimator( int rate )
|
||||
{
|
||||
mDecimationRate = rate;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new decimation rate. Rate can be changed after this object
|
||||
* is constructed.
|
||||
*/
|
||||
public synchronized void setDecimationRate( int rate )
|
||||
{
|
||||
mDecimationRate = rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives samples allowing only 1 of every (rate) sample to go on
|
||||
* to the registered listener
|
||||
*/
|
||||
@Override
|
||||
public void receive( float i, float q )
|
||||
{
|
||||
mCounter++;
|
||||
|
||||
if( mCounter >= mDecimationRate )
|
||||
{
|
||||
mListener.receive( i, q );
|
||||
|
||||
mCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the decimated output listener
|
||||
*/
|
||||
public void setListener( ComplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
42
src/sample/simplex/SimplexBuffer.java
Normal file
42
src/sample/simplex/SimplexBuffer.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package sample.simplex;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SimplexBuffer
|
||||
{
|
||||
private float[] mSamples;
|
||||
|
||||
/**
|
||||
* Wrapper around float array containing float samples
|
||||
*/
|
||||
public SimplexBuffer( float[] samples )
|
||||
{
|
||||
mSamples = samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a deep copy of the buffer
|
||||
*/
|
||||
public SimplexBuffer copyOf()
|
||||
{
|
||||
float[] copy = Arrays.copyOf( mSamples, mSamples.length );
|
||||
|
||||
return new SimplexBuffer( copy );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample data
|
||||
*/
|
||||
public float[] getSamples()
|
||||
{
|
||||
return mSamples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup method to nullify all data and references
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mSamples = null;
|
||||
}
|
||||
}
|
||||
76
src/sample/simplex/SimplexSampleBroadcaster.java
Normal file
76
src/sample/simplex/SimplexSampleBroadcaster.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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.simplex;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Broadcasts a received float sample to multiple listeners
|
||||
*/
|
||||
public class SimplexSampleBroadcaster implements SimplexSampleListener
|
||||
{
|
||||
private CopyOnWriteArrayList<SimplexSampleListener> mListeners =
|
||||
new CopyOnWriteArrayList<SimplexSampleListener>();
|
||||
|
||||
@Override
|
||||
public void receive( float sample )
|
||||
{
|
||||
broadcast( sample );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear listeners to prepare for garbage collection
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mListeners.clear();
|
||||
}
|
||||
|
||||
public boolean hasListeners()
|
||||
{
|
||||
return !mListeners.isEmpty();
|
||||
}
|
||||
|
||||
public int getListenerCount()
|
||||
{
|
||||
return mListeners.size();
|
||||
}
|
||||
|
||||
public void addListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListeners.add( listener );
|
||||
}
|
||||
|
||||
public void removeListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListeners.remove( listener );
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
mListeners.clear();
|
||||
}
|
||||
|
||||
public void broadcast( float sample )
|
||||
{
|
||||
for( SimplexSampleListener listener: mListeners )
|
||||
{
|
||||
listener.receive( sample );
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/sample/simplex/SimplexSampleListener.java
Normal file
6
src/sample/simplex/SimplexSampleListener.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package sample.simplex;
|
||||
|
||||
public interface SimplexSampleListener
|
||||
{
|
||||
public void receive( float sample );
|
||||
}
|
||||
8
src/sample/simplex/SimplexSampleProvider.java
Normal file
8
src/sample/simplex/SimplexSampleProvider.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package sample.simplex;
|
||||
|
||||
public interface SimplexSampleProvider
|
||||
{
|
||||
public void setListener( SimplexSampleListener listener );
|
||||
|
||||
public void removeListener( SimplexSampleListener listener );
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package source;
|
||||
|
||||
import sample.Provider;
|
||||
import source.Source.SampleType;
|
||||
|
||||
public abstract class FloatArraySource extends Source implements Provider<Float[]>
|
||||
{
|
||||
public FloatArraySource( String name )
|
||||
{
|
||||
super( name, SampleType.FLOAT );
|
||||
}
|
||||
}
|
||||
13
src/source/SimplexSource.java
Normal file
13
src/source/SimplexSource.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package source;
|
||||
|
||||
import sample.Provider;
|
||||
import sample.simplex.SimplexBuffer;
|
||||
|
||||
|
||||
public abstract class SimplexSource extends Source implements Provider<SimplexBuffer>
|
||||
{
|
||||
public SimplexSource( String name )
|
||||
{
|
||||
super( name, SampleType.FLOAT );
|
||||
}
|
||||
}
|
||||
273
src/source/mixer/ComplexMixer.java
Normal file
273
src/source/mixer/ComplexMixer.java
Normal file
@@ -0,0 +1,273 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 source.mixer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.TargetDataLine;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.adapter.SampleAdapter;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import sample.complex.ComplexSample;
|
||||
import source.SourceException;
|
||||
|
||||
public class ComplexMixer
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( ComplexMixer.class );
|
||||
|
||||
private long mFrequency = 0;
|
||||
private int mBufferSize = 16384;
|
||||
|
||||
private BufferReader mBufferReader = new BufferReader();
|
||||
private TargetDataLine mTargetDataLine;
|
||||
private AudioFormat mAudioFormat;
|
||||
private String mName;
|
||||
private int mBytesPerFrame = 0;
|
||||
private SampleAdapter mSampleAdapter;
|
||||
private Listener<ComplexBuffer> mListener;
|
||||
|
||||
/**
|
||||
* Complex Mixer - constructs a reader on the mixer/sound card target
|
||||
* data line using the specified audio format (sample size, sample rate )
|
||||
* and broadcasts complex (I/Q) sample buffers to all registered listeners.
|
||||
* Reads sample buffers sized to 10% of the sample rate specified in audio
|
||||
* format.
|
||||
*
|
||||
* @param targetDataLine - mixer or sound card to be used
|
||||
*
|
||||
* @param format - audio format
|
||||
*
|
||||
* @param name - token name to use for this mixer
|
||||
*
|
||||
* @param sampleAdapter - adapter to convert byte array data read from the
|
||||
* mixer into ComplexBuffer. The adapter can optionally invert the channel
|
||||
* data if the left/right stereo channels are inverted.
|
||||
*/
|
||||
public ComplexMixer( TargetDataLine targetDataLine,
|
||||
AudioFormat format,
|
||||
String name,
|
||||
SampleAdapter sampleAdapter,
|
||||
Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mTargetDataLine = targetDataLine;
|
||||
mName = name;
|
||||
mAudioFormat = format;
|
||||
mSampleAdapter = sampleAdapter;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public TargetDataLine getTargetDataLine()
|
||||
{
|
||||
return mTargetDataLine;
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
if( !mBufferReader.isRunning() )
|
||||
{
|
||||
Thread thread = new Thread( mBufferReader );
|
||||
thread.setDaemon( true );
|
||||
thread.setName( mName + " Complex Sample Reader" );
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
mBufferReader.stop();
|
||||
}
|
||||
|
||||
private List<ComplexSample> convert( ComplexBuffer sampleBuffer )
|
||||
{
|
||||
float[] samples = sampleBuffer.getSamples();
|
||||
|
||||
ArrayList<ComplexSample> converted = new ArrayList<ComplexSample>();
|
||||
|
||||
for( int x = 0; x < samples.length; x += 2 )
|
||||
{
|
||||
converted.add( new ComplexSample( samples[ x ], samples[ x + 1 ] ) );
|
||||
}
|
||||
|
||||
sampleBuffer.dispose();
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
public int getSampleRate()
|
||||
{
|
||||
if( mTargetDataLine != null )
|
||||
{
|
||||
return (int)mTargetDataLine.getFormat().getSampleRate();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the frequency of this source. Default is 0.
|
||||
*/
|
||||
public long getFrequency() throws SourceException
|
||||
{
|
||||
return mFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the frequency that will be returned from this source. This may
|
||||
* be useful if you are streaming an external audio source in through the
|
||||
* sound card and you want to specify a frequency for that source
|
||||
*/
|
||||
public void setFrequency( long frequency )
|
||||
{
|
||||
mFrequency = frequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reader thread. Performs blocking read against the mixer target data
|
||||
* line, converts the samples to an array of floats using the specified
|
||||
* adapter. Dispatches float arrays to all registered listeners.
|
||||
*/
|
||||
public class BufferReader implements Runnable
|
||||
{
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if( mRunning.compareAndSet( false, true ) )
|
||||
{
|
||||
if( mTargetDataLine == null )
|
||||
{
|
||||
mRunning.set( false );
|
||||
|
||||
mLog.error( "ComplexMixerSource - mixer target data line"
|
||||
+ " is null" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mBytesPerFrame = mAudioFormat.getSampleSizeInBits() / 8;
|
||||
|
||||
if( mBytesPerFrame == AudioSystem.NOT_SPECIFIED )
|
||||
{
|
||||
mBytesPerFrame = 2;
|
||||
}
|
||||
|
||||
/* Set buffer size to 1/10 second of samples */
|
||||
mBufferSize = (int)( mAudioFormat.getSampleRate()
|
||||
* 0.05 ) * mBytesPerFrame;
|
||||
|
||||
/* We'll reuse the same buffer for each read */
|
||||
byte[] buffer = new byte[ mBufferSize ];
|
||||
|
||||
try
|
||||
{
|
||||
mTargetDataLine.open( mAudioFormat );
|
||||
|
||||
mTargetDataLine.start();
|
||||
}
|
||||
catch ( LineUnavailableException e )
|
||||
{
|
||||
mLog.error( "ComplexMixerSource - mixer target data line"
|
||||
+ "not available to read data from", e );
|
||||
|
||||
mRunning.set( false );
|
||||
}
|
||||
|
||||
while( mRunning.get() )
|
||||
{
|
||||
try
|
||||
{
|
||||
/* Blocking read - waits until the buffer fills */
|
||||
mTargetDataLine.read( buffer, 0, buffer.length );
|
||||
|
||||
/* Convert samples to float array */
|
||||
float[] samples =
|
||||
mSampleAdapter.convert( buffer );
|
||||
|
||||
/* Dispatch samples to registered listeners */
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( new ComplexBuffer( samples ) );
|
||||
}
|
||||
}
|
||||
catch ( Exception e )
|
||||
{
|
||||
mLog.error( "error while reading"
|
||||
+ "from the mixer target data line", e );
|
||||
|
||||
mRunning.set( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Close the data line if it is still open */
|
||||
if( mTargetDataLine != null && mTargetDataLine.isOpen() )
|
||||
{
|
||||
mTargetDataLine.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the reader thread
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
if( mRunning.compareAndSet( true, false ) )
|
||||
{
|
||||
mTargetDataLine.stop();
|
||||
mTargetDataLine.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the reader thread is running
|
||||
*/
|
||||
public boolean isRunning()
|
||||
{
|
||||
return mRunning.get();
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
if( mBufferReader != null )
|
||||
{
|
||||
mBufferReader.stop();
|
||||
}
|
||||
|
||||
mListener = null;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
******************************************************************************/
|
||||
package source.mixer;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sound.sampled.Mixer;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
@@ -27,12 +27,13 @@ import javax.swing.JLabel;
|
||||
import source.SourceEditor;
|
||||
import source.config.SourceConfigMixer;
|
||||
import source.config.SourceConfiguration;
|
||||
import source.mixer.MixerManager.DiscoveredMixer;
|
||||
import controller.ResourceManager;
|
||||
|
||||
public class MixerEditor extends SourceEditor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private JComboBox<String> mComboMixers;
|
||||
private JComboBox<DiscoveredMixer> mComboMixers;
|
||||
private JComboBox<MixerChannel>mComboChannels;
|
||||
protected Mixer.Info mSelectedMixer = null;
|
||||
|
||||
@@ -50,7 +51,7 @@ public class MixerEditor extends SourceEditor
|
||||
JLabel mixerLabel = new JLabel( "Mixer:" );
|
||||
add( mixerLabel, "align right" );
|
||||
|
||||
mComboMixers = new JComboBox<String>();
|
||||
mComboMixers = new JComboBox<DiscoveredMixer>();
|
||||
|
||||
add( mComboMixers, "wrap" );
|
||||
|
||||
@@ -76,11 +77,12 @@ public class MixerEditor extends SourceEditor
|
||||
{
|
||||
SourceConfigMixer config = (SourceConfigMixer)mConfig;
|
||||
|
||||
String mixer = mComboMixers.getItemAt( mComboMixers.getSelectedIndex() );
|
||||
DiscoveredMixer selectedMixer =
|
||||
mComboMixers.getItemAt( mComboMixers.getSelectedIndex() );
|
||||
|
||||
if( mixer != null )
|
||||
if( selectedMixer != null )
|
||||
{
|
||||
config.setMixer( mixer );
|
||||
config.setMixer( selectedMixer.getMixerName() );
|
||||
}
|
||||
|
||||
MixerChannel channel = mComboChannels.getItemAt(
|
||||
@@ -103,16 +105,16 @@ public class MixerEditor extends SourceEditor
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Set<String> mixers = MixerManager.getInstance().getMixers();
|
||||
String[] mixerStrings = mixers.toArray( new String[ mixers.size() ] );
|
||||
DiscoveredMixer[] mixers = MixerManager.getInstance().getMixers();
|
||||
|
||||
mComboMixers.setModel(
|
||||
new DefaultComboBoxModel<String>( mixerStrings ) );
|
||||
new DefaultComboBoxModel<DiscoveredMixer>( mixers ) );
|
||||
|
||||
SourceConfigMixer config = (SourceConfigMixer)mConfig;
|
||||
|
||||
String mixer = config.getMixer();
|
||||
|
||||
if( mixer != null && mixers.contains( mixer ) )
|
||||
if( mixer != null )
|
||||
{
|
||||
mComboMixers.setSelectedItem( mixer );
|
||||
}
|
||||
@@ -121,11 +123,14 @@ public class MixerEditor extends SourceEditor
|
||||
//Mixer either hasn't been set yet, or it was set to a
|
||||
//mixer that is no longer available. Set the mixer to the
|
||||
//first item, and then store the value
|
||||
if( mixers.size() > 0 )
|
||||
if( mixers.length > 0 )
|
||||
{
|
||||
mComboMixers.setSelectedIndex( 0 );
|
||||
|
||||
config.setMixer( mComboMixers.getItemAt( 0 ) );
|
||||
DiscoveredMixer selectedMixer =
|
||||
(DiscoveredMixer)mComboMixers.getSelectedItem();
|
||||
|
||||
config.setMixer( selectedMixer.getMixerName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,20 +17,25 @@
|
||||
******************************************************************************/
|
||||
package source.mixer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import javax.sound.sampled.Line;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.Mixer;
|
||||
import javax.sound.sampled.TargetDataLine;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.adapter.ChannelShortAdapter;
|
||||
import sample.adapter.ShortAdapter;
|
||||
import source.config.SourceConfigMixer;
|
||||
import source.config.SourceConfiguration;
|
||||
import source.tuner.MixerTunerDataLine;
|
||||
@@ -44,8 +49,8 @@ public class MixerManager
|
||||
|
||||
private static MixerManager sInstance = null;
|
||||
|
||||
private HashMap<String,TargetDataLine> mMixerLines =
|
||||
new HashMap<String,TargetDataLine>();
|
||||
private ArrayList<DiscoveredMixer> mDiscoveredMixers =
|
||||
new ArrayList<DiscoveredMixer>();
|
||||
|
||||
private HashMap<String,MixerTunerDataLine> mMixerTuners =
|
||||
new HashMap<String,MixerTunerDataLine>();
|
||||
@@ -77,52 +82,109 @@ public class MixerManager
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public MixerSource getSource( ProcessingChain channel )
|
||||
public SimplexMixerSource getSource( ProcessingChain processingChain )
|
||||
{
|
||||
MixerSource retVal = null;
|
||||
SimplexMixerSource retVal = null;
|
||||
|
||||
SourceConfiguration config =
|
||||
channel.getChannel().getSourceConfiguration();
|
||||
processingChain.getChannel().getSourceConfiguration();
|
||||
|
||||
if( config instanceof SourceConfigMixer )
|
||||
{
|
||||
SourceConfigMixer mixerConfig = (SourceConfigMixer)config;
|
||||
|
||||
//Get the name of the requested mixer
|
||||
String mixerName = mixerConfig.getMixer();
|
||||
|
||||
if( mixerName != null )
|
||||
{
|
||||
TargetDataLine tdl = mMixerLines.get( mixerName );
|
||||
DiscoveredMixer mixer = getDiscoveredMixer( mixerName );
|
||||
|
||||
if( tdl != null )
|
||||
if( mixer != null )
|
||||
{
|
||||
switch( mixerConfig.getChannel() )
|
||||
MixerChannel channel = mixerConfig.getChannel();
|
||||
|
||||
if( mixer.supportsChannel( channel ) )
|
||||
{
|
||||
case MONO:
|
||||
retVal = new MixerSource( mMixerLines.get( mixerName ),
|
||||
MONO,
|
||||
mixerName,
|
||||
new ShortAdapter() );
|
||||
break;
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
retVal = new MixerSource( mMixerLines.get( mixerName ),
|
||||
STEREO,
|
||||
mixerName,
|
||||
new ChannelShortAdapter( mixerConfig.getChannel() ) );
|
||||
break;
|
||||
|
||||
if( channel == MixerChannel.MONO )
|
||||
{
|
||||
DataLine.Info info =
|
||||
new DataLine.Info(TargetDataLine.class, MONO );
|
||||
|
||||
TargetDataLine dataLine;
|
||||
|
||||
try
|
||||
{
|
||||
dataLine = (TargetDataLine)mixer
|
||||
.getMixer().getLine( info );
|
||||
|
||||
if( dataLine != null )
|
||||
{
|
||||
return new SimplexMixerSource( dataLine,
|
||||
MONO,
|
||||
mixerName,
|
||||
new ShortAdapter() );
|
||||
}
|
||||
}
|
||||
catch ( LineUnavailableException e )
|
||||
{
|
||||
mLog.error( "couldn't get mixer data line " +
|
||||
"for [" + mixerName + "] for channel [" +
|
||||
channel.name() + "]", e );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
DataLine.Info info =
|
||||
new DataLine.Info(TargetDataLine.class, STEREO );
|
||||
|
||||
TargetDataLine dataLine;
|
||||
|
||||
try
|
||||
{
|
||||
dataLine = (TargetDataLine)mixer
|
||||
.getMixer().getLine( info );
|
||||
|
||||
if( dataLine != null )
|
||||
{
|
||||
return new SimplexMixerSource( dataLine, STEREO,
|
||||
mixerName, new ChannelShortAdapter(
|
||||
mixerConfig.getChannel() ) );
|
||||
}
|
||||
}
|
||||
catch ( LineUnavailableException e )
|
||||
{
|
||||
mLog.error( "couldn't get mixer data line " +
|
||||
"for [" + mixerName + "] for channel [" +
|
||||
channel.name() + "]", e );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<String> getMixers()
|
||||
public DiscoveredMixer[] getMixers()
|
||||
{
|
||||
return mMixerLines.keySet();
|
||||
return mDiscoveredMixers.toArray(
|
||||
new DiscoveredMixer[ mDiscoveredMixers.size() ] );
|
||||
}
|
||||
|
||||
public DiscoveredMixer getDiscoveredMixer( String name )
|
||||
{
|
||||
for( DiscoveredMixer mixer: mDiscoveredMixers )
|
||||
{
|
||||
if( mixer.getMixerName().contentEquals( name ) )
|
||||
{
|
||||
return mixer;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Collection<MixerTunerDataLine> getMixerTunerDataLines()
|
||||
@@ -147,22 +209,27 @@ public class MixerManager
|
||||
|
||||
if( mixer != null )
|
||||
{
|
||||
TargetDataLine tdl = getTargetDataLine( mixerInfo, STEREO );
|
||||
|
||||
if( tdl != null )
|
||||
EnumSet<MixerChannel> channels = getSupportedChannels( mixer );
|
||||
|
||||
if( channels != null )
|
||||
{
|
||||
mMixerLines.put( mixerInfo.getName(), tdl );
|
||||
|
||||
sb.append( "\t[LOADED] Mixer:" + mixerInfo.getName() + "\n" );
|
||||
mDiscoveredMixers.add(
|
||||
new DiscoveredMixer( mixer, channels ) );
|
||||
|
||||
sb.append( "\t[LOADED] Mixer:" + mixerInfo.getName() +
|
||||
" CHANNELS: " + channels + "\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "\t[NOT LOADED] Mixer:" + mixerInfo.getName() + " - audio format not supported\n" );
|
||||
sb.append( "\t[NOT LOADED] Mixer:" +
|
||||
mixerInfo.getName() +
|
||||
" - audio format not supported\n" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "\t[NOT LOADED] Mixer:" + mixerInfo.getName() + " - couldn't get mixer\n" );
|
||||
sb.append( "\t[NOT LOADED] Mixer:" + mixerInfo.getName() +
|
||||
" - couldn't get mixer\n" );
|
||||
}
|
||||
}
|
||||
else //Process as a Mixer-based tuner data line
|
||||
@@ -226,6 +293,38 @@ public class MixerManager
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private EnumSet<MixerChannel> getSupportedChannels( Mixer mixer )
|
||||
{
|
||||
DataLine.Info stereoInfo=
|
||||
new DataLine.Info(TargetDataLine.class, STEREO );
|
||||
|
||||
boolean stereoSupported = mixer.isLineSupported( stereoInfo );
|
||||
|
||||
DataLine.Info monoInfo=
|
||||
new DataLine.Info(TargetDataLine.class, MONO );
|
||||
|
||||
boolean monoSupported = mixer.isLineSupported( monoInfo );
|
||||
|
||||
if( stereoSupported && monoSupported )
|
||||
{
|
||||
return EnumSet.of( MixerChannel.LEFT,
|
||||
MixerChannel.RIGHT,
|
||||
MixerChannel.MONO );
|
||||
}
|
||||
else if( stereoSupported )
|
||||
{
|
||||
return EnumSet.of( MixerChannel.LEFT,
|
||||
MixerChannel.RIGHT,
|
||||
MixerChannel.MONO );
|
||||
}
|
||||
else if( monoSupported )
|
||||
{
|
||||
return EnumSet.of( MixerChannel.MONO );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getMixerDevices()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -264,4 +363,41 @@ public class MixerManager
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public class DiscoveredMixer
|
||||
{
|
||||
private Mixer mMixer;
|
||||
private EnumSet<MixerChannel> mChannels;
|
||||
|
||||
public DiscoveredMixer( Mixer mixer, EnumSet<MixerChannel> channels )
|
||||
{
|
||||
mMixer = mixer;
|
||||
mChannels = channels;
|
||||
}
|
||||
|
||||
public Mixer getMixer()
|
||||
{
|
||||
return mMixer;
|
||||
}
|
||||
|
||||
public String getMixerName()
|
||||
{
|
||||
return mMixer.getMixerInfo().getName();
|
||||
}
|
||||
|
||||
public EnumSet<MixerChannel> getChannels()
|
||||
{
|
||||
return mChannels;
|
||||
}
|
||||
|
||||
public boolean supportsChannel( MixerChannel channel )
|
||||
{
|
||||
return mChannels.contains( channel );
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return getMixerName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
******************************************************************************/
|
||||
package source.mixer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -31,13 +30,15 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import source.FloatArraySource;
|
||||
import sample.adapter.SampleAdapter;
|
||||
import sample.simplex.SimplexBuffer;
|
||||
import source.SimplexSource;
|
||||
import source.SourceException;
|
||||
|
||||
public class MixerSource extends FloatArraySource
|
||||
public class SimplexMixerSource extends SimplexSource
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( MixerSource.class );
|
||||
LoggerFactory.getLogger( SimplexMixerSource.class );
|
||||
|
||||
private long mFrequency = 0;
|
||||
private int mBufferSize = 16384;
|
||||
@@ -49,14 +50,14 @@ public class MixerSource extends FloatArraySource
|
||||
private int mBytesPerFrame = 0;
|
||||
private SampleAdapter mSampleAdapter;
|
||||
|
||||
CopyOnWriteArrayList<Listener<Float[]>> mSampleListeners =
|
||||
new CopyOnWriteArrayList<Listener<Float[]>>();
|
||||
CopyOnWriteArrayList<Listener<SimplexBuffer>> mSampleListeners =
|
||||
new CopyOnWriteArrayList<Listener<SimplexBuffer>>();
|
||||
|
||||
/**
|
||||
* Complex Mixer Source - constructs a reader on the mixer/sound card target
|
||||
* Simplex Mixer Source - constructs a reader on the mixer/sound card target
|
||||
* data line using the specified audio format (sample size, sample rate )
|
||||
* and broadcasts float[] sample data to all registered listeners. Reads
|
||||
* sample buffers sized to 10% of the sample rate specified in audio format.
|
||||
* and broadcasts simplex buffers (float arrays) to all registered listeners.
|
||||
* Reads buffers sized to 10% of the sample rate specified in audio format.
|
||||
*
|
||||
* @param targetDataLine - mixer or sound card to be used
|
||||
*
|
||||
@@ -68,7 +69,7 @@ public class MixerSource extends FloatArraySource
|
||||
* mixer into float array data. Can optionally invert the channel data if
|
||||
* the left/right stereo channels are inverted.
|
||||
*/
|
||||
public MixerSource( TargetDataLine targetDataLine,
|
||||
public SimplexMixerSource( TargetDataLine targetDataLine,
|
||||
AudioFormat format,
|
||||
String name,
|
||||
SampleAdapter sampleAdapter )
|
||||
@@ -80,7 +81,7 @@ public class MixerSource extends FloatArraySource
|
||||
mSampleAdapter = sampleAdapter;
|
||||
}
|
||||
|
||||
public void setListener( Listener<Float[]> listener )
|
||||
public void setListener( Listener<SimplexBuffer> listener )
|
||||
{
|
||||
mSampleListeners.add( listener );
|
||||
|
||||
@@ -88,12 +89,13 @@ public class MixerSource extends FloatArraySource
|
||||
if( !mBufferReader.isRunning() )
|
||||
{
|
||||
Thread thread = new Thread( mBufferReader );
|
||||
thread.setDaemon( true );
|
||||
thread.setName( getName() + " Sample Reader" );
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListener( Listener<Float[]> listener )
|
||||
public void removeListener( Listener<SimplexBuffer> listener )
|
||||
{
|
||||
mSampleListeners.remove( listener );
|
||||
|
||||
@@ -104,19 +106,19 @@ public class MixerSource extends FloatArraySource
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcast( Float[] samples )
|
||||
public void broadcast( SimplexBuffer samples )
|
||||
{
|
||||
Iterator<Listener<Float[]>> it = mSampleListeners.iterator();
|
||||
Iterator<Listener<SimplexBuffer>> it = mSampleListeners.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
Listener<Float[]> next = it.next();
|
||||
Listener<SimplexBuffer> next = it.next();
|
||||
|
||||
/* if this is the last (or only) listener, send him the original
|
||||
* buffer, otherwise send him a copy of the buffer */
|
||||
if( it.hasNext() )
|
||||
{
|
||||
next.receive( Arrays.copyOf( samples, samples.length ) );
|
||||
next.receive( samples.copyOf() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -220,11 +222,11 @@ public class MixerSource extends FloatArraySource
|
||||
mTargetDataLine.read( buffer, 0, buffer.length );
|
||||
|
||||
/* Convert samples to float array */
|
||||
Float[] convertedSamples =
|
||||
float[] samples =
|
||||
mSampleAdapter.convert( buffer );
|
||||
|
||||
/* Dispatch samples to registered listeners */
|
||||
broadcast( convertedSamples );
|
||||
broadcast( new SimplexBuffer( samples ) );
|
||||
}
|
||||
catch ( Exception e )
|
||||
{
|
||||
@@ -25,25 +25,25 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.adapter.SampleAdapter;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import sample.complex.ComplexSampleListListener;
|
||||
import source.mixer.MixerSource;
|
||||
import source.mixer.SampleAdapter;
|
||||
import source.mixer.ComplexMixer;
|
||||
|
||||
public abstract class MixerTuner extends Tuner
|
||||
public abstract class MixerTuner extends Tuner implements Listener<ComplexBuffer>
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( MixerTuner.class );
|
||||
|
||||
private TargetDataLine mTargetDataLine;
|
||||
protected MixerTunerType mMixerTunerType;
|
||||
protected MixerSource mMixerSource;
|
||||
protected ComplexMixer mComplexMixer;
|
||||
protected ArrayList<ComplexSampleListListener> mListeners =
|
||||
new ArrayList<ComplexSampleListListener>();
|
||||
|
||||
/**
|
||||
* Provides a wrapper around a MixerSource to treat it like a Tuner that
|
||||
* can provide ComplexSample<Short> samples. Subclasses should couple this
|
||||
* functionality with a tuner control class to allow tuning.
|
||||
* Wrapper class to add basic Tuner functionality to the ComplexMixer.
|
||||
* Subclasses should couple this functionality with a tuner controller class
|
||||
* to support tuning.
|
||||
*/
|
||||
public MixerTuner( String name,
|
||||
MixerTunerType mixerTunerType,
|
||||
@@ -54,12 +54,11 @@ public abstract class MixerTuner extends Tuner
|
||||
|
||||
mMixerTunerType = mixerTunerType;
|
||||
|
||||
mTargetDataLine = targetDataLine;
|
||||
|
||||
mMixerSource = new MixerSource( targetDataLine,
|
||||
mMixerTunerType.getAudioFormat(),
|
||||
name,
|
||||
sampleAdapter );
|
||||
mComplexMixer = new ComplexMixer( targetDataLine,
|
||||
mMixerTunerType.getAudioFormat(),
|
||||
name,
|
||||
sampleAdapter,
|
||||
(Listener<ComplexBuffer>)this );
|
||||
}
|
||||
|
||||
public MixerTuner( String name,
|
||||
@@ -72,6 +71,13 @@ public abstract class MixerTuner extends Tuner
|
||||
sampleAdapter );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mComplexMixer.stop();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public MixerTunerType getMixerTunerType()
|
||||
{
|
||||
return mMixerTunerType;
|
||||
@@ -79,26 +85,44 @@ public abstract class MixerTuner extends Tuner
|
||||
|
||||
public TargetDataLine getTargetDataLine()
|
||||
{
|
||||
return mTargetDataLine;
|
||||
return mComplexMixer.getTargetDataLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the Tuner listener management methods and allows the
|
||||
* complex mixer source to manage sample listeners
|
||||
* Dispatches the received complex buffers from the mComplexMixer to all
|
||||
* listeners registered on the parent Tuner class
|
||||
*/
|
||||
@Override
|
||||
public void addListener( Listener<Float[]> listener )
|
||||
public void receive( ComplexBuffer buffer )
|
||||
{
|
||||
super.broadcast( buffer );
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the parent Tuner class listener management method to allow
|
||||
* starting the buffer processing thread
|
||||
*/
|
||||
@Override
|
||||
public void addListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mMixerSource.setListener( listener );
|
||||
super.addListener( listener );
|
||||
|
||||
/* We can call start multiple times -- it ignores additional requests */
|
||||
mComplexMixer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the Tuner listener management methods and allows the
|
||||
* complex mixer source to manage sample listeners
|
||||
* Overrides the parent Tuner class listener management method to allow
|
||||
* stopping the buffer processing thread when there are no more listeners
|
||||
*/
|
||||
@Override
|
||||
public void removeListener( Listener<Float[]> listener )
|
||||
public void removeListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mMixerSource.removeListener( listener );
|
||||
super.removeListener( listener );
|
||||
|
||||
if( mSampleListeners.isEmpty() )
|
||||
{
|
||||
mComplexMixer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.concurrent.RejectedExecutionException;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import source.SourceException;
|
||||
import source.tuner.FrequencyChangeEvent.Attribute;
|
||||
import controller.ResourceManager;
|
||||
@@ -37,8 +38,12 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
|
||||
{
|
||||
private String mName;
|
||||
|
||||
protected CopyOnWriteArrayList<Listener<Float[]>>
|
||||
mSampleListeners = new CopyOnWriteArrayList<Listener<Float[]>>();
|
||||
/**
|
||||
* Sample Listeners - these will typically be the DFT processor for spectral
|
||||
* display, or it will be one or more tuner channel sources
|
||||
*/
|
||||
protected CopyOnWriteArrayList<Listener<ComplexBuffer>>
|
||||
mSampleListeners = new CopyOnWriteArrayList<Listener<ComplexBuffer>>();
|
||||
|
||||
protected CopyOnWriteArrayList<FrequencyChangeListener>
|
||||
mFrequencyChangeListeners = new CopyOnWriteArrayList<FrequencyChangeListener>();
|
||||
@@ -53,7 +58,11 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
|
||||
return mName;
|
||||
}
|
||||
|
||||
public abstract void dispose();
|
||||
public void dispose()
|
||||
{
|
||||
mSampleListeners.clear();
|
||||
mFrequencyChangeListeners.clear();
|
||||
}
|
||||
|
||||
public void setName( String name )
|
||||
{
|
||||
@@ -141,7 +150,7 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
|
||||
/**
|
||||
* Registers the listener to receive complex float sample arrays
|
||||
*/
|
||||
public void addListener( Listener<Float[]> listener )
|
||||
public void addListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mSampleListeners.add( listener );
|
||||
}
|
||||
@@ -149,7 +158,7 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
|
||||
/**
|
||||
* Removes the registered listener
|
||||
*/
|
||||
public void removeListener( Listener<Float[]> listener )
|
||||
public void removeListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mSampleListeners.remove( listener );
|
||||
}
|
||||
@@ -157,11 +166,11 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
|
||||
/**
|
||||
* Broadcasts the samples to all registered listeners
|
||||
*/
|
||||
public void broadcast( Float[] samples )
|
||||
public void broadcast( ComplexBuffer sampleBuffer )
|
||||
{
|
||||
for( Listener<Float[]> listener: mSampleListeners )
|
||||
for( Listener<ComplexBuffer> listener: mSampleListeners )
|
||||
{
|
||||
listener.receive( samples );
|
||||
listener.receive( sampleBuffer );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,15 +237,4 @@ public abstract class Tuner implements FrequencyChangeBroadcaster,
|
||||
{
|
||||
broadcastFrequencyChangeEvent( event );
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Broadcasts a frequency change event to all registered listeners
|
||||
// */
|
||||
// public void broadcastBandwidthChange( int bandwidth )
|
||||
// {
|
||||
// broadcastFrequencyChangeEvent(
|
||||
// new FrequencyChangeEvent( Attribute.BANDWIDTH, bandwidth ) );
|
||||
// }
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import sample.complex.ComplexSample;
|
||||
import sample.complex.ComplexSampleAssembler;
|
||||
import source.ComplexSource;
|
||||
@@ -39,13 +40,13 @@ import dsp.filter.Window.WindowType;
|
||||
|
||||
public class TunerChannelSource extends ComplexSource
|
||||
implements FrequencyChangeListener,
|
||||
Listener<Float[]>
|
||||
Listener<ComplexBuffer>
|
||||
{
|
||||
private static int sCHANNEL_RATE = 48000;
|
||||
private static int sCHANNEL_PASS_FREQUENCY = 12500;
|
||||
|
||||
private LinkedTransferQueue<Float[]> mBuffer =
|
||||
new LinkedTransferQueue<Float[]>();
|
||||
private LinkedTransferQueue<ComplexBuffer> mBuffer =
|
||||
new LinkedTransferQueue<ComplexBuffer>();
|
||||
private Tuner mTuner;
|
||||
private TunerChannel mTunerChannel;
|
||||
private Oscillator mSineWaveGenerator;
|
||||
@@ -91,7 +92,7 @@ public class TunerChannelSource extends ComplexSource
|
||||
new DecimationProcessor(), 50, TimeUnit.MILLISECONDS );
|
||||
|
||||
/* Finally, register to receive samples from the tuner */
|
||||
mTuner.addListener( (Listener<Float[]>)this );
|
||||
mTuner.addListener( (Listener<ComplexBuffer>)this );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -137,9 +138,9 @@ public class TunerChannelSource extends ComplexSource
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float[] samples )
|
||||
public void receive( ComplexBuffer sampleArray )
|
||||
{
|
||||
mBuffer.add( samples );
|
||||
mBuffer.add( sampleArray );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -242,17 +243,20 @@ public class TunerChannelSource extends ComplexSource
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
List<Float[]> samplesList = new ArrayList<Float[]>();
|
||||
List<ComplexBuffer> sampleBuffers =
|
||||
new ArrayList<ComplexBuffer>();
|
||||
|
||||
if( mBuffer != null )
|
||||
{
|
||||
/* Limit to 10, so that after a garbage collect we don't
|
||||
* induce wild swings as it tries to take tons of samples
|
||||
* and process them, causing delays and buffer refill */
|
||||
mBuffer.drainTo( samplesList, 10 );
|
||||
mBuffer.drainTo( sampleBuffers, 10 );
|
||||
|
||||
for( Float[] samples: samplesList )
|
||||
for( ComplexBuffer sampleArray: sampleBuffers )
|
||||
{
|
||||
float[] samples = sampleArray.getSamples();
|
||||
|
||||
for( int x = 0; x < samples.length; x += 2 )
|
||||
{
|
||||
Float left = samples[ x ];
|
||||
@@ -273,7 +277,7 @@ public class TunerChannelSource extends ComplexSource
|
||||
}
|
||||
}
|
||||
|
||||
samplesList.clear();
|
||||
sampleBuffers.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +135,10 @@ public abstract class TunerController implements Tunable
|
||||
long maxLockedFrequency = mTunedChannels
|
||||
.get( mTunedChannels.size() - 1 ).getMaxFrequency();
|
||||
|
||||
mLog.debug( "Tuner Controller min [" + minLockedFrequency +
|
||||
"] max [" + maxLockedFrequency +
|
||||
"] bw [" + getBandwidth() + "]" );
|
||||
|
||||
//Requested channel is higher than min locked frequency
|
||||
if( minLockedFrequency <= channel.getMinFrequency() &&
|
||||
( channel.getMaxFrequency() - minLockedFrequency ) <= getBandwidth() )
|
||||
|
||||
@@ -364,7 +364,14 @@ public class TunerManager
|
||||
|
||||
HackRFTuner tuner = new HackRFTuner( hackRFController );
|
||||
|
||||
return new TunerInitStatus( tuner, "LOADED" );
|
||||
TunerConfiguration config = getTunerConfiguration( tuner );
|
||||
|
||||
if( config != null )
|
||||
{
|
||||
tuner.apply( config );
|
||||
}
|
||||
|
||||
return new TunerInitStatus( tuner, "LOADED" );
|
||||
}
|
||||
catch( SourceException se )
|
||||
{
|
||||
@@ -475,98 +482,6 @@ public class TunerManager
|
||||
return new TunerInitStatus( null, reason );
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * Get all USB tuner devices
|
||||
// */
|
||||
// private ArrayList<USBTunerDevice> getUSBTunerDevices()
|
||||
// {
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
//
|
||||
// List<UsbDevice> devices = getUSBDevices();
|
||||
//
|
||||
// sb.append( "discovered [" + devices.size() +
|
||||
// "] attached USB devices\n" );
|
||||
//
|
||||
// ArrayList<USBTunerDevice> tuners = new ArrayList<USBTunerDevice>();
|
||||
//
|
||||
// for (UsbDevice device : devices )
|
||||
// {
|
||||
// String status = "\t[NOT RECOGNIZED]\t";
|
||||
//
|
||||
// TunerClass type = TunerClass.valueOf( device.getUsbDeviceDescriptor() );
|
||||
//
|
||||
// if( type != TunerClass.UNKNOWN )
|
||||
// {
|
||||
// status = "\t[RECOGNIZED] \t";
|
||||
// tuners.add( new USBTunerDevice( device, type) );
|
||||
// }
|
||||
//
|
||||
// sb.append( status );
|
||||
// sb.append( device.toString() );
|
||||
//
|
||||
// if( type != TunerClass.UNKNOWN )
|
||||
// {
|
||||
// sb.append( " " );
|
||||
// sb.append( type.getVendorDescription() );
|
||||
// sb.append( " " );
|
||||
// sb.append( type.getDeviceDescription() );
|
||||
// }
|
||||
//
|
||||
// sb.append( "\n" );
|
||||
// }
|
||||
//
|
||||
// mLog.info( "USB Device Discovery" );
|
||||
// mLog.info( sb.toString() );
|
||||
//
|
||||
// return tuners;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Gets all USB devices from the root hub downward, recursively, or
|
||||
// * returns empty list if none are discovered
|
||||
// */
|
||||
// public static List<UsbDevice> getUSBDevices()
|
||||
// {
|
||||
// ArrayList<UsbDevice> devices = new ArrayList<UsbDevice>();
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// UsbServices services = UsbHostManager.getUsbServices();
|
||||
// devices = getUsbChildDevices( services.getRootUsbHub() );
|
||||
// }
|
||||
// catch( SecurityException e )
|
||||
// {
|
||||
// mLog.error( "security exception while getting USB devices", e );
|
||||
// }
|
||||
// catch( UsbException e )
|
||||
// {
|
||||
// mLog.error( "usb exception while getting USB devices", e );
|
||||
// }
|
||||
//
|
||||
// return devices;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Get (recursively) all usb child devices below the usb hub
|
||||
// */
|
||||
// public static ArrayList<UsbDevice> getUsbChildDevices( UsbHub hub )
|
||||
// {
|
||||
// ArrayList<UsbDevice> devices = new ArrayList<UsbDevice>();
|
||||
//
|
||||
// for ( UsbDevice device : (List<UsbDevice>) hub.getAttachedUsbDevices() )
|
||||
// {
|
||||
// devices.add( device );
|
||||
//
|
||||
// if ( device.isUsbHub() )
|
||||
// {
|
||||
// devices.addAll( getUsbChildDevices( (UsbHub)device ) );
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return devices;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Get a stored tuner configuration or create a default tuner configuration
|
||||
* for the tuner type, connected at the address.
|
||||
|
||||
@@ -22,8 +22,9 @@ import java.util.concurrent.RejectedExecutionException;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.adapter.ShortAdapter;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import source.SourceException;
|
||||
import source.mixer.ShortAdapter;
|
||||
import source.tuner.FrequencyChangeListener;
|
||||
import source.tuner.MixerTuner;
|
||||
import source.tuner.MixerTunerDataLine;
|
||||
@@ -122,7 +123,7 @@ public class FCDTuner extends MixerTuner
|
||||
if( source != null )
|
||||
{
|
||||
/* Unregister for receiving samples */
|
||||
removeListener( (Listener<Float[]>)source );
|
||||
removeListener( (Listener<ComplexBuffer>)source );
|
||||
|
||||
/* Tell the controller to release the channel and cleanup */
|
||||
/* This will release the channel as a frequency change listener */
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import source.SourceException;
|
||||
import source.tuner.Tuner;
|
||||
import source.tuner.TunerChannel;
|
||||
@@ -102,7 +103,7 @@ public class HackRFTuner extends Tuner
|
||||
@Override
|
||||
public void apply( TunerConfiguration config ) throws SourceException
|
||||
{
|
||||
mLog.error( "B100 Tuner - can't apply tuner configuration" );
|
||||
mController.apply( config );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,7 +129,7 @@ public class HackRFTuner extends Tuner
|
||||
public void releaseChannel( TunerChannelSource source )
|
||||
{
|
||||
/* Unregister for receiving samples */
|
||||
removeListener( (Listener<Float[]>)source );
|
||||
removeListener( (Listener<ComplexBuffer>)source );
|
||||
|
||||
/* Tell the controller to release the channel and cleanup */
|
||||
if( source != null )
|
||||
@@ -153,13 +154,13 @@ public class HackRFTuner extends Tuner
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener( Listener<Float[]> listener )
|
||||
public void addListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mController.addListener( listener );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener( Listener<Float[]> listener )
|
||||
public void removeListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mController.removeListener( listener );
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ package source.tuner.hackrf;
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -80,6 +79,7 @@ import org.usb4java.Transfer;
|
||||
import org.usb4java.TransferCallback;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import source.SourceException;
|
||||
import source.tuner.TunerConfiguration;
|
||||
import source.tuner.TunerController;
|
||||
@@ -114,8 +114,8 @@ public class HackRFTunerController extends TunerController
|
||||
private LinkedTransferQueue<byte[]> mFilledBuffers =
|
||||
new LinkedTransferQueue<byte[]>();
|
||||
|
||||
private CopyOnWriteArrayList<Listener<Float[]>> mSampleListeners =
|
||||
new CopyOnWriteArrayList<Listener<Float[]>>();
|
||||
private CopyOnWriteArrayList<Listener<ComplexBuffer>> mSampleListeners =
|
||||
new CopyOnWriteArrayList<Listener<ComplexBuffer>>();
|
||||
|
||||
private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter();
|
||||
private BufferProcessor mBufferProcessor = new BufferProcessor();
|
||||
@@ -491,6 +491,8 @@ public class HackRFTunerController extends TunerController
|
||||
*/
|
||||
public void setSampleRate( HackRFSampleRate rate ) throws UsbException
|
||||
{
|
||||
mLog.debug( "Setting sample rate to:" + rate.name() );
|
||||
|
||||
setSampleRateManual( rate.getRate(), 1 );
|
||||
|
||||
mFrequencyController.setSampleRate( rate.getRate() );
|
||||
@@ -561,11 +563,11 @@ public class HackRFTunerController extends TunerController
|
||||
RATE2_016MHZ( 2016000, "2.016 MHz", BasebandFilter.F3_50 ),
|
||||
RATE3_024MHZ( 3024000, "3.024 MHz", BasebandFilter.F5_00 ),
|
||||
RATE4_464MHZ( 4464000, "4.464 MHz", BasebandFilter.F6_00 ),
|
||||
RATE5_472MHZ( 5472000, "5.472 MHz", BasebandFilter.F7_00 ),
|
||||
RATE5_376MHZ( 5376000, "5.376 MHz", BasebandFilter.F7_00 ),
|
||||
RATE7_488MHZ( 7488000, "7.488 MHz", BasebandFilter.F9_00 ),
|
||||
RATE10_032MHZ( 9984000, " 9.984 MHz", BasebandFilter.F12_00 ),
|
||||
RATE10_080MHZ( 10080000, "10.080 MHz", BasebandFilter.F12_00 ),
|
||||
RATE12_000MHZ( 12000000, "12.000 MHz", BasebandFilter.F14_00 ),
|
||||
RATE13_488MHZ( 13488000, "13.488 MHz", BasebandFilter.F15_00 ),
|
||||
RATE13_440MHZ( 13440000, "13.440 MHz", BasebandFilter.F15_00 ),
|
||||
RATE14_976MHZ( 14976000, "14.976 MHz", BasebandFilter.F20_00 ),
|
||||
RATE19_968MHZ( 19968000, "19.968 MHz", BasebandFilter.F24_00 );
|
||||
|
||||
@@ -868,7 +870,7 @@ public class HackRFTunerController extends TunerController
|
||||
* Adds a sample listener. If the buffer processing thread is
|
||||
* not currently running, starts it running in a new thread.
|
||||
*/
|
||||
public void addListener( Listener<Float[]> listener )
|
||||
public void addListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mSampleListeners.add( listener );
|
||||
|
||||
@@ -878,7 +880,7 @@ public class HackRFTunerController extends TunerController
|
||||
|
||||
Thread thread = new Thread( mBufferProcessor );
|
||||
thread.setDaemon( true );
|
||||
thread.setName( "Sample Processor" );
|
||||
thread.setName( "HackRF Sample Processor" );
|
||||
|
||||
thread.start();
|
||||
}
|
||||
@@ -888,7 +890,7 @@ public class HackRFTunerController extends TunerController
|
||||
* Removes the sample listener. If this is the last registered listener,
|
||||
* shuts down the buffer processing thread.
|
||||
*/
|
||||
public void removeListener( Listener<Float[]> listener )
|
||||
public void removeListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mSampleListeners.remove( listener );
|
||||
|
||||
@@ -906,19 +908,19 @@ public class HackRFTunerController extends TunerController
|
||||
* This is to facilitate garbage collection of the array when the listener
|
||||
* is done processing the samples.
|
||||
*/
|
||||
public void broadcast( Float[] samples )
|
||||
public void broadcast( ComplexBuffer samples )
|
||||
{
|
||||
Iterator<Listener<Float[]>> it = mSampleListeners.iterator();
|
||||
Iterator<Listener<ComplexBuffer>> it = mSampleListeners.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
Listener<Float[]> next = it.next();
|
||||
Listener<ComplexBuffer> next = it.next();
|
||||
|
||||
/* if this is the last (or only) listener, send the original
|
||||
* buffer, otherwise send a copy of the buffer */
|
||||
if( it.hasNext() )
|
||||
{
|
||||
next.receive( Arrays.copyOf( samples, samples.length ) );
|
||||
next.receive( samples.copyOf() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -936,7 +938,7 @@ public class HackRFTunerController extends TunerController
|
||||
private ScheduledExecutorService mExecutor =
|
||||
Executors.newScheduledThreadPool( 1 );
|
||||
private ScheduledFuture<?> mSampleDispatcherTask;
|
||||
private ArrayList<Transfer> mTransfers;
|
||||
private CopyOnWriteArrayList<Transfer> mTransfers;
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
|
||||
@Override
|
||||
@@ -1078,7 +1080,7 @@ public class HackRFTunerController extends TunerController
|
||||
*/
|
||||
private void prepareTransfers() throws LibUsbException
|
||||
{
|
||||
mTransfers = new ArrayList<Transfer>();
|
||||
mTransfers = new CopyOnWriteArrayList<Transfer>();
|
||||
|
||||
for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ )
|
||||
{
|
||||
@@ -1121,10 +1123,9 @@ public class HackRFTunerController extends TunerController
|
||||
|
||||
for( byte[] buffer: buffers )
|
||||
{
|
||||
//TODO: convert this to float primitive
|
||||
Float[] samples = mSampleAdapter.convert( buffer );
|
||||
float[] samples = mSampleAdapter.convert( buffer );
|
||||
|
||||
broadcast( samples );
|
||||
broadcast( new ComplexBuffer( samples ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
******************************************************************************/
|
||||
package source.tuner.rtl;
|
||||
|
||||
import source.mixer.SampleAdapter;
|
||||
import sample.adapter.SampleAdapter;
|
||||
import sample.complex.ComplexBuffer;
|
||||
|
||||
public class ByteSampleAdapter extends SampleAdapter
|
||||
{
|
||||
@@ -40,9 +41,9 @@ public class ByteSampleAdapter extends SampleAdapter
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float[] convert( byte[] samples )
|
||||
public float[] convert( byte[] samples )
|
||||
{
|
||||
Float[] convertedSamples = new Float[ samples.length ];
|
||||
float[] convertedSamples = new float[ samples.length ];
|
||||
int pointer = 0;
|
||||
|
||||
for( byte sample: samples )
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.usb4java.LibUsbException;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import source.SourceException;
|
||||
import source.tuner.FrequencyChangeListener;
|
||||
import source.tuner.Tuner;
|
||||
@@ -165,7 +166,7 @@ public class RTL2832Tuner extends Tuner
|
||||
public void releaseChannel( TunerChannelSource source )
|
||||
{
|
||||
/* Unregister for receiving samples */
|
||||
removeListener( (Listener<Float[]>)source );
|
||||
removeListener( (Listener<ComplexBuffer>)source );
|
||||
|
||||
/* Tell the controller to release the channel and cleanup */
|
||||
if( source != null )
|
||||
@@ -175,13 +176,13 @@ public class RTL2832Tuner extends Tuner
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener( Listener<Float[]> listener )
|
||||
public void addListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mController.addListener( listener );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener( Listener<Float[]> listener )
|
||||
public void removeListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mController.removeListener( listener );
|
||||
}
|
||||
|
||||
@@ -52,12 +52,12 @@ import org.usb4java.Transfer;
|
||||
import org.usb4java.TransferCallback;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import source.SourceException;
|
||||
import source.tuner.FrequencyChangeEvent;
|
||||
import source.tuner.FrequencyChangeEvent.Attribute;
|
||||
import source.tuner.TunerController;
|
||||
import source.tuner.TunerType;
|
||||
import util.TimeStamp;
|
||||
import buffer.FloatAveragingBuffer;
|
||||
import controller.ResourceManager;
|
||||
|
||||
@@ -107,8 +107,8 @@ public abstract class RTL2832TunerController extends TunerController
|
||||
|
||||
private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter();
|
||||
|
||||
private CopyOnWriteArrayList<Listener<Float[]>> mSampleListeners =
|
||||
new CopyOnWriteArrayList<Listener<Float[]>>();
|
||||
private CopyOnWriteArrayList<Listener<ComplexBuffer>> mSampleListeners =
|
||||
new CopyOnWriteArrayList<Listener<ComplexBuffer>>();
|
||||
|
||||
private LinkedTransferQueue<byte[]> mFilledBuffers =
|
||||
new LinkedTransferQueue<byte[]>();
|
||||
@@ -1342,7 +1342,7 @@ public abstract class RTL2832TunerController extends TunerController
|
||||
* Adds a sample listener. If the buffer processing thread is
|
||||
* not currently running, starts it running in a new thread.
|
||||
*/
|
||||
public void addListener( Listener<Float[]> listener )
|
||||
public void addListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mSampleListeners.add( listener );
|
||||
|
||||
@@ -1352,7 +1352,7 @@ public abstract class RTL2832TunerController extends TunerController
|
||||
|
||||
Thread thread = new Thread( mBufferProcessor );
|
||||
thread.setDaemon( true );
|
||||
thread.setName( "Sample Processor" );
|
||||
thread.setName( "RTL2832 Sample Processor" );
|
||||
|
||||
thread.start();
|
||||
}
|
||||
@@ -1362,7 +1362,7 @@ public abstract class RTL2832TunerController extends TunerController
|
||||
* Removes the sample listener. If this is the last registered listener,
|
||||
* shuts down the buffer processing thread.
|
||||
*/
|
||||
public void removeListener( Listener<Float[]> listener )
|
||||
public void removeListener( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mSampleListeners.remove( listener );
|
||||
|
||||
@@ -1375,23 +1375,23 @@ public abstract class RTL2832TunerController extends TunerController
|
||||
/**
|
||||
* Dispatches float sample buffers to all registered listeners
|
||||
*/
|
||||
public void broadcast( Float[] samples )
|
||||
public void broadcast( ComplexBuffer buffer )
|
||||
{
|
||||
Iterator<Listener<Float[]>> it = mSampleListeners.iterator();
|
||||
Iterator<Listener<ComplexBuffer>> it = mSampleListeners.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
Listener<Float[]> next = it.next();
|
||||
Listener<ComplexBuffer> next = it.next();
|
||||
|
||||
/* if this is the last (or only) listener, send him the original
|
||||
* buffer, otherwise send him a copy of the buffer */
|
||||
if( it.hasNext() )
|
||||
{
|
||||
next.receive( Arrays.copyOf( samples, samples.length ) );
|
||||
next.receive( buffer.copyOf() );
|
||||
}
|
||||
else
|
||||
{
|
||||
next.receive( samples );
|
||||
next.receive( buffer );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1406,7 +1406,7 @@ public abstract class RTL2832TunerController extends TunerController
|
||||
Executors.newScheduledThreadPool( 2 );
|
||||
private ScheduledFuture<?> mSampleDispatcherTask;
|
||||
private ScheduledFuture<?> mSampleRateCounterTask;
|
||||
private ArrayList<Transfer> mTransfers;
|
||||
private CopyOnWriteArrayList<Transfer> mTransfers;
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
|
||||
@Override
|
||||
@@ -1573,7 +1573,7 @@ public abstract class RTL2832TunerController extends TunerController
|
||||
*/
|
||||
private void prepareTransfers() throws LibUsbException
|
||||
{
|
||||
mTransfers = new ArrayList<Transfer>();
|
||||
mTransfers = new CopyOnWriteArrayList<Transfer>();
|
||||
|
||||
for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ )
|
||||
{
|
||||
@@ -1616,10 +1616,9 @@ public abstract class RTL2832TunerController extends TunerController
|
||||
|
||||
for( byte[] buffer: buffers )
|
||||
{
|
||||
//TODO: convert this to float primitive
|
||||
Float[] samples = mSampleAdapter.convert( buffer );
|
||||
float[] samples = mSampleAdapter.convert( buffer );
|
||||
|
||||
broadcast( samples );
|
||||
broadcast( new ComplexBuffer( samples ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
|
||||
public class FloatWaveSource extends WaveSource
|
||||
{
|
||||
@@ -44,7 +44,7 @@ public class FloatWaveSource extends WaveSource
|
||||
private int mBufferPointer = 0;
|
||||
private int mBufferLength;
|
||||
private ShortBuffer mBufferWrapper;
|
||||
private Listener<Float> mListener;
|
||||
private SimplexSampleListener mListener;
|
||||
|
||||
private static int BUFFER_SAMPLE_SIZE = 2000;
|
||||
|
||||
@@ -272,12 +272,12 @@ public class FloatWaveSource extends WaveSource
|
||||
/**
|
||||
* Adds a new listener to receive samples as they are read from the wave file
|
||||
*/
|
||||
public void setListener( Listener<Float> listener )
|
||||
public void setListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void removeListener( Listener<Float> listener )
|
||||
public void removeListener( SimplexSampleListener listener )
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import javax.swing.SwingUtilities;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import sample.simplex.SimplexSampleListener;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.ColorSettingMenuItem;
|
||||
import settings.Setting;
|
||||
@@ -43,7 +45,7 @@ import dsp.filter.Window.WindowType;
|
||||
|
||||
public class ChannelSpectrumPanel extends JPanel
|
||||
implements ChannelEventListener,
|
||||
Listener<Float>,
|
||||
SimplexSampleListener,
|
||||
SettingChangeListener,
|
||||
SpectralDisplayAdjuster
|
||||
{
|
||||
@@ -62,7 +64,7 @@ public class ChannelSpectrumPanel extends JPanel
|
||||
|
||||
private int mSpectrumAveragingSize;
|
||||
private int mSampleBufferSize = 2400;
|
||||
private Float[] mSamples = new Float[ mSampleBufferSize ];
|
||||
private float[] mSamples = new float[ mSampleBufferSize ];
|
||||
private int mSamplePointer = 0;
|
||||
|
||||
private DecimatingSampleAssembler mDecimatingSampleAssembler;
|
||||
@@ -204,7 +206,7 @@ public class ChannelSpectrumPanel extends JPanel
|
||||
{
|
||||
if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.isProcessing() )
|
||||
{
|
||||
mCurrentChannel.getProcessingChain().addFloatListener( this );
|
||||
mCurrentChannel.getProcessingChain().addSimplexListener( (SimplexSampleListener)this );
|
||||
mDFTProcessor.start();
|
||||
}
|
||||
}
|
||||
@@ -214,7 +216,7 @@ public class ChannelSpectrumPanel extends JPanel
|
||||
|
||||
if( mCurrentChannel != null && mCurrentChannel.isProcessing() )
|
||||
{
|
||||
mCurrentChannel.getProcessingChain().removeFloatListener( this );
|
||||
mCurrentChannel.getProcessingChain().removeSimplexListener( (SimplexSampleListener)this );
|
||||
}
|
||||
|
||||
mDFTProcessor.stop();
|
||||
@@ -250,7 +252,7 @@ public class ChannelSpectrumPanel extends JPanel
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
mDecimatingSampleAssembler.receive( sample );
|
||||
}
|
||||
@@ -470,37 +472,36 @@ public class ChannelSpectrumPanel extends JPanel
|
||||
mSpectrumPanel.setBaseline( baseline );
|
||||
}
|
||||
|
||||
public class DecimatingSampleAssembler implements Listener<Float>
|
||||
public class DecimatingSampleAssembler
|
||||
{
|
||||
private FloatHalfBandFilter mDecimationFilter = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
|
||||
private SampleAssembler mSampleAssembler;
|
||||
|
||||
public DecimatingSampleAssembler( Listener<Float[]> listener )
|
||||
public DecimatingSampleAssembler( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mSampleAssembler = new SampleAssembler( listener );
|
||||
mDecimationFilter.setListener( mSampleAssembler );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
mDecimationFilter.receive( sample );
|
||||
}
|
||||
}
|
||||
|
||||
public class SampleAssembler implements Listener<Float>
|
||||
public class SampleAssembler implements SimplexSampleListener
|
||||
{
|
||||
private Listener<Float[]> mListener;
|
||||
private Listener<ComplexBuffer> mListener;
|
||||
|
||||
public SampleAssembler( Listener<Float[]> listener )
|
||||
public SampleAssembler( Listener<ComplexBuffer> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
public void receive( float sample )
|
||||
{
|
||||
mSamples[ mSamplePointer++ ] = sample;
|
||||
|
||||
@@ -508,15 +509,16 @@ public class ChannelSpectrumPanel extends JPanel
|
||||
{
|
||||
if( mEnabled.get() )
|
||||
{
|
||||
mListener.receive( Arrays.copyOf( mSamples,
|
||||
mSamples.length ) );
|
||||
float[] copy = Arrays.copyOf( mSamples, mSamples.length );
|
||||
|
||||
mListener.receive( new ComplexBuffer( copy ) );
|
||||
}
|
||||
|
||||
mSamplePointer = 0;
|
||||
|
||||
if( mSamples.length != mSampleBufferSize )
|
||||
{
|
||||
mSamples = new Float[ mSampleBufferSize ];
|
||||
mSamples = new float[ mSampleBufferSize ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import source.Source;
|
||||
import source.Source.SampleType;
|
||||
import source.tuner.FrequencyChangeEvent;
|
||||
@@ -43,7 +44,7 @@ import dsp.filter.Window.WindowType;
|
||||
* Processes both complex samples or float samples and dispatches a float array
|
||||
* of DFT results, using configurable fft size and output dispatch timelines.
|
||||
*/
|
||||
public class DFTProcessor implements Listener<Float[]>,
|
||||
public class DFTProcessor implements Listener<ComplexBuffer>,
|
||||
FrequencyChangeListener
|
||||
{
|
||||
private final static Logger mLog =
|
||||
@@ -52,8 +53,8 @@ public class DFTProcessor implements Listener<Float[]>,
|
||||
private CopyOnWriteArrayList<DFTResultsConverter> mListeners =
|
||||
new CopyOnWriteArrayList<DFTResultsConverter>();
|
||||
|
||||
private ArrayBlockingQueue<Float[]> mQueue =
|
||||
new ArrayBlockingQueue<Float[]>( 200 );
|
||||
private ArrayBlockingQueue<ComplexBuffer> mQueue =
|
||||
new ArrayBlockingQueue<ComplexBuffer>( 200 );
|
||||
|
||||
private ScheduledExecutorService mScheduler =
|
||||
Executors.newScheduledThreadPool(1);
|
||||
@@ -74,7 +75,7 @@ public class DFTProcessor implements Listener<Float[]>,
|
||||
private float mNewFloatResidual;
|
||||
private float[] mPreviousFrame = new float[ 8192 ];
|
||||
|
||||
private Float[] mCurrentBuffer;
|
||||
private float[] mCurrentBuffer;
|
||||
private int mCurrentBufferPointer = 0;
|
||||
|
||||
private SampleType mSampleType;
|
||||
@@ -222,16 +223,16 @@ public class DFTProcessor implements Listener<Float[]>,
|
||||
* Places the sample into a transfer queue for future processing.
|
||||
*/
|
||||
@Override
|
||||
public void receive( Float[] samples )
|
||||
public void receive( ComplexBuffer sampleBuffer )
|
||||
{
|
||||
if( !mQueue.offer( samples ) )
|
||||
if( !mQueue.offer( sampleBuffer ) )
|
||||
{
|
||||
mLog.error( "DFTProcessor - [" + mSampleType.toString()
|
||||
+ "]queue is full, purging queue, "
|
||||
+ "samples[" + samples + "]" );
|
||||
+ "samples[" + sampleBuffer + "]" );
|
||||
|
||||
mQueue.clear();
|
||||
mQueue.offer( samples );
|
||||
mQueue.offer( sampleBuffer );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +242,8 @@ public class DFTProcessor implements Listener<Float[]>,
|
||||
|
||||
try
|
||||
{
|
||||
mCurrentBuffer = mQueue.take();
|
||||
ComplexBuffer buffer = mQueue.take();
|
||||
mCurrentBuffer = buffer.getSamples();
|
||||
}
|
||||
catch ( InterruptedException e )
|
||||
{
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import settings.ColorSetting;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.ColorSettingMenuItem;
|
||||
@@ -89,7 +90,7 @@ import dsp.filter.Window.WindowType;
|
||||
*/
|
||||
public class SpectralDisplayPanel extends JPanel
|
||||
implements ChannelEventListener,
|
||||
Listener<Float[]>,
|
||||
Listener<ComplexBuffer>,
|
||||
FrequencyChangeListener,
|
||||
SettingChangeListener,
|
||||
TunerSelectionListener
|
||||
@@ -359,9 +360,9 @@ public class SpectralDisplayPanel extends JPanel
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float[] samples )
|
||||
public void receive( ComplexBuffer sampleBuffer )
|
||||
{
|
||||
mDFTProcessor.receive( samples );
|
||||
mDFTProcessor.receive( sampleBuffer );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,7 +383,7 @@ public class SpectralDisplayPanel extends JPanel
|
||||
mTuner.addListener( (FrequencyChangeListener)this );
|
||||
|
||||
//Register the dft processor to receive samples from the tuner
|
||||
mTuner.addListener( (Listener<Float[]>)mDFTProcessor );
|
||||
mTuner.addListener( (Listener<ComplexBuffer>)mDFTProcessor );
|
||||
|
||||
//Fire frequency and sample rate change events so that everyone
|
||||
//can init
|
||||
@@ -413,7 +414,7 @@ public class SpectralDisplayPanel extends JPanel
|
||||
mTuner.removeListener( (FrequencyChangeListener)this );
|
||||
|
||||
//Deregister the dft processor from receiving samples
|
||||
mTuner.removeListener( (Listener<Float[]>)mDFTProcessor );
|
||||
mTuner.removeListener( (Listener<ComplexBuffer>)mDFTProcessor );
|
||||
mTuner = null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user