wip-channelizer

This commit is contained in:
Denny
2017-04-21 12:17:55 -04:00
parent ab31b40124
commit 7c6b7494b8
50 changed files with 2360 additions and 1670 deletions

View File

@@ -36,18 +36,18 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.real.IOverflowListener;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.IFrequencyChangeListener;
import source.SourceEvent;
import source.ISourceEventListener;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import static source.tuner.frequency.FrequencyChangeEvent.Event.NOTIFICATION_FREQUENCY_CHANGE;
import static source.SourceEvent.Event.NOTIFICATION_FREQUENCY_CHANGE;
public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener,
IDecoderStateEventProvider, IFrequencyChangeListener, ISquelchStateProvider, IAttributeChangeRequestListener,
IDecoderStateEventProvider, ISourceEventListener, ISquelchStateProvider, IAttributeChangeRequestListener,
IOverflowListener
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class);
@@ -66,7 +66,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
private ChannelType mChannelType;
private TrafficChannelManager mTrafficChannelEndListener;
private CallEvent mTrafficChannelCallEvent;
private FrequencyChangeListener mFrequencyChangeListener;
private SourceChangeListener mSourceChangeListener;
private boolean mSquelchLocked = false;
private boolean mSelected = false;
@@ -196,14 +196,14 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
}
@Override
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
public Listener<SourceEvent> getSourceEventListener()
{
if(mFrequencyChangeListener == null)
if(mSourceChangeListener == null)
{
mFrequencyChangeListener = new FrequencyChangeListener();
mSourceChangeListener = new SourceChangeListener();
}
return mFrequencyChangeListener;
return mSourceChangeListener;
}
private boolean isStandardChannel()
@@ -552,14 +552,14 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
/**
* Listener to receive frequency change events and rebroadcast them to the decoder states
*/
public class FrequencyChangeListener implements Listener<FrequencyChangeEvent>
public class SourceChangeListener implements Listener<SourceEvent>
{
@Override
public void receive(FrequencyChangeEvent frequencyChangeEvent)
public void receive(SourceEvent sourceEvent)
{
if(frequencyChangeEvent.getEvent() == NOTIFICATION_FREQUENCY_CHANGE)
if(sourceEvent.getEvent() == NOTIFICATION_FREQUENCY_CHANGE)
{
long frequency = frequencyChangeEvent.getValue().longValue();
long frequency = sourceEvent.getValue().longValue();
broadcast(new DecoderStateEvent(this, Event.SOURCE_FREQUENCY, getState(), frequency));
}

View File

@@ -18,8 +18,6 @@
******************************************************************************/
package channel.traffic;
import alias.Alias;
import alias.id.priority.Priority;
import channel.state.DecoderStateEvent;
import channel.state.IDecoderStateEventListener;
import controller.channel.Channel;
@@ -27,7 +25,6 @@ import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent;
import controller.channel.ChannelEvent.Event;
import controller.channel.ChannelModel;
import controller.channel.ChannelProcessingManager;
import controller.channel.TrafficChannelEvent;
import module.Module;
import module.decode.config.DecodeConfiguration;
@@ -40,7 +37,6 @@ import record.config.RecordConfiguration;
import sample.Listener;
import source.config.SourceConfigTuner;
import source.tuner.TunerChannel;
import source.tuner.TunerChannel.Type;
import java.util.ArrayList;
import java.util.List;
@@ -193,8 +189,8 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
if(frequency > 0)
{
Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(Type.TRAFFIC,
frequency, mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(frequency,
mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
if(channel != null)
{

View File

@@ -34,9 +34,8 @@ import source.config.SourceConfigRecording;
import source.config.SourceConfigTuner;
import source.config.SourceConfiguration;
import source.tuner.TunerChannel;
import source.tuner.TunerChannel.Type;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Event;
import source.SourceEvent;
import source.SourceEvent.Event;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
@@ -46,7 +45,7 @@ import javax.xml.bind.annotation.XmlTransient;
@XmlSeeAlso({Configuration.class})
@XmlRootElement(name = "channel")
public class Channel extends Configuration implements Listener<FrequencyChangeEvent>
public class Channel extends Configuration implements Listener<SourceEvent>
{
// Standard channels are persisted and traffic channels are temporary
public enum ChannelType
@@ -418,15 +417,14 @@ public class Channel extends Configuration implements Listener<FrequencyChangeEv
{
SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration;
mTunerChannel = new TunerChannel(Type.LOCKED, config.getFrequency(),
mTunerChannel = new TunerChannel(config.getFrequency(),
mDecodeConfiguration.getDecoderType().getChannelBandwidth());
}
else if(mSourceConfiguration.getSourceType() == SourceType.RECORDING)
{
SourceConfigRecording config =
(SourceConfigRecording)mSourceConfiguration;
SourceConfigRecording config = (SourceConfigRecording)mSourceConfiguration;
mTunerChannel = new TunerChannel(Type.LOCKED, config.getFrequency(),
mTunerChannel = new TunerChannel(config.getFrequency(),
mDecodeConfiguration.getDecoderType().getChannelBandwidth());
}
}
@@ -452,7 +450,7 @@ public class Channel extends Configuration implements Listener<FrequencyChangeEv
* be used to visually show in the spectral display.
*/
@Override
public void receive(FrequencyChangeEvent event)
public void receive(SourceEvent event)
{
if(event.getEvent() == Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE)
{

View File

@@ -567,7 +567,7 @@ public class FilterFactory
* For 40db attenuation, calculate the number of symbols based on the following formula:
* Symbols = -44 * alpha + 33
*
* Polyphase Channelizer notes:
* Polyphase TunerChannelizer notes:
* -Set samples Per Symbol at 2 (or more) * number of channels
* -Set symbolCount to sufficient size to produce required attenuation
* -Set symbol rate in hertz
@@ -725,7 +725,7 @@ public class FilterFactory
}
/**
* Creates a windowed-sync (Nyquist) M/2 prototype FIR filter for use with a Polyphase Channelizer/Synthesizer.
* Creates a windowed-sync (Nyquist) M/2 prototype FIR filter for use with a Polyphase TunerChannelizer/Synthesizer.
* The filter is designed for x2 oversampling of each channel and the filter cutoff frequency is incrementally
* adjusted to achieve a -6.02 dB attenuation at the channel band edge to enable perfect reconstruction of adjacent
* channels.
@@ -833,7 +833,7 @@ public class FilterFactory
if(logResults)
{
mLog.debug("Polyphase Channelizer Filter Design Summary");
mLog.debug("Polyphase TunerChannelizer Filter Design Summary");
mLog.debug("-------------------------------------------");
mLog.debug("Channels: " + channels);
mLog.debug("Taps Per Channel: " + tapsPerChannel);

View File

@@ -0,0 +1,128 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.filter.channelizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.ISourceEventListener;
import source.SourceEvent;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class AbstractComplexPolyphaseChannelizer implements Listener<ComplexBuffer>, ISourceEventListener
{
private final static Logger mLog = LoggerFactory.getLogger(AbstractComplexPolyphaseChannelizer.class);
private Broadcaster<SourceEvent> mSourceChangeBroadcaster = new Broadcaster();
private List<PolyphaseChannelSource> mChannels = new CopyOnWriteArrayList<>();
private int mChannelCount;
private int mChannelSampleRate;
/**
* Complex sample polyphase channelizer
*
* @param channelCount
*/
public AbstractComplexPolyphaseChannelizer(int channelCount, int channelSampleRate)
{
mChannelCount = channelCount;
mChannelSampleRate = channelSampleRate;
}
/**
* Number of channels being processed by this channelizer.
*/
public int getChannelCount()
{
return mChannelCount;
}
/**
* Output sample rate for each channel
* @return sample rate in hertz
*/
public int getChannelSampleRate()
{
return mChannelSampleRate;
}
/**
* Filters the complex sample pair
*
* @param inphase sample
* @param quadrature sample
*/
protected abstract void filter(float inphase, float quadrature);
@Override
public void receive(ComplexBuffer complexBuffer)
{
float[] samples = complexBuffer.getSamples();
for(int x = 0; x < samples.length; x += 2)
{
filter(samples[x], samples[x + 1]);
}
}
/**
* Dispatches the processed channel samples to any registered polyphase channel outputs.
*
* @param channels containing an array of I/Q samples per channel
*/
protected void dispatch(float[] channels)
{
for(PolyphaseChannelSource channel : mChannels)
{
channel.processChannels(channels);
}
}
/**
* Adds the polyphase channel source to receive processed output channel samples
*
* @param polyphaseChannelSource
*/
public void addChannel(PolyphaseChannelSource polyphaseChannelSource)
{
mChannels.add(polyphaseChannelSource);
mSourceChangeBroadcaster.addListener(polyphaseChannelSource.getSourceEventListener());
}
/**
* Removes the polyphase channel source from receiving output channel samples.
*
* @param polyphaseChannelSource
*/
public void removeChannel(PolyphaseChannelSource polyphaseChannelSource)
{
mChannels.remove(polyphaseChannelSource);
mSourceChangeBroadcaster.removeListener(polyphaseChannelSource.getSourceEventListener());
}
@Override
public Listener<SourceEvent> getSourceEventListener()
{
return mSourceChangeBroadcaster;
}
}

View File

@@ -0,0 +1,135 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.filter.channelizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import source.ISourceEventProcessor;
import source.Source;
import source.SourceEvent;
import source.tuner.Tuner;
import source.tuner.TunerChannel;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Channelizer implements ISourceEventProcessor, Listener<SourceEvent>
{
private final static Logger mLog = LoggerFactory.getLogger(Channelizer.class);
private Tuner mTuner;
private List<PolyphaseChannelSource> mChannelSources = new CopyOnWriteArrayList<>();
/**
* Provides access to Digital Drop Channel (DDC) sources from the tuner. Incorporates a polyphase channelizer
* with downstream channel and upstream tuner management.
*
* @param tuner providing broadband sample buffers
*/
public Channelizer(Tuner tuner)
{
mTuner = tuner;
mTuner.addSourceEventListener(this);
}
public Source getChannel(TunerChannel tunerChannel)
{
//TODO: return a polyphase channel here
return null;
}
/**
* Starts/adds the channel source to receive channelized sample buffers, registering with the tuner to receive
* sample buffers when this is the first channel.
*
* @param channelSource to start
*/
private void startChannelSource(PolyphaseChannelSource channelSource)
{
if(mChannelSources.isEmpty())
{
//TODO: start the tuner
}
mChannelSources.add(channelSource);
}
/**
* Stops/removes the channel source from receiving channelized sample buffers and deregisters from the tuner
* if this is the last channel being sourced.
*
* @param channelSource to stop
*/
private void stopChannelSource(PolyphaseChannelSource channelSource)
{
mChannelSources.remove(channelSource);
if(mChannelSources.isEmpty())
{
//TODO: stop the tuner
}
if(mTuner != null)
{
//TODO: change the tuner to accept a remove(TunerChannel) method
// mTuner.remove(channel.getTunerChannel());
}
}
@Override
public void process(SourceEvent sourceEvent)
{
switch(sourceEvent.getEvent())
{
case REQUEST_START_SAMPLE_STREAM:
if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource)
{
startChannelSource((PolyphaseChannelSource)sourceEvent.getSource());
}
else
{
mLog.error("Request to stop sample stream for unrecognized source: " +
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
}
//TODO: add the channel and register for samples from the tuner
break;
case REQUEST_STOP_SAMPLE_STREAM:
if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource)
{
stopChannelSource((PolyphaseChannelSource)sourceEvent.getSource());
}
else
{
mLog.error("Request to stop sample stream for unrecognized source: " +
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
}
break;
}
}
@Override
public void receive(SourceEvent sourceEvent)
{
}
}

View File

@@ -20,7 +20,6 @@ package dsp.filter.channelizer;
import dsp.filter.FilterFactory;
import dsp.filter.Window;
import dsp.filter.correction.IQCorrectionFilter;
import dsp.filter.design.FilterDesignException;
import dsp.mixer.LowPhaseNoiseOscillator;
import net.miginfocom.swing.MigLayout;
@@ -31,7 +30,7 @@ import sample.SampleType;
import sample.complex.ComplexBuffer;
import settings.SettingsManager;
import source.tuner.configuration.TunerConfigurationModel;
import source.tuner.frequency.FrequencyChangeEvent;
import source.SourceEvent;
import spectrum.DFTProcessor;
import spectrum.DFTSize;
import spectrum.SpectrumPanel;
@@ -86,7 +85,7 @@ public class ChannelizerViewer extends JFrame
private void init()
{
setTitle("Channelizer Viewer");
setTitle("TunerChannelizer Viewer");
setSize(1000, 800);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
@@ -212,7 +211,7 @@ public class ChannelizerViewer extends JFrame
public class ChannelArrayPanel extends JPanel implements Listener<ComplexBuffer>
{
private ComplexPolyphaseChannelizerM2 mPolyphaseChannelizer;
private ComplexPolyphaseChannelizer mPolyphaseChannelizer;
private ChannelDistributor mChannelDistributor;
public ChannelArrayPanel(float[] taps)
@@ -223,7 +222,7 @@ public class ChannelizerViewer extends JFrame
bufferSize++;
}
mPolyphaseChannelizer = new ComplexPolyphaseChannelizerM2(taps, mChannelCount);
mPolyphaseChannelizer = new ComplexPolyphaseChannelizer(taps, mChannelCount, CHANNEL_BANDWIDTH);
mChannelDistributor = new ChannelDistributor(bufferSize, mChannelCount);
mPolyphaseChannelizer.setChannelDistributor(mChannelDistributor);
@@ -272,8 +271,7 @@ public class ChannelizerViewer extends JFrame
add(mSpectrumPanel);
mDFTProcessor.addConverter(mComplexDecibelConverter);
mDFTProcessor.frequencyChanged(new FrequencyChangeEvent(
FrequencyChangeEvent.Event.NOTIFICATION_SAMPLE_RATE_CHANGE, sampleRate));
mDFTProcessor.process(SourceEvent.sampleRateChange(sampleRate));
mComplexDecibelConverter.addListener(mSpectrumPanel);
}

View File

@@ -25,44 +25,40 @@ import dsp.mixer.Oscillator;
import org.jtransforms.fft.FloatFFT_1D;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import java.text.DecimalFormat;
public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
public class ComplexPolyphaseChannelizer extends AbstractComplexPolyphaseChannelizer
{
private final static Logger mLog = LoggerFactory.getLogger(ComplexPolyphaseChannelizer.class);
private RealFIRFilter[] mInphaseFilters;
private RealFIRFilter[] mQuadratureFilters;
private int mFilterPointer = 0;
private int mChannelCount;
private float[] mFilteredSamples;
private FloatFFT_1D mFFT;
private ChannelDistributor mChannelDistributor;
/**
* Maximally Decimated Polyphase Filter Bank (MDPFB) channelizer that divides the input frequency band into
* equal bandwidth channels.
*
* @param taps of a low-pass filter designed for the inbound sample rate with a cutoff frequency
* @param coefficients of a prototype low-pass filter designed for the inbound sample rate with a cutoff frequency
* equal to the channel bandwidth (sample rate / filters). If you need to synthesize (combine two or more
* filte outputs) a new bandwidth signal from the outputs of this filter, then the filter should be designed
* filter outputs) a new bandwidth signal from the outputs of this filter, then the filter should be designed
* as a nyquist filter with -6 dB attenuation at the channel bandwidth cutoff frequency
*
* @param channels - number of channels to output.
* @param channelCount - number of channels to output.
* @param channelSampleRate - channel sample rate (after decimation)
*/
public ComplexPolyphaseChannelizer(float[] taps, int channels)
public ComplexPolyphaseChannelizer(float[] coefficients, int channelCount, int channelSampleRate)
{
mChannelCount = channels;
super(channelCount, channelSampleRate);
initFilters(taps);
initFilters(coefficients);
}
public void filter(float inphase, float quadrature)
@Override
protected void filter(float inphase, float quadrature)
{
int index = (mChannelCount - mFilterPointer - 1) * 2;
int index = (getChannelCount() - mFilterPointer - 1) * 2;
mFilteredSamples[index] = mInphaseFilters[mFilterPointer].filter(inphase);
mFilteredSamples[index + 1] = mQuadratureFilters[mFilterPointer].filter(quadrature);
@@ -70,7 +66,7 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
if(mFilterPointer == 0)
{
calculate();
mFilterPointer = mChannelCount - 1;
mFilterPointer = getChannelCount() - 1;
}
else
{
@@ -78,20 +74,9 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
}
}
@Override
public void receive(ComplexBuffer complexBuffer)
{
float[] samples = complexBuffer.getSamples();
for(int x = 0; x < samples.length; x += 2)
{
filter(samples[x], samples[x + 1]);
}
}
private void calculate()
{
float[] samples = new float[mChannelCount * 2];
float[] samples = new float[getChannelCount() * 2];
System.arraycopy(mFilteredSamples, 0, samples, 0, mFilteredSamples.length);
//FFT is executed in-place where the output overwrites the input
@@ -100,24 +85,6 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
dispatch(samples);
}
private void dispatch(float[] channels)
{
if(mChannelDistributor != null)
{
mChannelDistributor.receive(channels);
}
}
public void setChannelDistributor(ChannelDistributor channelDistributor)
{
mChannelDistributor = channelDistributor;
}
private double magnitude(float real, float imaginary, int fftSize)
{
return Math.sqrt(Math.pow(real, 2.0) + Math.pow(imaginary, 2.0));
}
/**
* Distributes the filter taps to each of the polyphase filters
*
@@ -125,15 +92,15 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
*/
private void initFilters(float[] taps)
{
int tapCount = (int)Math.ceil((double)taps.length / (double)mChannelCount);
int tapCount = (int)Math.ceil((double)taps.length / (double)getChannelCount());
float[][] coefficients = new float[mChannelCount][tapCount];
float[][] coefficients = new float[getChannelCount()][tapCount];
for(int tap = 0; tap < tapCount; tap++)
{
for(int filter = 0; filter < mChannelCount; filter++)
for(int filter = 0; filter < getChannelCount(); filter++)
{
int index = tap * mChannelCount + filter;
int index = tap * getChannelCount() + filter;
if(index < taps.length)
{
@@ -142,18 +109,18 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
}
}
mInphaseFilters = new RealFIRFilter[mChannelCount];
mQuadratureFilters = new RealFIRFilter[mChannelCount];
mInphaseFilters = new RealFIRFilter[getChannelCount()];
mQuadratureFilters = new RealFIRFilter[getChannelCount()];
for(int filter = 0; filter < mChannelCount; filter++)
for(int filter = 0; filter < getChannelCount(); filter++)
{
mInphaseFilters[filter] = new RealFIRFilter(coefficients[filter], 1.0f);
mQuadratureFilters[filter] = new RealFIRFilter(coefficients[filter], 1.0f);
}
mFilteredSamples = new float[mChannelCount * 2];
mFFT = new FloatFFT_1D(mChannelCount);
mFilterPointer = mChannelCount -1;
mFilteredSamples = new float[getChannelCount() * 2];
mFFT = new FloatFFT_1D(getChannelCount());
mFilterPointer = getChannelCount() -1;
}
public static void main(String[] args)
@@ -171,14 +138,14 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
int channelCenter = (int)(12500 * 1.5);
StringBuilder sb = new StringBuilder();
sb.append("\nPolyphase Channelizer\n");
sb.append("\nPolyphase TunerChannelizer\n");
sb.append("Sample Rate:" + sampleRate + " Channels:" + channels + " Channel Rate:" + channelBandwidth + "\n");
sb.append("Tap Count:" + taps.length + "\n");
sb.append("Channel:" + channelCenter);
mLog.debug(sb.toString());
ComplexPolyphaseChannelizer channelizer = new ComplexPolyphaseChannelizer(taps, 8);
ComplexPolyphaseChannelizer channelizer = new ComplexPolyphaseChannelizer(taps, 8, channelBandwidth);
Oscillator oscillator = new Oscillator(channelCenter, 2 * sampleRate);

View File

@@ -25,21 +25,18 @@ import dsp.mixer.Oscillator;
import org.jtransforms.fft.FloatFFT_1D;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import java.text.DecimalFormat;
public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChannelizer
{
private final static Logger mLog = LoggerFactory.getLogger(ComplexPolyphaseChannelizerM2.class);
private ReverseFloatCircularBuffer[] mDataBuffers;
private float[][] mFilterCoefficients;
private int mFilterPointer = 0;
private int mChannelCount;
private FloatFFT_1D mFFT;
private ChannelDistributor mChannelDistributor;
private DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0000");
@@ -51,23 +48,23 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
* equal to the channel bandwidth (sample rate / filters). If you need to synthesize (combine two or more
* filte outputs) a new bandwidth signal from the outputs of this filter, then the filter should be designed
* as a nyquist filter with -6 dB attenuation at the channel bandwidth cutoff frequency
*
* @param channels - number of filters/channels to output. Since this filter bank oversamples each filter
* output, this number must be even (divisible by the oversample rate).
* @param channelSampleRate for each decimated, oversampled channel
*/
public ComplexPolyphaseChannelizerM2(float[] taps, int channels)
public ComplexPolyphaseChannelizerM2(float[] taps, int channels, int channelSampleRate)
{
super(channels, channelSampleRate);
if(channels % 2 != 0)
{
throw new IllegalArgumentException("Channel count must be a multiple of 2");
}
mChannelCount = channels;
initFilters(taps);
}
public void filter(float inphase, float quadrature)
protected void filter(float inphase, float quadrature)
{
try
{
@@ -77,9 +74,9 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
if(mFilterPointer == 0)
{
calculate();
mFilterPointer = mChannelCount * 2;
mFilterPointer = getChannelCount() * 2;
}
else if(mFilterPointer == mChannelCount)
else if(mFilterPointer == getChannelCount())
{
calculate();
}
@@ -92,20 +89,9 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
}
}
@Override
public void receive(ComplexBuffer complexBuffer)
{
float[] samples = complexBuffer.getSamples();
for(int x = 0; x < samples.length; x += 2)
{
filter(samples[x], samples[x + 1]);
}
}
private void calculate()
{
float[] samples = new float[mChannelCount * 2];
float[] samples = new float[getChannelCount() * 2];
if(mFilterPointer == 0)
{
@@ -158,6 +144,7 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
/**
* Filters the samples contained in the buffer against the filter coefficients
*
* @param coefficients of the sub filter
* @param buffer containing data samples
* @return
@@ -166,32 +153,14 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
{
float accumulator = 0.0f;
for( int x = 0; x < coefficients.length; x++ )
for(int x = 0; x < coefficients.length; x++)
{
accumulator += coefficients[ x ] * buffer.get(x);
accumulator += coefficients[x] * buffer.get(x);
}
return accumulator;
}
private void dispatch(float[] channels)
{
if(mChannelDistributor != null)
{
mChannelDistributor.receive(channels);
}
}
public void setChannelDistributor(ChannelDistributor channelDistributor)
{
mChannelDistributor = channelDistributor;
}
private double magnitude(float real, float imaginary, int fftSize)
{
return Math.sqrt(Math.pow(real, 2.0) + Math.pow(imaginary, 2.0));
}
/**
* Distributes the filter taps to each of the polyphase filters
*
@@ -199,22 +168,22 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
*/
private void initFilters(float[] taps)
{
int tapCount = (int)Math.ceil((double)taps.length / (double)mChannelCount);
int tapCount = (int)Math.ceil((double)taps.length / (double)getChannelCount());
mDataBuffers = new ReverseFloatCircularBuffer[mChannelCount * 2];
mDataBuffers = new ReverseFloatCircularBuffer[getChannelCount() * 2];
for(int x = 0; x < mChannelCount * 2; x++)
for(int x = 0; x < getChannelCount() * 2; x++)
{
mDataBuffers[x] = new ReverseFloatCircularBuffer(tapCount);
}
mFilterCoefficients = new float[mChannelCount][tapCount];
mFilterCoefficients = new float[getChannelCount()][tapCount];
for(int tap = 0; tap < tapCount; tap++)
{
for(int filter = 0; filter < mChannelCount; filter++)
for(int filter = 0; filter < getChannelCount(); filter++)
{
int index = tap * mChannelCount + filter;
int index = tap * getChannelCount() + filter;
if(index < taps.length)
{
@@ -223,8 +192,8 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
}
}
mFFT = new FloatFFT_1D(mChannelCount);
mFilterPointer = mChannelCount - 1; //Start at M/2
mFFT = new FloatFFT_1D(getChannelCount());
mFilterPointer = getChannelCount() - 1; //Start at M/2
}
public static void main(String[] args)
@@ -237,19 +206,19 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
int channelBandwidth = 12500;
int sampleRate = channels * channelBandwidth;
float[] taps = FilterFactory.getLowPass( 10000000, 12500, 5600, Window.WindowType.BLACKMAN_HARRIS_7 );
float[] taps = FilterFactory.getLowPass(10000000, 12500, 5600, Window.WindowType.BLACKMAN_HARRIS_7);
int channelCenter = (int)(12500 * 1.5);
StringBuilder sb = new StringBuilder();
sb.append("\nPolyphase Channelizer\n");
sb.append("\nPolyphase TunerChannelizer\n");
sb.append("Sample Rate:" + sampleRate + " Channels:" + channels + " Channel Rate:" + channelBandwidth + "\n");
sb.append("Tap Count:" + taps.length + "\n");
sb.append("Channel:" + channelCenter);
mLog.debug(sb.toString());
ComplexPolyphaseChannelizerM2 channelizer = new ComplexPolyphaseChannelizerM2(taps, 8);
ComplexPolyphaseChannelizerM2 channelizer = new ComplexPolyphaseChannelizerM2(taps, 8, channelBandwidth * 2);
Oscillator oscillator = new Oscillator(channelCenter, 2 * sampleRate);

View File

@@ -0,0 +1,201 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.filter.channelizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.ISourceEventProcessor;
import source.SourceEvent;
import source.SourceException;
import source.tuner.ComplexChannelSource;
import source.tuner.TunerChannel;
import java.util.concurrent.ScheduledExecutorService;
public class PolyphaseChannelSource extends ComplexChannelSource
{
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class);
private TunerChannel mTunerChannel;
private long mTunerCenterFrequency;
private int mUpstreamSampleRate;
private int mChannelSampleRate;
private int mChannelCount;
private int mOutputFrequencyCorrection;
/**
* Polyphase channelizer channel implementation. Adapts the channel array output samples from the polyphase
* channelizer into a single channel, or a channel synthesized from two adjacent channels that is frequency
* translated and decimated to a single channel.
*
* @param tunerChannel - requested output tuner channel frequency and bandwidth.
*/
public PolyphaseChannelSource(ISourceEventProcessor upstreamSourceEventProcessor, TunerChannel tunerChannel)
{
super(upstreamSourceEventProcessor);
mTunerChannel = tunerChannel;
}
/**
* Process source event notifications received from upstream source provider and source event requests
* received from downstream consumers.
*
* @param sourceEvent containing a request or notification
* @throws SourceException if there are any errors while processing the source event
*/
@Override
public void process(SourceEvent sourceEvent) throws SourceException
{
switch(sourceEvent.getEvent())
{
case NOTIFICATION_CHANNEL_SAMPLE_RATE_CHANGE:
setChannelSampleRate(sourceEvent.getValue().intValue());
break;
case NOTIFICATION_FREQUENCY_CHANGE:
mTunerCenterFrequency = sourceEvent.getValue().longValue();
updateInputConfiguration();
break;
case NOTIFICATION_SAMPLE_RATE_CHANGE:
setUpstreamSampleRate(sourceEvent.getValue().intValue());
updateInputConfiguration();
break;
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
setFrequencyCorrection(sourceEvent.getValue().intValue());
break;
}
}
/**
* Process the array samples for each of the output channels from the polyphase channelizer.
* @param channels
*/
public void processChannels(float[] channels)
{
//TODO we have to dispatch full buffers on a separate thread so that we don't bog down the channelizer
//TODO thread that is delivering the channel samples to us.
}
/**
* Updates current processing configuration any time there is a change in tuner center frequency
* or tuner sample rate.
*/
private void updateInputConfiguration()
{
}
/**
* Downstream sample rate
*
* @return sample rate in Hertz
* @throws SourceException if this method is accessed before the channel count is established
*/
@Override
public int getSampleRate() throws SourceException
{
if(mChannelSampleRate > 0)
{
return mChannelSampleRate;
}
throw new SourceException("Channel sample rate has not been set.");
}
/**
* Sets the downstream channel sample rate. Since oversampling may occur in the upstream channelizer, we can't
* rely on simply dividing the upstream sample rate by the number of channels. Thus, we required the upstream
* channelizer to tell us the exact channel sample rate.
*
* @param channelSampleRate in hertz
*/
private void setChannelSampleRate(int channelSampleRate)
{
mChannelSampleRate = channelSampleRate;
//Translate the upstream channel sample rate event into a downstream sample rate change event
broadcast(SourceEvent.sampleRateChange(mChannelSampleRate));
}
/**
* Sets the input sample rate and broadcasts a source change event to the downstream listener
* @param sampleRate from the upstream source
*/
private void setUpstreamSampleRate(int sampleRate)
{
mUpstreamSampleRate = sampleRate;
try
{
broadcast(SourceEvent.sampleRateChange(getSampleRate()));
}
catch(SourceException se)
{
mLog.error("Upstream sample rate changed - couldn't broadcast channel sample rate change event " +
"to downstream channel", se);
}
}
/**
* Sets the polyphase channel count.
* @param channelCount number of channels being processed by the polyphase channelizer
*/
private void setChannelCount(int channelCount)
{
mChannelCount = channelCount;
try
{
broadcast(SourceEvent.sampleRateChange(getSampleRate()));
}
catch(SourceException se)
{
mLog.error("Polyphase channel count changed - couldn't broadcast channel sample rate change event " +
"to downstream channel", se);
}
}
@Override
public long getFrequency() throws SourceException
{
return mTunerChannel.getFrequency();
}
private void setFrequencyCorrection(int correction)
{
//TODO:
mLog.debug("Request for frequency correction: " + correction + " -- not yet implemented");
}
@Override
public void reset()
{
}
@Override
public void dispose()
{
}
}

View File

@@ -151,7 +151,7 @@ public class FIRFilterSpecification
}
/**
* Creates a Polyphase Channelizer filter specification builder that allows you to define the filter parameters and
* Creates a Polyphase TunerChannelizer filter specification builder that allows you to define the filter parameters and
* create a filter specification
*/
public static ChannelizerBuilder channelizerBuilder()

View File

@@ -23,7 +23,7 @@ import dsp.filter.fir.FIRFilterSpecification;
public class PolyphaseChannelizerDesigner extends FIRFilterSpecification
{
/**
* Constructs a Polyphase Channelizer Remez filter specification.
* Constructs a Polyphase TunerChannelizer Remez filter specification.
*
* Constructor is package private -- use the PolyphaseChannelizerFilterFactory to create a filter
*

View File

@@ -43,7 +43,7 @@ import java.util.List;
import sample.Listener;
import sample.complex.Complex;
import sample.complex.ComplexSampleListener;
import source.tuner.frequency.IFrequencyChangeListener;
import source.ISourceEventListener;
import buffer.FloatAveragingBuffer;
import dsp.filter.interpolator.RealInterpolator;
import dsp.symbol.Dibit;
@@ -73,7 +73,7 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener
private CostasLoop mCostasLoop = new CostasLoop();
private IFrequencyChangeListener mFrequencyChangeListener;
private ISourceEventListener mFrequencyChangeListener;
private EyeDiagramDataTap mEyeDiagramDataTap;
@@ -147,12 +147,12 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener
}
}
public void addListener( IFrequencyChangeListener listener )
public void addListener( ISourceEventListener listener )
{
mFrequencyChangeListener = listener;
}
public void removeListener( IFrequencyChangeListener listener )
public void removeListener( ISourceEventListener listener )
{
mFrequencyChangeListener = null;
}
@@ -202,8 +202,8 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener
// if( correctionNeeded && mFrequencyChangeListener != null )
// {
// mLog.debug( "Issuing Frequency Correction: " + mFrequencyError );
// mFrequencyChangeListener.frequencyChanged(
// new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR,
// mFrequencyChangeListener.process(
// new SourceEvent( Attribute.FREQUENCY_ERROR,
// mFrequencyError ) );
// }
//

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,15 +14,21 @@
*
* 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 gui.control;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import source.ISourceEventProcessor;
import source.InvalidFrequencyException;
import source.SourceEvent;
import source.SourceEvent.Event;
import source.SourceException;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
@@ -35,26 +41,12 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import javax.swing.*;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import source.InvalidFrequencyException;
import source.SourceException;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Event;
import source.tuner.frequency.IFrequencyChangeProcessor;
public class JFrequencyControl extends JPanel implements IFrequencyChangeProcessor
public class JFrequencyControl extends JPanel implements ISourceEventProcessor
{
private static final long serialVersionUID = 1L;
private final static Logger mLog =
LoggerFactory.getLogger(JFrequencyControl.class);
private final static Logger mLog = LoggerFactory.getLogger(JFrequencyControl.class);
private List<IFrequencyChangeProcessor> mProcessors = new ArrayList<>();
private List<ISourceEventProcessor> mProcessors = new ArrayList<>();
private Color mHighlightColor = Color.YELLOW;
@@ -62,7 +54,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
private Cursor mBlankCursor;
private HashMap<Integer, Digit> mDigits = new HashMap<Integer, Digit>();
private HashMap<Integer,Digit> mDigits = new HashMap<Integer,Digit>();
public JFrequencyControl()
{
@@ -81,7 +73,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
Font font = new Font(Font.MONOSPACED, Font.BOLD, 30);
for (int x = 9; x >= 0; x--)
for(int x = 9; x >= 0; x--)
{
Digit digit = null;
@@ -89,13 +81,13 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
{
digit = new Digit(x);
}
catch (ParseException e)
catch(ParseException e)
{
mLog.error("JFrequencyControl - parse exception "
+ "constructing a digit - " + e);
+ "constructing a digit - " + e);
}
if (digit != null)
if(digit != null)
{
mDigits.put(x, digit);
@@ -103,7 +95,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
digit.setFont(font);
if (x == 6)
if(x == 6)
{
JLabel period = new JLabel(".");
@@ -124,7 +116,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
Image cursorImage = Toolkit.getDefaultToolkit().createImage(imageByte);
mBlankCursor = Toolkit.getDefaultToolkit()
.createCustomCursor(cursorImage, new Point(0, 0), "cursor");
.createCustomCursor(cursorImage, new Point(0, 0), "cursor");
revalidate();
}
@@ -134,7 +126,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
{
super.setEnabled(enabled);
for (Digit digit : mDigits.values())
for(Digit digit : mDigits.values())
{
digit.setEnabled(enabled);
}
@@ -145,9 +137,9 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
* rebroadcast this event, just set the control to indicate the new frequency.
*/
@Override
public void frequencyChanged(FrequencyChangeEvent event)
public void process(SourceEvent event)
{
if (event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE)
if(event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE)
{
setFrequency(event.getValue().longValue(), false);
}
@@ -161,7 +153,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
{
mFrequency = frequency;
for (Digit digit : mDigits.values())
for(Digit digit : mDigits.values())
{
digit.setFrequency(frequency, fireChangeEvent);
}
@@ -176,7 +168,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
{
long frequency = 0;
for (Digit digit : mDigits.values())
for(Digit digit : mDigits.values())
{
frequency += digit.getFrequency();
}
@@ -184,27 +176,26 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
mFrequency = frequency;
}
private void fireFrequencyChanged() throws SourceException
private void fireSourceChanged() throws SourceException
{
updateFrequency();
Iterator<IFrequencyChangeProcessor> it = mProcessors.iterator();
Iterator<ISourceEventProcessor> it = mProcessors.iterator();
FrequencyChangeEvent event = new FrequencyChangeEvent(
Event.REQUEST_FREQUENCY_CHANGE, mFrequency);
SourceEvent event = SourceEvent.frequencyRequest(mFrequency);
while (it.hasNext())
while(it.hasNext())
{
it.next().frequencyChanged(event);
it.next().process(event);
}
}
public void addListener(IFrequencyChangeProcessor processor)
public void addListener(ISourceEventProcessor processor)
{
mProcessors.add(processor);
}
public void removeListener(IFrequencyChangeProcessor processor)
public void removeListener(ISourceEventProcessor processor)
{
mProcessors.remove(processor);
}
@@ -236,17 +227,17 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
public void setFrequency(long frequency, boolean fireChangeEvent)
{
//Strip the digits higher than this one
long lower = frequency % (long) (Math.pow(10, mPower + 1));
long lower = frequency % (long)(Math.pow(10, mPower + 1));
//Set the value to int value of dividing by 10 to this power
long value = (long) (lower / (long) (Math.pow(10, mPower)));
long value = (long)(lower / (long)(Math.pow(10, mPower)));
set(value, fireChangeEvent);
}
public long getFrequency()
{
return mValue * (long) Math.pow(10, mPower);
return mValue * (long)Math.pow(10, mPower);
}
public void increment()
@@ -256,7 +247,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
public void increment(boolean fireChangeEvent)
{
if (isEnabled())
if(isEnabled())
{
set(mValue + 1, fireChangeEvent);
}
@@ -269,7 +260,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
public void decrement(boolean fireChangeEvent)
{
if (isEnabled())
if(isEnabled())
{
set(mValue - 1, fireChangeEvent);
}
@@ -295,13 +286,13 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
mValue = amount;
while (mValue < 0)
while(mValue < 0)
{
mValue += 10;
Digit nextHigherDigit = mDigits.get(mPower + 1);
if (nextHigherDigit != null)
if(nextHigherDigit != null)
{
nextHigherDigit.decrement(false);
higherDigitDecremented = true;
@@ -309,55 +300,55 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
}
while (mValue > 9)
while(mValue > 9)
{
mValue -= 10;
Digit nextHigherDigit = mDigits.get(mPower + 1);
if (nextHigherDigit != null)
if(nextHigherDigit != null)
{
nextHigherDigit.increment(false);
higherDigitIncremented = true;
}
}
if (fireChangeEvent)
if(fireChangeEvent)
{
try
{
fireFrequencyChanged();
fireSourceChanged();
}
catch (SourceException se)
catch(SourceException se)
{
mValue = previous;
if (higherDigitIncremented)
if(higherDigitIncremented)
{
Digit nextHigherDigit = mDigits.get(mPower + 1);
if (nextHigherDigit != null)
if(nextHigherDigit != null)
{
nextHigherDigit.decrement(false);
}
}
if (higherDigitDecremented)
if(higherDigitDecremented)
{
Digit nextHigherDigit = mDigits.get(mPower + 1);
if (nextHigherDigit != null)
if(nextHigherDigit != null)
{
nextHigherDigit.increment(false);
}
}
if (se instanceof InvalidFrequencyException)
if(se instanceof InvalidFrequencyException)
{
InvalidFrequencyException ife = (InvalidFrequencyException) se;
InvalidFrequencyException ife = (InvalidFrequencyException)se;
JOptionPane.showMessageDialog(this, "Frequency [" + ife.getInvalidFrequency() +
"] exceeds the frequency limit [" + ife.getValidFrequency() + "] for this tuner.");
"] exceeds the frequency limit [" + ife.getValidFrequency() + "] for this tuner.");
}
}
}
@@ -369,15 +360,15 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
}
private class Listener implements KeyListener,
MouseListener,
MouseWheelListener
MouseListener,
MouseWheelListener
{
@Override
public void keyReleased(KeyEvent e)
{
int key = e.getKeyCode();
switch (key)
switch(key)
{
case KeyEvent.VK_0:
case KeyEvent.VK_NUMPAD0:
@@ -464,7 +455,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
@Override
public void mouseClicked(MouseEvent e)
{
switch (e.getButton())
switch(e.getButton())
{
case MouseEvent.BUTTON1:
increment();

View File

@@ -30,10 +30,8 @@ import channel.metadata.IAttributeChangeRequestProvider;
import channel.state.ChannelState;
import channel.state.DecoderState;
import channel.state.DecoderStateEvent;
import channel.state.DecoderStateEvent.Event;
import channel.state.IDecoderStateEventListener;
import channel.state.IDecoderStateEventProvider;
import channel.state.State;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent;
import controller.channel.IChannelEventListener;
@@ -63,11 +61,9 @@ import sample.real.RealBuffer;
import source.ComplexSource;
import source.RealSource;
import source.Source;
import source.SourceException;
import source.tuner.TunerChannelSource;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.IFrequencyChangeListener;
import source.tuner.frequency.IFrequencyChangeProvider;
import source.ISourceEventListener;
import source.ISourceEventProvider;
import source.SourceEvent;
import util.ThreadPool;
import java.util.ArrayList;
@@ -102,7 +98,7 @@ public class ProcessingChain implements IChannelEventListener
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster<>();
private Broadcaster<ComplexBuffer> mComplexBufferBroadcaster = new Broadcaster<>();
private Broadcaster<DecoderStateEvent> mDecoderStateEventBroadcaster = new Broadcaster<>();
private Broadcaster<FrequencyChangeEvent> mFrequencyChangeEventBroadcaster = new Broadcaster<>();
private Broadcaster<SourceEvent> mFrequencyChangeEventBroadcaster = new Broadcaster<>();
private Broadcaster<Message> mMessageBroadcaster = new Broadcaster<>();
private Broadcaster<RealBuffer> mFilteredRealBufferBroadcaster = new Broadcaster<>();
private Broadcaster<RealBuffer> mUnFilteredRealBufferBroadcaster = new Broadcaster<>();
@@ -318,9 +314,9 @@ public class ProcessingChain implements IChannelEventListener
mDecoderStateEventBroadcaster.addListener(((IDecoderStateEventListener) module).getDecoderStateListener());
}
if(module instanceof IFrequencyChangeListener)
if(module instanceof ISourceEventListener)
{
mFrequencyChangeEventBroadcaster.addListener(((IFrequencyChangeListener) module).getFrequencyChangeListener());
mFrequencyChangeEventBroadcaster.addListener(((ISourceEventListener) module).getSourceEventListener());
}
if(module instanceof IMessageListener)
@@ -380,9 +376,9 @@ public class ProcessingChain implements IChannelEventListener
mDecoderStateEventBroadcaster.removeListener(((IDecoderStateEventListener) module).getDecoderStateListener());
}
if(module instanceof IFrequencyChangeListener)
if(module instanceof ISourceEventListener)
{
mFrequencyChangeEventBroadcaster.removeListener(((IFrequencyChangeListener) module).getFrequencyChangeListener());
mFrequencyChangeEventBroadcaster.removeListener(((ISourceEventListener) module).getSourceEventListener());
}
if(module instanceof IMessageListener)
@@ -437,9 +433,9 @@ public class ProcessingChain implements IChannelEventListener
((IDecoderStateEventProvider) module).setDecoderStateListener(mDecoderStateEventBroadcaster);
}
if(module instanceof IFrequencyChangeProvider)
if(module instanceof IFilteredRealBufferProvider)
{
((IFrequencyChangeProvider) module).setFrequencyChangeListener(mFrequencyChangeEventBroadcaster);
((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(mFilteredRealBufferBroadcaster);
}
if(module instanceof IMessageProvider)
@@ -447,9 +443,9 @@ public class ProcessingChain implements IChannelEventListener
((IMessageProvider) module).setMessageListener(mMessageBroadcaster);
}
if(module instanceof IFilteredRealBufferProvider)
if(module instanceof ISourceEventProvider)
{
((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(mFilteredRealBufferBroadcaster);
((ISourceEventProvider) module).setSourceEventListener(mFrequencyChangeEventBroadcaster);
}
if(module instanceof ISquelchStateProvider)
@@ -494,9 +490,9 @@ public class ProcessingChain implements IChannelEventListener
((IDecoderStateEventProvider) module).setDecoderStateListener(null);
}
if(module instanceof IFrequencyChangeProvider)
if(module instanceof IFilteredRealBufferProvider)
{
((IFrequencyChangeProvider) module).setFrequencyChangeListener(null);
((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(null);
}
if(module instanceof IMessageProvider)
@@ -504,9 +500,9 @@ public class ProcessingChain implements IChannelEventListener
((IMessageProvider) module).setMessageListener(null);
}
if(module instanceof IFilteredRealBufferProvider)
if(module instanceof ISourceEventProvider)
{
((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(null);
((ISourceEventProvider) module).setSourceEventListener(null);
}
if(module instanceof ISquelchStateProvider)
@@ -743,7 +739,7 @@ public class ProcessingChain implements IChannelEventListener
* Adds the listener to receive frequency change events from the processing chain
* @param listener to receive events
*/
public void addFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
public void addFrequencyChangeListener(Listener<SourceEvent> listener)
{
mFrequencyChangeEventBroadcaster.addListener(listener);
}
@@ -752,7 +748,7 @@ public class ProcessingChain implements IChannelEventListener
* Removes the listener from receiving frequency change events
* @param listener to remove
*/
public void removeFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
public void removeFrequencyChangeListener(Listener<SourceEvent> listener)
{
mFrequencyChangeEventBroadcaster.removeListener(listener);
}

View File

@@ -1,512 +1,508 @@
package module.decode.p25;
import dsp.gain.DirectGainControl;
import instrument.Instrumentable;
import instrument.tap.Tap;
import instrument.tap.TapGroup;
import instrument.tap.stream.AdditiveFloatTap;
import instrument.tap.stream.FloatTap;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.real.RealBuffer;
import sample.real.RealSampleListener;
import sample.real.RealSampleProvider;
import source.tuner.TunerChannelSource;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Event;
import source.tuner.frequency.IFrequencyChangeListener;
import dsp.gain.DirectGainControl;
import source.ISourceEventListener;
import source.SourceEvent;
public class C4FMSymbolFilter implements Listener<RealBuffer>, IFrequencyChangeListener, RealSampleProvider, Instrumentable
import java.util.ArrayList;
import java.util.List;
public class C4FMSymbolFilter implements Listener<RealBuffer>, ISourceEventListener, RealSampleProvider, Instrumentable
{
private static final float TAPS[][] =
{
{ 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f }, // 0/128
{ -1.54700e-04f, 8.53777e-04f, -2.76968e-03f, 7.89295e-03f, 9.98534e-01f, -5.41054e-03f, 1.24642e-03f, -1.98993e-04f }, // 1/128
{ -3.09412e-04f, 1.70888e-03f, -5.55134e-03f, 1.58840e-02f, 9.96891e-01f, -1.07209e-02f, 2.47942e-03f, -3.96391e-04f }, // 2/128
{ -4.64053e-04f, 2.56486e-03f, -8.34364e-03f, 2.39714e-02f, 9.95074e-01f, -1.59305e-02f, 3.69852e-03f, -5.92100e-04f }, // 3/128
{ -6.18544e-04f, 3.42130e-03f, -1.11453e-02f, 3.21531e-02f, 9.93082e-01f, -2.10389e-02f, 4.90322e-03f, -7.86031e-04f }, // 4/128
{ -7.72802e-04f, 4.27773e-03f, -1.39548e-02f, 4.04274e-02f, 9.90917e-01f, -2.60456e-02f, 6.09305e-03f, -9.78093e-04f }, // 5/128
{ -9.26747e-04f, 5.13372e-03f, -1.67710e-02f, 4.87921e-02f, 9.88580e-01f, -3.09503e-02f, 7.26755e-03f, -1.16820e-03f }, // 6/128
{ -1.08030e-03f, 5.98883e-03f, -1.95925e-02f, 5.72454e-02f, 9.86071e-01f, -3.57525e-02f, 8.42626e-03f, -1.35627e-03f }, // 7/128
{ -1.23337e-03f, 6.84261e-03f, -2.24178e-02f, 6.57852e-02f, 9.83392e-01f, -4.04519e-02f, 9.56876e-03f, -1.54221e-03f }, // 8/128
{ -1.38589e-03f, 7.69462e-03f, -2.52457e-02f, 7.44095e-02f, 9.80543e-01f, -4.50483e-02f, 1.06946e-02f, -1.72594e-03f }, // 9/128
{ -1.53777e-03f, 8.54441e-03f, -2.80746e-02f, 8.31162e-02f, 9.77526e-01f, -4.95412e-02f, 1.18034e-02f, -1.90738e-03f }, // 10/128
{ -1.68894e-03f, 9.39154e-03f, -3.09033e-02f, 9.19033e-02f, 9.74342e-01f, -5.39305e-02f, 1.28947e-02f, -2.08645e-03f }, // 11/128
{ -1.83931e-03f, 1.02356e-02f, -3.37303e-02f, 1.00769e-01f, 9.70992e-01f, -5.82159e-02f, 1.39681e-02f, -2.26307e-03f }, // 12/128
{ -1.98880e-03f, 1.10760e-02f, -3.65541e-02f, 1.09710e-01f, 9.67477e-01f, -6.23972e-02f, 1.50233e-02f, -2.43718e-03f }, // 13/128
{ -2.13733e-03f, 1.19125e-02f, -3.93735e-02f, 1.18725e-01f, 9.63798e-01f, -6.64743e-02f, 1.60599e-02f, -2.60868e-03f }, // 14/128
{ -2.28483e-03f, 1.27445e-02f, -4.21869e-02f, 1.27812e-01f, 9.59958e-01f, -7.04471e-02f, 1.70776e-02f, -2.77751e-03f }, // 15/128
{ -2.43121e-03f, 1.35716e-02f, -4.49929e-02f, 1.36968e-01f, 9.55956e-01f, -7.43154e-02f, 1.80759e-02f, -2.94361e-03f }, // 16/128
{ -2.57640e-03f, 1.43934e-02f, -4.77900e-02f, 1.46192e-01f, 9.51795e-01f, -7.80792e-02f, 1.90545e-02f, -3.10689e-03f }, // 17/128
{ -2.72032e-03f, 1.52095e-02f, -5.05770e-02f, 1.55480e-01f, 9.47477e-01f, -8.17385e-02f, 2.00132e-02f, -3.26730e-03f }, // 18/128
{ -2.86289e-03f, 1.60193e-02f, -5.33522e-02f, 1.64831e-01f, 9.43001e-01f, -8.52933e-02f, 2.09516e-02f, -3.42477e-03f }, // 19/128
{ -3.00403e-03f, 1.68225e-02f, -5.61142e-02f, 1.74242e-01f, 9.38371e-01f, -8.87435e-02f, 2.18695e-02f, -3.57923e-03f }, // 20/128
{ -3.14367e-03f, 1.76185e-02f, -5.88617e-02f, 1.83711e-01f, 9.33586e-01f, -9.20893e-02f, 2.27664e-02f, -3.73062e-03f }, // 21/128
{ -3.28174e-03f, 1.84071e-02f, -6.15931e-02f, 1.93236e-01f, 9.28650e-01f, -9.53307e-02f, 2.36423e-02f, -3.87888e-03f }, // 22/128
{ -3.41815e-03f, 1.91877e-02f, -6.43069e-02f, 2.02814e-01f, 9.23564e-01f, -9.84679e-02f, 2.44967e-02f, -4.02397e-03f }, // 23/128
{ -3.55283e-03f, 1.99599e-02f, -6.70018e-02f, 2.12443e-01f, 9.18329e-01f, -1.01501e-01f, 2.53295e-02f, -4.16581e-03f }, // 24/128
{ -3.68570e-03f, 2.07233e-02f, -6.96762e-02f, 2.22120e-01f, 9.12947e-01f, -1.04430e-01f, 2.61404e-02f, -4.30435e-03f }, // 25/128
{ -3.81671e-03f, 2.14774e-02f, -7.23286e-02f, 2.31843e-01f, 9.07420e-01f, -1.07256e-01f, 2.69293e-02f, -4.43955e-03f }, // 26/128
{ -3.94576e-03f, 2.22218e-02f, -7.49577e-02f, 2.41609e-01f, 9.01749e-01f, -1.09978e-01f, 2.76957e-02f, -4.57135e-03f }, // 27/128
{ -4.07279e-03f, 2.29562e-02f, -7.75620e-02f, 2.51417e-01f, 8.95936e-01f, -1.12597e-01f, 2.84397e-02f, -4.69970e-03f }, // 28/128
{ -4.19774e-03f, 2.36801e-02f, -8.01399e-02f, 2.61263e-01f, 8.89984e-01f, -1.15113e-01f, 2.91609e-02f, -4.82456e-03f }, // 29/128
{ -4.32052e-03f, 2.43930e-02f, -8.26900e-02f, 2.71144e-01f, 8.83893e-01f, -1.17526e-01f, 2.98593e-02f, -4.94589e-03f }, // 30/128
{ -4.44107e-03f, 2.50946e-02f, -8.52109e-02f, 2.81060e-01f, 8.77666e-01f, -1.19837e-01f, 3.05345e-02f, -5.06363e-03f }, // 31/128
{ -4.55932e-03f, 2.57844e-02f, -8.77011e-02f, 2.91006e-01f, 8.71305e-01f, -1.22047e-01f, 3.11866e-02f, -5.17776e-03f }, // 32/128
{ -4.67520e-03f, 2.64621e-02f, -9.01591e-02f, 3.00980e-01f, 8.64812e-01f, -1.24154e-01f, 3.18153e-02f, -5.28823e-03f }, // 33/128
{ -4.78866e-03f, 2.71272e-02f, -9.25834e-02f, 3.10980e-01f, 8.58189e-01f, -1.26161e-01f, 3.24205e-02f, -5.39500e-03f }, // 34/128
{ -4.89961e-03f, 2.77794e-02f, -9.49727e-02f, 3.21004e-01f, 8.51437e-01f, -1.28068e-01f, 3.30021e-02f, -5.49804e-03f }, // 35/128
{ -5.00800e-03f, 2.84182e-02f, -9.73254e-02f, 3.31048e-01f, 8.44559e-01f, -1.29874e-01f, 3.35600e-02f, -5.59731e-03f }, // 36/128
{ -5.11376e-03f, 2.90433e-02f, -9.96402e-02f, 3.41109e-01f, 8.37557e-01f, -1.31581e-01f, 3.40940e-02f, -5.69280e-03f }, // 37/128
{ -5.21683e-03f, 2.96543e-02f, -1.01915e-01f, 3.51186e-01f, 8.30432e-01f, -1.33189e-01f, 3.46042e-02f, -5.78446e-03f }, // 38/128
{ -5.31716e-03f, 3.02507e-02f, -1.04150e-01f, 3.61276e-01f, 8.23188e-01f, -1.34699e-01f, 3.50903e-02f, -5.87227e-03f }, // 39/128
{ -5.41467e-03f, 3.08323e-02f, -1.06342e-01f, 3.71376e-01f, 8.15826e-01f, -1.36111e-01f, 3.55525e-02f, -5.95620e-03f }, // 40/128
{ -5.50931e-03f, 3.13987e-02f, -1.08490e-01f, 3.81484e-01f, 8.08348e-01f, -1.37426e-01f, 3.59905e-02f, -6.03624e-03f }, // 41/128
{ -5.60103e-03f, 3.19495e-02f, -1.10593e-01f, 3.91596e-01f, 8.00757e-01f, -1.38644e-01f, 3.64044e-02f, -6.11236e-03f }, // 42/128
{ -5.68976e-03f, 3.24843e-02f, -1.12650e-01f, 4.01710e-01f, 7.93055e-01f, -1.39767e-01f, 3.67941e-02f, -6.18454e-03f }, // 43/128
{ -5.77544e-03f, 3.30027e-02f, -1.14659e-01f, 4.11823e-01f, 7.85244e-01f, -1.40794e-01f, 3.71596e-02f, -6.25277e-03f }, // 44/128
{ -5.85804e-03f, 3.35046e-02f, -1.16618e-01f, 4.21934e-01f, 7.77327e-01f, -1.41727e-01f, 3.75010e-02f, -6.31703e-03f }, // 45/128
{ -5.93749e-03f, 3.39894e-02f, -1.18526e-01f, 4.32038e-01f, 7.69305e-01f, -1.42566e-01f, 3.78182e-02f, -6.37730e-03f }, // 46/128
{ -6.01374e-03f, 3.44568e-02f, -1.20382e-01f, 4.42134e-01f, 7.61181e-01f, -1.43313e-01f, 3.81111e-02f, -6.43358e-03f }, // 47/128
{ -6.08674e-03f, 3.49066e-02f, -1.22185e-01f, 4.52218e-01f, 7.52958e-01f, -1.43968e-01f, 3.83800e-02f, -6.48585e-03f }, // 48/128
{ -6.15644e-03f, 3.53384e-02f, -1.23933e-01f, 4.62289e-01f, 7.44637e-01f, -1.44531e-01f, 3.86247e-02f, -6.53412e-03f }, // 49/128
{ -6.22280e-03f, 3.57519e-02f, -1.25624e-01f, 4.72342e-01f, 7.36222e-01f, -1.45004e-01f, 3.88454e-02f, -6.57836e-03f }, // 50/128
{ -6.28577e-03f, 3.61468e-02f, -1.27258e-01f, 4.82377e-01f, 7.27714e-01f, -1.45387e-01f, 3.90420e-02f, -6.61859e-03f }, // 51/128
{ -6.34530e-03f, 3.65227e-02f, -1.28832e-01f, 4.92389e-01f, 7.19116e-01f, -1.45682e-01f, 3.92147e-02f, -6.65479e-03f }, // 52/128
{ -6.40135e-03f, 3.68795e-02f, -1.30347e-01f, 5.02377e-01f, 7.10431e-01f, -1.45889e-01f, 3.93636e-02f, -6.68698e-03f }, // 53/128
{ -6.45388e-03f, 3.72167e-02f, -1.31800e-01f, 5.12337e-01f, 7.01661e-01f, -1.46009e-01f, 3.94886e-02f, -6.71514e-03f }, // 54/128
{ -6.50285e-03f, 3.75341e-02f, -1.33190e-01f, 5.22267e-01f, 6.92808e-01f, -1.46043e-01f, 3.95900e-02f, -6.73929e-03f }, // 55/128
{ -6.54823e-03f, 3.78315e-02f, -1.34515e-01f, 5.32164e-01f, 6.83875e-01f, -1.45993e-01f, 3.96678e-02f, -6.75943e-03f }, // 56/128
{ -6.58996e-03f, 3.81085e-02f, -1.35775e-01f, 5.42025e-01f, 6.74865e-01f, -1.45859e-01f, 3.97222e-02f, -6.77557e-03f }, // 57/128
{ -6.62802e-03f, 3.83650e-02f, -1.36969e-01f, 5.51849e-01f, 6.65779e-01f, -1.45641e-01f, 3.97532e-02f, -6.78771e-03f }, // 58/128
{ -6.66238e-03f, 3.86006e-02f, -1.38094e-01f, 5.61631e-01f, 6.56621e-01f, -1.45343e-01f, 3.97610e-02f, -6.79588e-03f }, // 59/128
{ -6.69300e-03f, 3.88151e-02f, -1.39150e-01f, 5.71370e-01f, 6.47394e-01f, -1.44963e-01f, 3.97458e-02f, -6.80007e-03f }, // 60/128
{ -6.71985e-03f, 3.90083e-02f, -1.40136e-01f, 5.81063e-01f, 6.38099e-01f, -1.44503e-01f, 3.97077e-02f, -6.80032e-03f }, // 61/128
{ -6.74291e-03f, 3.91800e-02f, -1.41050e-01f, 5.90706e-01f, 6.28739e-01f, -1.43965e-01f, 3.96469e-02f, -6.79662e-03f }, // 62/128
{ -6.76214e-03f, 3.93299e-02f, -1.41891e-01f, 6.00298e-01f, 6.19318e-01f, -1.43350e-01f, 3.95635e-02f, -6.78902e-03f }, // 63/128
{ -6.77751e-03f, 3.94578e-02f, -1.42658e-01f, 6.09836e-01f, 6.09836e-01f, -1.42658e-01f, 3.94578e-02f, -6.77751e-03f }, // 64/128
{ -6.78902e-03f, 3.95635e-02f, -1.43350e-01f, 6.19318e-01f, 6.00298e-01f, -1.41891e-01f, 3.93299e-02f, -6.76214e-03f }, // 65/128
{ -6.79662e-03f, 3.96469e-02f, -1.43965e-01f, 6.28739e-01f, 5.90706e-01f, -1.41050e-01f, 3.91800e-02f, -6.74291e-03f }, // 66/128
{ -6.80032e-03f, 3.97077e-02f, -1.44503e-01f, 6.38099e-01f, 5.81063e-01f, -1.40136e-01f, 3.90083e-02f, -6.71985e-03f }, // 67/128
{ -6.80007e-03f, 3.97458e-02f, -1.44963e-01f, 6.47394e-01f, 5.71370e-01f, -1.39150e-01f, 3.88151e-02f, -6.69300e-03f }, // 68/128
{ -6.79588e-03f, 3.97610e-02f, -1.45343e-01f, 6.56621e-01f, 5.61631e-01f, -1.38094e-01f, 3.86006e-02f, -6.66238e-03f }, // 69/128
{ -6.78771e-03f, 3.97532e-02f, -1.45641e-01f, 6.65779e-01f, 5.51849e-01f, -1.36969e-01f, 3.83650e-02f, -6.62802e-03f }, // 70/128
{ -6.77557e-03f, 3.97222e-02f, -1.45859e-01f, 6.74865e-01f, 5.42025e-01f, -1.35775e-01f, 3.81085e-02f, -6.58996e-03f }, // 71/128
{ -6.75943e-03f, 3.96678e-02f, -1.45993e-01f, 6.83875e-01f, 5.32164e-01f, -1.34515e-01f, 3.78315e-02f, -6.54823e-03f }, // 72/128
{ -6.73929e-03f, 3.95900e-02f, -1.46043e-01f, 6.92808e-01f, 5.22267e-01f, -1.33190e-01f, 3.75341e-02f, -6.50285e-03f }, // 73/128
{ -6.71514e-03f, 3.94886e-02f, -1.46009e-01f, 7.01661e-01f, 5.12337e-01f, -1.31800e-01f, 3.72167e-02f, -6.45388e-03f }, // 74/128
{ -6.68698e-03f, 3.93636e-02f, -1.45889e-01f, 7.10431e-01f, 5.02377e-01f, -1.30347e-01f, 3.68795e-02f, -6.40135e-03f }, // 75/128
{ -6.65479e-03f, 3.92147e-02f, -1.45682e-01f, 7.19116e-01f, 4.92389e-01f, -1.28832e-01f, 3.65227e-02f, -6.34530e-03f }, // 76/128
{ -6.61859e-03f, 3.90420e-02f, -1.45387e-01f, 7.27714e-01f, 4.82377e-01f, -1.27258e-01f, 3.61468e-02f, -6.28577e-03f }, // 77/128
{ -6.57836e-03f, 3.88454e-02f, -1.45004e-01f, 7.36222e-01f, 4.72342e-01f, -1.25624e-01f, 3.57519e-02f, -6.22280e-03f }, // 78/128
{ -6.53412e-03f, 3.86247e-02f, -1.44531e-01f, 7.44637e-01f, 4.62289e-01f, -1.23933e-01f, 3.53384e-02f, -6.15644e-03f }, // 79/128
{ -6.48585e-03f, 3.83800e-02f, -1.43968e-01f, 7.52958e-01f, 4.52218e-01f, -1.22185e-01f, 3.49066e-02f, -6.08674e-03f }, // 80/128
{ -6.43358e-03f, 3.81111e-02f, -1.43313e-01f, 7.61181e-01f, 4.42134e-01f, -1.20382e-01f, 3.44568e-02f, -6.01374e-03f }, // 81/128
{ -6.37730e-03f, 3.78182e-02f, -1.42566e-01f, 7.69305e-01f, 4.32038e-01f, -1.18526e-01f, 3.39894e-02f, -5.93749e-03f }, // 82/128
{ -6.31703e-03f, 3.75010e-02f, -1.41727e-01f, 7.77327e-01f, 4.21934e-01f, -1.16618e-01f, 3.35046e-02f, -5.85804e-03f }, // 83/128
{ -6.25277e-03f, 3.71596e-02f, -1.40794e-01f, 7.85244e-01f, 4.11823e-01f, -1.14659e-01f, 3.30027e-02f, -5.77544e-03f }, // 84/128
{ -6.18454e-03f, 3.67941e-02f, -1.39767e-01f, 7.93055e-01f, 4.01710e-01f, -1.12650e-01f, 3.24843e-02f, -5.68976e-03f }, // 85/128
{ -6.11236e-03f, 3.64044e-02f, -1.38644e-01f, 8.00757e-01f, 3.91596e-01f, -1.10593e-01f, 3.19495e-02f, -5.60103e-03f }, // 86/128
{ -6.03624e-03f, 3.59905e-02f, -1.37426e-01f, 8.08348e-01f, 3.81484e-01f, -1.08490e-01f, 3.13987e-02f, -5.50931e-03f }, // 87/128
{ -5.95620e-03f, 3.55525e-02f, -1.36111e-01f, 8.15826e-01f, 3.71376e-01f, -1.06342e-01f, 3.08323e-02f, -5.41467e-03f }, // 88/128
{ -5.87227e-03f, 3.50903e-02f, -1.34699e-01f, 8.23188e-01f, 3.61276e-01f, -1.04150e-01f, 3.02507e-02f, -5.31716e-03f }, // 89/128
{ -5.78446e-03f, 3.46042e-02f, -1.33189e-01f, 8.30432e-01f, 3.51186e-01f, -1.01915e-01f, 2.96543e-02f, -5.21683e-03f }, // 90/128
{ -5.69280e-03f, 3.40940e-02f, -1.31581e-01f, 8.37557e-01f, 3.41109e-01f, -9.96402e-02f, 2.90433e-02f, -5.11376e-03f }, // 91/128
{ -5.59731e-03f, 3.35600e-02f, -1.29874e-01f, 8.44559e-01f, 3.31048e-01f, -9.73254e-02f, 2.84182e-02f, -5.00800e-03f }, // 92/128
{ -5.49804e-03f, 3.30021e-02f, -1.28068e-01f, 8.51437e-01f, 3.21004e-01f, -9.49727e-02f, 2.77794e-02f, -4.89961e-03f }, // 93/128
{ -5.39500e-03f, 3.24205e-02f, -1.26161e-01f, 8.58189e-01f, 3.10980e-01f, -9.25834e-02f, 2.71272e-02f, -4.78866e-03f }, // 94/128
{ -5.28823e-03f, 3.18153e-02f, -1.24154e-01f, 8.64812e-01f, 3.00980e-01f, -9.01591e-02f, 2.64621e-02f, -4.67520e-03f }, // 95/128
{ -5.17776e-03f, 3.11866e-02f, -1.22047e-01f, 8.71305e-01f, 2.91006e-01f, -8.77011e-02f, 2.57844e-02f, -4.55932e-03f }, // 96/128
{ -5.06363e-03f, 3.05345e-02f, -1.19837e-01f, 8.77666e-01f, 2.81060e-01f, -8.52109e-02f, 2.50946e-02f, -4.44107e-03f }, // 97/128
{ -4.94589e-03f, 2.98593e-02f, -1.17526e-01f, 8.83893e-01f, 2.71144e-01f, -8.26900e-02f, 2.43930e-02f, -4.32052e-03f }, // 98/128
{ -4.82456e-03f, 2.91609e-02f, -1.15113e-01f, 8.89984e-01f, 2.61263e-01f, -8.01399e-02f, 2.36801e-02f, -4.19774e-03f }, // 99/128
{ -4.69970e-03f, 2.84397e-02f, -1.12597e-01f, 8.95936e-01f, 2.51417e-01f, -7.75620e-02f, 2.29562e-02f, -4.07279e-03f }, // 100/128
{ -4.57135e-03f, 2.76957e-02f, -1.09978e-01f, 9.01749e-01f, 2.41609e-01f, -7.49577e-02f, 2.22218e-02f, -3.94576e-03f }, // 101/128
{ -4.43955e-03f, 2.69293e-02f, -1.07256e-01f, 9.07420e-01f, 2.31843e-01f, -7.23286e-02f, 2.14774e-02f, -3.81671e-03f }, // 102/128
{ -4.30435e-03f, 2.61404e-02f, -1.04430e-01f, 9.12947e-01f, 2.22120e-01f, -6.96762e-02f, 2.07233e-02f, -3.68570e-03f }, // 103/128
{ -4.16581e-03f, 2.53295e-02f, -1.01501e-01f, 9.18329e-01f, 2.12443e-01f, -6.70018e-02f, 1.99599e-02f, -3.55283e-03f }, // 104/128
{ -4.02397e-03f, 2.44967e-02f, -9.84679e-02f, 9.23564e-01f, 2.02814e-01f, -6.43069e-02f, 1.91877e-02f, -3.41815e-03f }, // 105/128
{ -3.87888e-03f, 2.36423e-02f, -9.53307e-02f, 9.28650e-01f, 1.93236e-01f, -6.15931e-02f, 1.84071e-02f, -3.28174e-03f }, // 106/128
{ -3.73062e-03f, 2.27664e-02f, -9.20893e-02f, 9.33586e-01f, 1.83711e-01f, -5.88617e-02f, 1.76185e-02f, -3.14367e-03f }, // 107/128
{ -3.57923e-03f, 2.18695e-02f, -8.87435e-02f, 9.38371e-01f, 1.74242e-01f, -5.61142e-02f, 1.68225e-02f, -3.00403e-03f }, // 108/128
{ -3.42477e-03f, 2.09516e-02f, -8.52933e-02f, 9.43001e-01f, 1.64831e-01f, -5.33522e-02f, 1.60193e-02f, -2.86289e-03f }, // 109/128
{ -3.26730e-03f, 2.00132e-02f, -8.17385e-02f, 9.47477e-01f, 1.55480e-01f, -5.05770e-02f, 1.52095e-02f, -2.72032e-03f }, // 110/128
{ -3.10689e-03f, 1.90545e-02f, -7.80792e-02f, 9.51795e-01f, 1.46192e-01f, -4.77900e-02f, 1.43934e-02f, -2.57640e-03f }, // 111/128
{ -2.94361e-03f, 1.80759e-02f, -7.43154e-02f, 9.55956e-01f, 1.36968e-01f, -4.49929e-02f, 1.35716e-02f, -2.43121e-03f }, // 112/128
{ -2.77751e-03f, 1.70776e-02f, -7.04471e-02f, 9.59958e-01f, 1.27812e-01f, -4.21869e-02f, 1.27445e-02f, -2.28483e-03f }, // 113/128
{ -2.60868e-03f, 1.60599e-02f, -6.64743e-02f, 9.63798e-01f, 1.18725e-01f, -3.93735e-02f, 1.19125e-02f, -2.13733e-03f }, // 114/128
{ -2.43718e-03f, 1.50233e-02f, -6.23972e-02f, 9.67477e-01f, 1.09710e-01f, -3.65541e-02f, 1.10760e-02f, -1.98880e-03f }, // 115/128
{ -2.26307e-03f, 1.39681e-02f, -5.82159e-02f, 9.70992e-01f, 1.00769e-01f, -3.37303e-02f, 1.02356e-02f, -1.83931e-03f }, // 116/128
{ -2.08645e-03f, 1.28947e-02f, -5.39305e-02f, 9.74342e-01f, 9.19033e-02f, -3.09033e-02f, 9.39154e-03f, -1.68894e-03f }, // 117/128
{ -1.90738e-03f, 1.18034e-02f, -4.95412e-02f, 9.77526e-01f, 8.31162e-02f, -2.80746e-02f, 8.54441e-03f, -1.53777e-03f }, // 118/128
{ -1.72594e-03f, 1.06946e-02f, -4.50483e-02f, 9.80543e-01f, 7.44095e-02f, -2.52457e-02f, 7.69462e-03f, -1.38589e-03f }, // 119/128
{ -1.54221e-03f, 9.56876e-03f, -4.04519e-02f, 9.83392e-01f, 6.57852e-02f, -2.24178e-02f, 6.84261e-03f, -1.23337e-03f }, // 120/128
{ -1.35627e-03f, 8.42626e-03f, -3.57525e-02f, 9.86071e-01f, 5.72454e-02f, -1.95925e-02f, 5.98883e-03f, -1.08030e-03f }, // 121/128
{ -1.16820e-03f, 7.26755e-03f, -3.09503e-02f, 9.88580e-01f, 4.87921e-02f, -1.67710e-02f, 5.13372e-03f, -9.26747e-04f }, // 122/128
{ -9.78093e-04f, 6.09305e-03f, -2.60456e-02f, 9.90917e-01f, 4.04274e-02f, -1.39548e-02f, 4.27773e-03f, -7.72802e-04f }, // 123/128
{ -7.86031e-04f, 4.90322e-03f, -2.10389e-02f, 9.93082e-01f, 3.21531e-02f, -1.11453e-02f, 3.42130e-03f, -6.18544e-04f }, // 124/128
{ -5.92100e-04f, 3.69852e-03f, -1.59305e-02f, 9.95074e-01f, 2.39714e-02f, -8.34364e-03f, 2.56486e-03f, -4.64053e-04f }, // 125/128
{ -3.96391e-04f, 2.47942e-03f, -1.07209e-02f, 9.96891e-01f, 1.58840e-02f, -5.55134e-03f, 1.70888e-03f, -3.09412e-04f }, // 126/128
{ -1.98993e-04f, 1.24642e-03f, -5.41054e-03f, 9.98534e-01f, 7.89295e-03f, -2.76968e-03f, 8.53777e-04f, -1.54700e-04f }, // 127/128
{ 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f }, // 128/128
};
private static final float TAPS[][] =
{
{0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f}, // 0/128
{-1.54700e-04f, 8.53777e-04f, -2.76968e-03f, 7.89295e-03f, 9.98534e-01f, -5.41054e-03f, 1.24642e-03f, -1.98993e-04f}, // 1/128
{-3.09412e-04f, 1.70888e-03f, -5.55134e-03f, 1.58840e-02f, 9.96891e-01f, -1.07209e-02f, 2.47942e-03f, -3.96391e-04f}, // 2/128
{-4.64053e-04f, 2.56486e-03f, -8.34364e-03f, 2.39714e-02f, 9.95074e-01f, -1.59305e-02f, 3.69852e-03f, -5.92100e-04f}, // 3/128
{-6.18544e-04f, 3.42130e-03f, -1.11453e-02f, 3.21531e-02f, 9.93082e-01f, -2.10389e-02f, 4.90322e-03f, -7.86031e-04f}, // 4/128
{-7.72802e-04f, 4.27773e-03f, -1.39548e-02f, 4.04274e-02f, 9.90917e-01f, -2.60456e-02f, 6.09305e-03f, -9.78093e-04f}, // 5/128
{-9.26747e-04f, 5.13372e-03f, -1.67710e-02f, 4.87921e-02f, 9.88580e-01f, -3.09503e-02f, 7.26755e-03f, -1.16820e-03f}, // 6/128
{-1.08030e-03f, 5.98883e-03f, -1.95925e-02f, 5.72454e-02f, 9.86071e-01f, -3.57525e-02f, 8.42626e-03f, -1.35627e-03f}, // 7/128
{-1.23337e-03f, 6.84261e-03f, -2.24178e-02f, 6.57852e-02f, 9.83392e-01f, -4.04519e-02f, 9.56876e-03f, -1.54221e-03f}, // 8/128
{-1.38589e-03f, 7.69462e-03f, -2.52457e-02f, 7.44095e-02f, 9.80543e-01f, -4.50483e-02f, 1.06946e-02f, -1.72594e-03f}, // 9/128
{-1.53777e-03f, 8.54441e-03f, -2.80746e-02f, 8.31162e-02f, 9.77526e-01f, -4.95412e-02f, 1.18034e-02f, -1.90738e-03f}, // 10/128
{-1.68894e-03f, 9.39154e-03f, -3.09033e-02f, 9.19033e-02f, 9.74342e-01f, -5.39305e-02f, 1.28947e-02f, -2.08645e-03f}, // 11/128
{-1.83931e-03f, 1.02356e-02f, -3.37303e-02f, 1.00769e-01f, 9.70992e-01f, -5.82159e-02f, 1.39681e-02f, -2.26307e-03f}, // 12/128
{-1.98880e-03f, 1.10760e-02f, -3.65541e-02f, 1.09710e-01f, 9.67477e-01f, -6.23972e-02f, 1.50233e-02f, -2.43718e-03f}, // 13/128
{-2.13733e-03f, 1.19125e-02f, -3.93735e-02f, 1.18725e-01f, 9.63798e-01f, -6.64743e-02f, 1.60599e-02f, -2.60868e-03f}, // 14/128
{-2.28483e-03f, 1.27445e-02f, -4.21869e-02f, 1.27812e-01f, 9.59958e-01f, -7.04471e-02f, 1.70776e-02f, -2.77751e-03f}, // 15/128
{-2.43121e-03f, 1.35716e-02f, -4.49929e-02f, 1.36968e-01f, 9.55956e-01f, -7.43154e-02f, 1.80759e-02f, -2.94361e-03f}, // 16/128
{-2.57640e-03f, 1.43934e-02f, -4.77900e-02f, 1.46192e-01f, 9.51795e-01f, -7.80792e-02f, 1.90545e-02f, -3.10689e-03f}, // 17/128
{-2.72032e-03f, 1.52095e-02f, -5.05770e-02f, 1.55480e-01f, 9.47477e-01f, -8.17385e-02f, 2.00132e-02f, -3.26730e-03f}, // 18/128
{-2.86289e-03f, 1.60193e-02f, -5.33522e-02f, 1.64831e-01f, 9.43001e-01f, -8.52933e-02f, 2.09516e-02f, -3.42477e-03f}, // 19/128
{-3.00403e-03f, 1.68225e-02f, -5.61142e-02f, 1.74242e-01f, 9.38371e-01f, -8.87435e-02f, 2.18695e-02f, -3.57923e-03f}, // 20/128
{-3.14367e-03f, 1.76185e-02f, -5.88617e-02f, 1.83711e-01f, 9.33586e-01f, -9.20893e-02f, 2.27664e-02f, -3.73062e-03f}, // 21/128
{-3.28174e-03f, 1.84071e-02f, -6.15931e-02f, 1.93236e-01f, 9.28650e-01f, -9.53307e-02f, 2.36423e-02f, -3.87888e-03f}, // 22/128
{-3.41815e-03f, 1.91877e-02f, -6.43069e-02f, 2.02814e-01f, 9.23564e-01f, -9.84679e-02f, 2.44967e-02f, -4.02397e-03f}, // 23/128
{-3.55283e-03f, 1.99599e-02f, -6.70018e-02f, 2.12443e-01f, 9.18329e-01f, -1.01501e-01f, 2.53295e-02f, -4.16581e-03f}, // 24/128
{-3.68570e-03f, 2.07233e-02f, -6.96762e-02f, 2.22120e-01f, 9.12947e-01f, -1.04430e-01f, 2.61404e-02f, -4.30435e-03f}, // 25/128
{-3.81671e-03f, 2.14774e-02f, -7.23286e-02f, 2.31843e-01f, 9.07420e-01f, -1.07256e-01f, 2.69293e-02f, -4.43955e-03f}, // 26/128
{-3.94576e-03f, 2.22218e-02f, -7.49577e-02f, 2.41609e-01f, 9.01749e-01f, -1.09978e-01f, 2.76957e-02f, -4.57135e-03f}, // 27/128
{-4.07279e-03f, 2.29562e-02f, -7.75620e-02f, 2.51417e-01f, 8.95936e-01f, -1.12597e-01f, 2.84397e-02f, -4.69970e-03f}, // 28/128
{-4.19774e-03f, 2.36801e-02f, -8.01399e-02f, 2.61263e-01f, 8.89984e-01f, -1.15113e-01f, 2.91609e-02f, -4.82456e-03f}, // 29/128
{-4.32052e-03f, 2.43930e-02f, -8.26900e-02f, 2.71144e-01f, 8.83893e-01f, -1.17526e-01f, 2.98593e-02f, -4.94589e-03f}, // 30/128
{-4.44107e-03f, 2.50946e-02f, -8.52109e-02f, 2.81060e-01f, 8.77666e-01f, -1.19837e-01f, 3.05345e-02f, -5.06363e-03f}, // 31/128
{-4.55932e-03f, 2.57844e-02f, -8.77011e-02f, 2.91006e-01f, 8.71305e-01f, -1.22047e-01f, 3.11866e-02f, -5.17776e-03f}, // 32/128
{-4.67520e-03f, 2.64621e-02f, -9.01591e-02f, 3.00980e-01f, 8.64812e-01f, -1.24154e-01f, 3.18153e-02f, -5.28823e-03f}, // 33/128
{-4.78866e-03f, 2.71272e-02f, -9.25834e-02f, 3.10980e-01f, 8.58189e-01f, -1.26161e-01f, 3.24205e-02f, -5.39500e-03f}, // 34/128
{-4.89961e-03f, 2.77794e-02f, -9.49727e-02f, 3.21004e-01f, 8.51437e-01f, -1.28068e-01f, 3.30021e-02f, -5.49804e-03f}, // 35/128
{-5.00800e-03f, 2.84182e-02f, -9.73254e-02f, 3.31048e-01f, 8.44559e-01f, -1.29874e-01f, 3.35600e-02f, -5.59731e-03f}, // 36/128
{-5.11376e-03f, 2.90433e-02f, -9.96402e-02f, 3.41109e-01f, 8.37557e-01f, -1.31581e-01f, 3.40940e-02f, -5.69280e-03f}, // 37/128
{-5.21683e-03f, 2.96543e-02f, -1.01915e-01f, 3.51186e-01f, 8.30432e-01f, -1.33189e-01f, 3.46042e-02f, -5.78446e-03f}, // 38/128
{-5.31716e-03f, 3.02507e-02f, -1.04150e-01f, 3.61276e-01f, 8.23188e-01f, -1.34699e-01f, 3.50903e-02f, -5.87227e-03f}, // 39/128
{-5.41467e-03f, 3.08323e-02f, -1.06342e-01f, 3.71376e-01f, 8.15826e-01f, -1.36111e-01f, 3.55525e-02f, -5.95620e-03f}, // 40/128
{-5.50931e-03f, 3.13987e-02f, -1.08490e-01f, 3.81484e-01f, 8.08348e-01f, -1.37426e-01f, 3.59905e-02f, -6.03624e-03f}, // 41/128
{-5.60103e-03f, 3.19495e-02f, -1.10593e-01f, 3.91596e-01f, 8.00757e-01f, -1.38644e-01f, 3.64044e-02f, -6.11236e-03f}, // 42/128
{-5.68976e-03f, 3.24843e-02f, -1.12650e-01f, 4.01710e-01f, 7.93055e-01f, -1.39767e-01f, 3.67941e-02f, -6.18454e-03f}, // 43/128
{-5.77544e-03f, 3.30027e-02f, -1.14659e-01f, 4.11823e-01f, 7.85244e-01f, -1.40794e-01f, 3.71596e-02f, -6.25277e-03f}, // 44/128
{-5.85804e-03f, 3.35046e-02f, -1.16618e-01f, 4.21934e-01f, 7.77327e-01f, -1.41727e-01f, 3.75010e-02f, -6.31703e-03f}, // 45/128
{-5.93749e-03f, 3.39894e-02f, -1.18526e-01f, 4.32038e-01f, 7.69305e-01f, -1.42566e-01f, 3.78182e-02f, -6.37730e-03f}, // 46/128
{-6.01374e-03f, 3.44568e-02f, -1.20382e-01f, 4.42134e-01f, 7.61181e-01f, -1.43313e-01f, 3.81111e-02f, -6.43358e-03f}, // 47/128
{-6.08674e-03f, 3.49066e-02f, -1.22185e-01f, 4.52218e-01f, 7.52958e-01f, -1.43968e-01f, 3.83800e-02f, -6.48585e-03f}, // 48/128
{-6.15644e-03f, 3.53384e-02f, -1.23933e-01f, 4.62289e-01f, 7.44637e-01f, -1.44531e-01f, 3.86247e-02f, -6.53412e-03f}, // 49/128
{-6.22280e-03f, 3.57519e-02f, -1.25624e-01f, 4.72342e-01f, 7.36222e-01f, -1.45004e-01f, 3.88454e-02f, -6.57836e-03f}, // 50/128
{-6.28577e-03f, 3.61468e-02f, -1.27258e-01f, 4.82377e-01f, 7.27714e-01f, -1.45387e-01f, 3.90420e-02f, -6.61859e-03f}, // 51/128
{-6.34530e-03f, 3.65227e-02f, -1.28832e-01f, 4.92389e-01f, 7.19116e-01f, -1.45682e-01f, 3.92147e-02f, -6.65479e-03f}, // 52/128
{-6.40135e-03f, 3.68795e-02f, -1.30347e-01f, 5.02377e-01f, 7.10431e-01f, -1.45889e-01f, 3.93636e-02f, -6.68698e-03f}, // 53/128
{-6.45388e-03f, 3.72167e-02f, -1.31800e-01f, 5.12337e-01f, 7.01661e-01f, -1.46009e-01f, 3.94886e-02f, -6.71514e-03f}, // 54/128
{-6.50285e-03f, 3.75341e-02f, -1.33190e-01f, 5.22267e-01f, 6.92808e-01f, -1.46043e-01f, 3.95900e-02f, -6.73929e-03f}, // 55/128
{-6.54823e-03f, 3.78315e-02f, -1.34515e-01f, 5.32164e-01f, 6.83875e-01f, -1.45993e-01f, 3.96678e-02f, -6.75943e-03f}, // 56/128
{-6.58996e-03f, 3.81085e-02f, -1.35775e-01f, 5.42025e-01f, 6.74865e-01f, -1.45859e-01f, 3.97222e-02f, -6.77557e-03f}, // 57/128
{-6.62802e-03f, 3.83650e-02f, -1.36969e-01f, 5.51849e-01f, 6.65779e-01f, -1.45641e-01f, 3.97532e-02f, -6.78771e-03f}, // 58/128
{-6.66238e-03f, 3.86006e-02f, -1.38094e-01f, 5.61631e-01f, 6.56621e-01f, -1.45343e-01f, 3.97610e-02f, -6.79588e-03f}, // 59/128
{-6.69300e-03f, 3.88151e-02f, -1.39150e-01f, 5.71370e-01f, 6.47394e-01f, -1.44963e-01f, 3.97458e-02f, -6.80007e-03f}, // 60/128
{-6.71985e-03f, 3.90083e-02f, -1.40136e-01f, 5.81063e-01f, 6.38099e-01f, -1.44503e-01f, 3.97077e-02f, -6.80032e-03f}, // 61/128
{-6.74291e-03f, 3.91800e-02f, -1.41050e-01f, 5.90706e-01f, 6.28739e-01f, -1.43965e-01f, 3.96469e-02f, -6.79662e-03f}, // 62/128
{-6.76214e-03f, 3.93299e-02f, -1.41891e-01f, 6.00298e-01f, 6.19318e-01f, -1.43350e-01f, 3.95635e-02f, -6.78902e-03f}, // 63/128
{-6.77751e-03f, 3.94578e-02f, -1.42658e-01f, 6.09836e-01f, 6.09836e-01f, -1.42658e-01f, 3.94578e-02f, -6.77751e-03f}, // 64/128
{-6.78902e-03f, 3.95635e-02f, -1.43350e-01f, 6.19318e-01f, 6.00298e-01f, -1.41891e-01f, 3.93299e-02f, -6.76214e-03f}, // 65/128
{-6.79662e-03f, 3.96469e-02f, -1.43965e-01f, 6.28739e-01f, 5.90706e-01f, -1.41050e-01f, 3.91800e-02f, -6.74291e-03f}, // 66/128
{-6.80032e-03f, 3.97077e-02f, -1.44503e-01f, 6.38099e-01f, 5.81063e-01f, -1.40136e-01f, 3.90083e-02f, -6.71985e-03f}, // 67/128
{-6.80007e-03f, 3.97458e-02f, -1.44963e-01f, 6.47394e-01f, 5.71370e-01f, -1.39150e-01f, 3.88151e-02f, -6.69300e-03f}, // 68/128
{-6.79588e-03f, 3.97610e-02f, -1.45343e-01f, 6.56621e-01f, 5.61631e-01f, -1.38094e-01f, 3.86006e-02f, -6.66238e-03f}, // 69/128
{-6.78771e-03f, 3.97532e-02f, -1.45641e-01f, 6.65779e-01f, 5.51849e-01f, -1.36969e-01f, 3.83650e-02f, -6.62802e-03f}, // 70/128
{-6.77557e-03f, 3.97222e-02f, -1.45859e-01f, 6.74865e-01f, 5.42025e-01f, -1.35775e-01f, 3.81085e-02f, -6.58996e-03f}, // 71/128
{-6.75943e-03f, 3.96678e-02f, -1.45993e-01f, 6.83875e-01f, 5.32164e-01f, -1.34515e-01f, 3.78315e-02f, -6.54823e-03f}, // 72/128
{-6.73929e-03f, 3.95900e-02f, -1.46043e-01f, 6.92808e-01f, 5.22267e-01f, -1.33190e-01f, 3.75341e-02f, -6.50285e-03f}, // 73/128
{-6.71514e-03f, 3.94886e-02f, -1.46009e-01f, 7.01661e-01f, 5.12337e-01f, -1.31800e-01f, 3.72167e-02f, -6.45388e-03f}, // 74/128
{-6.68698e-03f, 3.93636e-02f, -1.45889e-01f, 7.10431e-01f, 5.02377e-01f, -1.30347e-01f, 3.68795e-02f, -6.40135e-03f}, // 75/128
{-6.65479e-03f, 3.92147e-02f, -1.45682e-01f, 7.19116e-01f, 4.92389e-01f, -1.28832e-01f, 3.65227e-02f, -6.34530e-03f}, // 76/128
{-6.61859e-03f, 3.90420e-02f, -1.45387e-01f, 7.27714e-01f, 4.82377e-01f, -1.27258e-01f, 3.61468e-02f, -6.28577e-03f}, // 77/128
{-6.57836e-03f, 3.88454e-02f, -1.45004e-01f, 7.36222e-01f, 4.72342e-01f, -1.25624e-01f, 3.57519e-02f, -6.22280e-03f}, // 78/128
{-6.53412e-03f, 3.86247e-02f, -1.44531e-01f, 7.44637e-01f, 4.62289e-01f, -1.23933e-01f, 3.53384e-02f, -6.15644e-03f}, // 79/128
{-6.48585e-03f, 3.83800e-02f, -1.43968e-01f, 7.52958e-01f, 4.52218e-01f, -1.22185e-01f, 3.49066e-02f, -6.08674e-03f}, // 80/128
{-6.43358e-03f, 3.81111e-02f, -1.43313e-01f, 7.61181e-01f, 4.42134e-01f, -1.20382e-01f, 3.44568e-02f, -6.01374e-03f}, // 81/128
{-6.37730e-03f, 3.78182e-02f, -1.42566e-01f, 7.69305e-01f, 4.32038e-01f, -1.18526e-01f, 3.39894e-02f, -5.93749e-03f}, // 82/128
{-6.31703e-03f, 3.75010e-02f, -1.41727e-01f, 7.77327e-01f, 4.21934e-01f, -1.16618e-01f, 3.35046e-02f, -5.85804e-03f}, // 83/128
{-6.25277e-03f, 3.71596e-02f, -1.40794e-01f, 7.85244e-01f, 4.11823e-01f, -1.14659e-01f, 3.30027e-02f, -5.77544e-03f}, // 84/128
{-6.18454e-03f, 3.67941e-02f, -1.39767e-01f, 7.93055e-01f, 4.01710e-01f, -1.12650e-01f, 3.24843e-02f, -5.68976e-03f}, // 85/128
{-6.11236e-03f, 3.64044e-02f, -1.38644e-01f, 8.00757e-01f, 3.91596e-01f, -1.10593e-01f, 3.19495e-02f, -5.60103e-03f}, // 86/128
{-6.03624e-03f, 3.59905e-02f, -1.37426e-01f, 8.08348e-01f, 3.81484e-01f, -1.08490e-01f, 3.13987e-02f, -5.50931e-03f}, // 87/128
{-5.95620e-03f, 3.55525e-02f, -1.36111e-01f, 8.15826e-01f, 3.71376e-01f, -1.06342e-01f, 3.08323e-02f, -5.41467e-03f}, // 88/128
{-5.87227e-03f, 3.50903e-02f, -1.34699e-01f, 8.23188e-01f, 3.61276e-01f, -1.04150e-01f, 3.02507e-02f, -5.31716e-03f}, // 89/128
{-5.78446e-03f, 3.46042e-02f, -1.33189e-01f, 8.30432e-01f, 3.51186e-01f, -1.01915e-01f, 2.96543e-02f, -5.21683e-03f}, // 90/128
{-5.69280e-03f, 3.40940e-02f, -1.31581e-01f, 8.37557e-01f, 3.41109e-01f, -9.96402e-02f, 2.90433e-02f, -5.11376e-03f}, // 91/128
{-5.59731e-03f, 3.35600e-02f, -1.29874e-01f, 8.44559e-01f, 3.31048e-01f, -9.73254e-02f, 2.84182e-02f, -5.00800e-03f}, // 92/128
{-5.49804e-03f, 3.30021e-02f, -1.28068e-01f, 8.51437e-01f, 3.21004e-01f, -9.49727e-02f, 2.77794e-02f, -4.89961e-03f}, // 93/128
{-5.39500e-03f, 3.24205e-02f, -1.26161e-01f, 8.58189e-01f, 3.10980e-01f, -9.25834e-02f, 2.71272e-02f, -4.78866e-03f}, // 94/128
{-5.28823e-03f, 3.18153e-02f, -1.24154e-01f, 8.64812e-01f, 3.00980e-01f, -9.01591e-02f, 2.64621e-02f, -4.67520e-03f}, // 95/128
{-5.17776e-03f, 3.11866e-02f, -1.22047e-01f, 8.71305e-01f, 2.91006e-01f, -8.77011e-02f, 2.57844e-02f, -4.55932e-03f}, // 96/128
{-5.06363e-03f, 3.05345e-02f, -1.19837e-01f, 8.77666e-01f, 2.81060e-01f, -8.52109e-02f, 2.50946e-02f, -4.44107e-03f}, // 97/128
{-4.94589e-03f, 2.98593e-02f, -1.17526e-01f, 8.83893e-01f, 2.71144e-01f, -8.26900e-02f, 2.43930e-02f, -4.32052e-03f}, // 98/128
{-4.82456e-03f, 2.91609e-02f, -1.15113e-01f, 8.89984e-01f, 2.61263e-01f, -8.01399e-02f, 2.36801e-02f, -4.19774e-03f}, // 99/128
{-4.69970e-03f, 2.84397e-02f, -1.12597e-01f, 8.95936e-01f, 2.51417e-01f, -7.75620e-02f, 2.29562e-02f, -4.07279e-03f}, // 100/128
{-4.57135e-03f, 2.76957e-02f, -1.09978e-01f, 9.01749e-01f, 2.41609e-01f, -7.49577e-02f, 2.22218e-02f, -3.94576e-03f}, // 101/128
{-4.43955e-03f, 2.69293e-02f, -1.07256e-01f, 9.07420e-01f, 2.31843e-01f, -7.23286e-02f, 2.14774e-02f, -3.81671e-03f}, // 102/128
{-4.30435e-03f, 2.61404e-02f, -1.04430e-01f, 9.12947e-01f, 2.22120e-01f, -6.96762e-02f, 2.07233e-02f, -3.68570e-03f}, // 103/128
{-4.16581e-03f, 2.53295e-02f, -1.01501e-01f, 9.18329e-01f, 2.12443e-01f, -6.70018e-02f, 1.99599e-02f, -3.55283e-03f}, // 104/128
{-4.02397e-03f, 2.44967e-02f, -9.84679e-02f, 9.23564e-01f, 2.02814e-01f, -6.43069e-02f, 1.91877e-02f, -3.41815e-03f}, // 105/128
{-3.87888e-03f, 2.36423e-02f, -9.53307e-02f, 9.28650e-01f, 1.93236e-01f, -6.15931e-02f, 1.84071e-02f, -3.28174e-03f}, // 106/128
{-3.73062e-03f, 2.27664e-02f, -9.20893e-02f, 9.33586e-01f, 1.83711e-01f, -5.88617e-02f, 1.76185e-02f, -3.14367e-03f}, // 107/128
{-3.57923e-03f, 2.18695e-02f, -8.87435e-02f, 9.38371e-01f, 1.74242e-01f, -5.61142e-02f, 1.68225e-02f, -3.00403e-03f}, // 108/128
{-3.42477e-03f, 2.09516e-02f, -8.52933e-02f, 9.43001e-01f, 1.64831e-01f, -5.33522e-02f, 1.60193e-02f, -2.86289e-03f}, // 109/128
{-3.26730e-03f, 2.00132e-02f, -8.17385e-02f, 9.47477e-01f, 1.55480e-01f, -5.05770e-02f, 1.52095e-02f, -2.72032e-03f}, // 110/128
{-3.10689e-03f, 1.90545e-02f, -7.80792e-02f, 9.51795e-01f, 1.46192e-01f, -4.77900e-02f, 1.43934e-02f, -2.57640e-03f}, // 111/128
{-2.94361e-03f, 1.80759e-02f, -7.43154e-02f, 9.55956e-01f, 1.36968e-01f, -4.49929e-02f, 1.35716e-02f, -2.43121e-03f}, // 112/128
{-2.77751e-03f, 1.70776e-02f, -7.04471e-02f, 9.59958e-01f, 1.27812e-01f, -4.21869e-02f, 1.27445e-02f, -2.28483e-03f}, // 113/128
{-2.60868e-03f, 1.60599e-02f, -6.64743e-02f, 9.63798e-01f, 1.18725e-01f, -3.93735e-02f, 1.19125e-02f, -2.13733e-03f}, // 114/128
{-2.43718e-03f, 1.50233e-02f, -6.23972e-02f, 9.67477e-01f, 1.09710e-01f, -3.65541e-02f, 1.10760e-02f, -1.98880e-03f}, // 115/128
{-2.26307e-03f, 1.39681e-02f, -5.82159e-02f, 9.70992e-01f, 1.00769e-01f, -3.37303e-02f, 1.02356e-02f, -1.83931e-03f}, // 116/128
{-2.08645e-03f, 1.28947e-02f, -5.39305e-02f, 9.74342e-01f, 9.19033e-02f, -3.09033e-02f, 9.39154e-03f, -1.68894e-03f}, // 117/128
{-1.90738e-03f, 1.18034e-02f, -4.95412e-02f, 9.77526e-01f, 8.31162e-02f, -2.80746e-02f, 8.54441e-03f, -1.53777e-03f}, // 118/128
{-1.72594e-03f, 1.06946e-02f, -4.50483e-02f, 9.80543e-01f, 7.44095e-02f, -2.52457e-02f, 7.69462e-03f, -1.38589e-03f}, // 119/128
{-1.54221e-03f, 9.56876e-03f, -4.04519e-02f, 9.83392e-01f, 6.57852e-02f, -2.24178e-02f, 6.84261e-03f, -1.23337e-03f}, // 120/128
{-1.35627e-03f, 8.42626e-03f, -3.57525e-02f, 9.86071e-01f, 5.72454e-02f, -1.95925e-02f, 5.98883e-03f, -1.08030e-03f}, // 121/128
{-1.16820e-03f, 7.26755e-03f, -3.09503e-02f, 9.88580e-01f, 4.87921e-02f, -1.67710e-02f, 5.13372e-03f, -9.26747e-04f}, // 122/128
{-9.78093e-04f, 6.09305e-03f, -2.60456e-02f, 9.90917e-01f, 4.04274e-02f, -1.39548e-02f, 4.27773e-03f, -7.72802e-04f}, // 123/128
{-7.86031e-04f, 4.90322e-03f, -2.10389e-02f, 9.93082e-01f, 3.21531e-02f, -1.11453e-02f, 3.42130e-03f, -6.18544e-04f}, // 124/128
{-5.92100e-04f, 3.69852e-03f, -1.59305e-02f, 9.95074e-01f, 2.39714e-02f, -8.34364e-03f, 2.56486e-03f, -4.64053e-04f}, // 125/128
{-3.96391e-04f, 2.47942e-03f, -1.07209e-02f, 9.96891e-01f, 1.58840e-02f, -5.55134e-03f, 1.70888e-03f, -3.09412e-04f}, // 126/128
{-1.98993e-04f, 1.24642e-03f, -5.41054e-03f, 9.98534e-01f, 7.89295e-03f, -2.76968e-03f, 8.53777e-04f, -1.54700e-04f}, // 127/128
{0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f}, // 128/128
};
/* Instrumentation Taps */
private static final String INSTRUMENT_SYMBOL_SPREAD = "Tap Point: Symbol Spread (Goal=2.0)";
private FloatTap mSymbolSpreadTap;
private static final String INSTRUMENT_SYMBOL_SPREAD = "Tap Point: Symbol Spread (Goal=2.0)";
private FloatTap mSymbolSpreadTap;
private List<TapGroup> mAvailableTaps;
private static final int NUMBER_FILTER_TAPS = 8;
private static final int NUMBER_FILTER_STEPS = 128;
private static final int SAMPLE_RATE = 48000;
private static final int SYMBOL_RATE = 4800;
/* Tracking loop gain constant */
private static final double K_SYMBOL_SPREAD = 0.0100;
/* Constraints on symbol spreading */
private static final float SYMBOL_SPREAD_MAX = 2.4f; // upper range limit: +20%
private static final float SYMBOL_SPREAD_MIN = 1.6f; // lower range limit: -20%
private static final int NUMBER_FILTER_TAPS = 8;
private static final int NUMBER_FILTER_STEPS = 128;
/* Symbol clock tracking loop gain */
private static final double K_SYMBOL_TIMING = 0.025;
/* Coarse and fine frequency tracking constants */
private static final double K_COARSE_FREQUENCY = 0.00125;
private static final double K_FINE_FREQUENCY = 0.125;
/* Frequency correction broadcast threshold */
private static final int SAMPLE_RATE = 48000;
private static final int SYMBOL_RATE = 4800;
/* Tracking loop gain constant */
private static final double K_SYMBOL_SPREAD = 0.0100;
/* Constraints on symbol spreading */
private static final float SYMBOL_SPREAD_MAX = 2.4f; // upper range limit: +20%
private static final float SYMBOL_SPREAD_MIN = 1.6f; // lower range limit: -20%
/* Symbol clock tracking loop gain */
private static final double K_SYMBOL_TIMING = 0.025;
/* Coarse and fine frequency tracking constants */
private static final double K_COARSE_FREQUENCY = 0.00125;
private static final double K_FINE_FREQUENCY = 0.125;
/* Frequency correction broadcast threshold */
// private static final double COARSE_FREQUENCY_DEADBAND = 1.66;
private static final double COARSE_FREQUENCY_THRESHOLD = 1.20;
/* 2.0 symbol spread gives -3, -1, 1, 3 */
private float mSymbolSpread = 2.0f;
private float mSymbolClock = 0.0f;
private float mSymbolTime = (float)SYMBOL_RATE / (float)SAMPLE_RATE;
private float mFineFrequencyCorrection = 0.0f;
private float mCoarseFrequencyCorrection = 0.0f;
private static final double COARSE_FREQUENCY_THRESHOLD = 1.20;
private float mHistory[] = new float[ NUMBER_FILTER_TAPS ];
private int mHistoryLast = 0;
private RealSampleListener mListener;
private DirectGainControl mGainController = new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f );
/* 2.0 symbol spread gives -3, -1, 1, 3 */
private float mSymbolSpread = 2.0f;
private float mSymbolClock = 0.0f;
private float mSymbolTime = (float)SYMBOL_RATE / (float)SAMPLE_RATE;
private int mFrequencyAdjustmentRequested = 0;
private int mFrequencyCorrection = 0;
private int mFrequencyCorrectionMaximum = 3000;
private boolean mResetFrequencyTracker = false;
private FrequencyCorrectionProcessor mFrequencyCorrectionProcessor;
private Listener<FrequencyChangeEvent> mFrequencyChangeListener;
/**
* C4FM Symbol Filter
*
* Sample Gain values - the gain of incoming sample values will critically
* impact the performance of this filter. Gain should be adjusted to
* optimize the value of mSymbolSpread toward a value of 2.0. If the value
* is less than 2.0, then increase gain. If the value is over 2.0, then
* decrease gain.
*
* When preceded by the RealAutomaticGainFilter class, an optimal gain
* setting for this filter is 15.4 and that yields a symbol spread that
* is centered on 2.0, ranging 1.92 to 2.08.
*/
public C4FMSymbolFilter( int frequencyCorrectionMaximum )
{
mFrequencyCorrectionMaximum = frequencyCorrectionMaximum;
}
public void dispose()
{
mGainController = null;
private float mFineFrequencyCorrection = 0.0f;
private float mCoarseFrequencyCorrection = 0.0f;
private float mHistory[] = new float[NUMBER_FILTER_TAPS];
private int mHistoryLast = 0;
private RealSampleListener mListener;
private DirectGainControl mGainController = new DirectGainControl(15.0f, 0.1f, 35.0f, 0.3f);
private int mFrequencyAdjustmentRequested = 0;
private int mFrequencyCorrection = 0;
private int mFrequencyCorrectionMaximum = 3000;
private boolean mResetFrequencyTracker = false;
private FrequencyCorrectionProcessor mFrequencyCorrectionProcessor;
private Listener<SourceEvent> mFrequencyChangeListener;
/**
* C4FM Symbol Filter
*
* Sample Gain values - the gain of incoming sample values will critically
* impact the performance of this filter. Gain should be adjusted to
* optimize the value of mSymbolSpread toward a value of 2.0. If the value
* is less than 2.0, then increase gain. If the value is over 2.0, then
* decrease gain.
*
* When preceded by the RealAutomaticGainFilter class, an optimal gain
* setting for this filter is 15.4 and that yields a symbol spread that
* is centered on 2.0, ranging 1.92 to 2.08.
*/
public C4FMSymbolFilter(int frequencyCorrectionMaximum)
{
mFrequencyCorrectionMaximum = frequencyCorrectionMaximum;
}
public void dispose()
{
mGainController = null;
// mFrequencyCorrectionControl = null;
mListener = null;
}
@Override
public void receive( RealBuffer buffer )
{
for( float sample: buffer.getSamples() )
{
receive( sample );
}
mListener = null;
}
@Override
public void receive(RealBuffer buffer)
{
for(float sample : buffer.getSamples())
{
receive(sample);
}
/* If a frequency correction was requested during the processing of this
* buffer, we'll apply the change and it will be reflected in the next
* buffer, we'll apply the change and it will be reflected in the next
* arriving buffer. Reset the lock on frequency correction and reset
* the internal frequency correction tracker */
if( mFrequencyAdjustmentRequested != 0 )
{
int correction = mFrequencyCorrection + mFrequencyAdjustmentRequested;
if( correction > mFrequencyCorrectionMaximum )
{
correction = mFrequencyCorrectionMaximum;
}
else if( correction < -mFrequencyCorrectionMaximum )
{
correction = -mFrequencyCorrectionMaximum;
}
if(mFrequencyAdjustmentRequested != 0)
{
int correction = mFrequencyCorrection + mFrequencyAdjustmentRequested;
broadcast( new FrequencyChangeEvent(Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, correction ) );
if(correction > mFrequencyCorrectionMaximum)
{
correction = mFrequencyCorrectionMaximum;
}
else if(correction < -mFrequencyCorrectionMaximum)
{
correction = -mFrequencyCorrectionMaximum;
}
mFrequencyAdjustmentRequested = 0;
}
}
broadcast(SourceEvent.channelFrequencyCorrectionRequest(correction));
public void receive( float sample )
mFrequencyAdjustmentRequested = 0;
}
}
public void receive(float sample)
{
sample = mGainController.correct( sample );
if( mResetFrequencyTracker )
{
mCoarseFrequencyCorrection = 0.0f;
mFineFrequencyCorrection = 0.0f;
broadcast( new FrequencyChangeEvent(
Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, 0 ));
mResetFrequencyTracker = false;
}
mSymbolClock += mSymbolTime;
mHistory[ mHistoryLast++ ] = sample;
mHistoryLast %= NUMBER_FILTER_TAPS;
if( mSymbolClock > 1.0f )
{
mSymbolClock -= 1.0f;
int imu = (int)Math.floor( 0.5 +
( (float)NUMBER_FILTER_STEPS * ( mSymbolClock / mSymbolTime ) ) );
if( imu >= NUMBER_FILTER_STEPS )
{
imu = NUMBER_FILTER_STEPS - 1;
}
sample = mGainController.correct(sample);
int imu_p1 = imu + 1;
if(mResetFrequencyTracker)
{
mCoarseFrequencyCorrection = 0.0f;
mFineFrequencyCorrection = 0.0f;
int j = mHistoryLast;
double interp = 0.0;
double interp_p1 = 0.0;
for( int i = 0; i < NUMBER_FILTER_TAPS; i++ )
{
interp += TAPS[ imu][ i ] * mHistory[ j ];
interp_p1 += TAPS[ imu_p1 ][ i ] * mHistory[ j ];
j = ( j + 1 ) % NUMBER_FILTER_TAPS;
}
broadcast(SourceEvent.channelFrequencyCorrectionRequest(0l));
mResetFrequencyTracker = false;
}
mSymbolClock += mSymbolTime;
mHistory[mHistoryLast++] = sample;
mHistoryLast %= NUMBER_FILTER_TAPS;
if(mSymbolClock > 1.0f)
{
mSymbolClock -= 1.0f;
int imu = (int)Math.floor(0.5 +
((float)NUMBER_FILTER_STEPS * (mSymbolClock / mSymbolTime)));
if(imu >= NUMBER_FILTER_STEPS)
{
imu = NUMBER_FILTER_STEPS - 1;
}
int imu_p1 = imu + 1;
int j = mHistoryLast;
double interp = 0.0;
double interp_p1 = 0.0;
for(int i = 0; i < NUMBER_FILTER_TAPS; i++)
{
interp += TAPS[imu][i] * mHistory[j];
interp_p1 += TAPS[imu_p1][i] * mHistory[j];
j = (j + 1) % NUMBER_FILTER_TAPS;
}
/* Output symbol will be interpolated value corrected for symbol
* spread and frequency offset */
interp -= mFineFrequencyCorrection;
interp_p1 -= mFineFrequencyCorrection;
interp -= mFineFrequencyCorrection;
interp_p1 -= mFineFrequencyCorrection;
/* Correct output for symbol deviation (spread) */
float output = (float)( 2.0 * interp / mSymbolSpread );
float output = (float)(2.0 * interp / mSymbolSpread);
/* Detect received symbol error: basically use a hard decision and
* subtract off expected position nominal symbol level which will be
* +/- 0.5 * symbol spread and +/- 1.5 symbol spread. Remember that
* nominal symbol spread will be 2.0 */
double symbolError;
if( interp < -mSymbolSpread )
{
double symbolError;
if(interp < -mSymbolSpread)
{
/* symbol is -3: Expected at -1.5 * symbol spread */
symbolError = interp + ( 1.5 * mSymbolSpread );
mSymbolSpread -= ( symbolError * 0.5 * K_SYMBOL_SPREAD );
}
else if( interp < 0.0 )
{
symbolError = interp + (1.5 * mSymbolSpread);
mSymbolSpread -= (symbolError * 0.5 * K_SYMBOL_SPREAD);
}
else if(interp < 0.0)
{
/* symbol is -1: Expected at -0.5 * symbol_spread */
symbolError = interp + (0.5 * mSymbolSpread );
mSymbolSpread -= ( symbolError * K_SYMBOL_SPREAD );
}
else if( interp < mSymbolSpread )
{
symbolError = interp + (0.5 * mSymbolSpread);
mSymbolSpread -= (symbolError * K_SYMBOL_SPREAD);
}
else if(interp < mSymbolSpread)
{
/* symbol is +1: Expected at +0.5 * symbol_spread */
symbolError = interp - ( 0.5 * mSymbolSpread );
mSymbolSpread += ( symbolError * K_SYMBOL_SPREAD );
}
else
{
symbolError = interp - (0.5 * mSymbolSpread);
mSymbolSpread += (symbolError * K_SYMBOL_SPREAD);
}
else
{
/* symbol is +3: Expected at +1.5 * symbol_spread */
symbolError = interp - ( 1.5 * mSymbolSpread );
mSymbolSpread += ( symbolError * 0.5 * K_SYMBOL_SPREAD );
}
symbolError = interp - (1.5 * mSymbolSpread);
mSymbolSpread += (symbolError * 0.5 * K_SYMBOL_SPREAD);
}
/* Symbol clock tracking loop adjustment */
if( interp_p1 < interp )
{
mSymbolClock += symbolError * K_SYMBOL_TIMING;
}
else
{
mSymbolClock -= symbolError * K_SYMBOL_TIMING;
}
if( mSymbolSpread < SYMBOL_SPREAD_MIN )
{
mGainController.increase();
mSymbolSpread = SYMBOL_SPREAD_MIN;
}
else if( mSymbolSpread > SYMBOL_SPREAD_MAX )
{
mGainController.decrease();
if(interp_p1 < interp)
{
mSymbolClock += symbolError * K_SYMBOL_TIMING;
}
else
{
mSymbolClock -= symbolError * K_SYMBOL_TIMING;
}
mSymbolSpread = SYMBOL_SPREAD_MAX;
}
if(mSymbolSpread < SYMBOL_SPREAD_MIN)
{
mGainController.increase();
mCoarseFrequencyCorrection += ( ( mFineFrequencyCorrection -
mCoarseFrequencyCorrection ) * K_COARSE_FREQUENCY );
mFineFrequencyCorrection += ( symbolError * K_FINE_FREQUENCY );
mSymbolSpread = SYMBOL_SPREAD_MIN;
}
else if(mSymbolSpread > SYMBOL_SPREAD_MAX)
{
mGainController.decrease();
mSymbolSpread = SYMBOL_SPREAD_MAX;
}
mCoarseFrequencyCorrection += ((mFineFrequencyCorrection -
mCoarseFrequencyCorrection) * K_COARSE_FREQUENCY);
mFineFrequencyCorrection += (symbolError * K_FINE_FREQUENCY);
/* Queue a frequency adjustment (once per buffer) as needed */
if( Math.abs( mCoarseFrequencyCorrection ) > COARSE_FREQUENCY_THRESHOLD )
{
mFrequencyAdjustmentRequested =
500 * ( mCoarseFrequencyCorrection > 0 ? 1 : -1 );
}
if( mSymbolSpreadTap != null )
{
mSymbolSpreadTap.receive( mSymbolSpread );
}
if(Math.abs(mCoarseFrequencyCorrection) > COARSE_FREQUENCY_THRESHOLD)
{
mFrequencyAdjustmentRequested =
500 * (mCoarseFrequencyCorrection > 0 ? 1 : -1);
}
if(mSymbolSpreadTap != null)
{
mSymbolSpreadTap.receive(mSymbolSpread);
}
/* dispatch the interpolated value to the listener */
if( mListener != null )
{
mListener.receive( output );
}
}
if(mListener != null)
{
mListener.receive(output);
}
}
}
@Override
public void setListener( RealSampleListener listener )
@Override
public void setListener(RealSampleListener listener)
{
mListener = listener;
mListener = listener;
}
@Override
public void removeListener( RealSampleListener listener )
@Override
public void removeListener(RealSampleListener listener)
{
mListener = null;
mListener = null;
}
@Override
@Override
public List<TapGroup> getTapGroups()
{
if( mAvailableTaps == null )
{
mAvailableTaps = new ArrayList<>();
if(mAvailableTaps == null)
{
mAvailableTaps = new ArrayList<>();
/*
* Since the target is 2.0, we subtract 2.0 from the value so that
* a good value is plotted right on the zero center line
*/
TapGroup group = new TapGroup( "C4FM Symbol Filter" );
group.add(
new AdditiveFloatTap( INSTRUMENT_SYMBOL_SPREAD, 0, 0.1f, -2.0f ) );
mAvailableTaps.add( group );
}
return mAvailableTaps;
TapGroup group = new TapGroup("C4FM Symbol Filter");
group.add(
new AdditiveFloatTap(INSTRUMENT_SYMBOL_SPREAD, 0, 0.1f, -2.0f));
mAvailableTaps.add(group);
}
return mAvailableTaps;
}
@Override
public void registerTap( Tap tap )
@Override
public void registerTap(Tap tap)
{
if( tap.getName().contentEquals( INSTRUMENT_SYMBOL_SPREAD ) )
{
mSymbolSpreadTap = (FloatTap)tap;
}
if(tap.getName().contentEquals(INSTRUMENT_SYMBOL_SPREAD))
{
mSymbolSpreadTap = (FloatTap)tap;
}
}
@Override
public void unregisterTap( Tap tap )
@Override
public void unregisterTap(Tap tap)
{
if( tap.getName().contentEquals( INSTRUMENT_SYMBOL_SPREAD ) )
{
mSymbolSpreadTap = null;
}
if(tap.getName().contentEquals(INSTRUMENT_SYMBOL_SPREAD))
{
mSymbolSpreadTap = null;
}
}
/**
* Broadcasts a frequency change event to the registered listener
*
* @param event
*/
public void broadcast( FrequencyChangeEvent event )
{
if( mFrequencyChangeListener != null )
{
mFrequencyChangeListener.receive( event );
}
}
public void broadcast(SourceEvent event)
{
if(mFrequencyChangeListener != null)
{
mFrequencyChangeListener.receive(event);
}
}
/**
* Sets the listener for frequency change events.
*
* @param listener
*/
public void setFrequencyChangeListener( Listener<FrequencyChangeEvent> listener )
{
mFrequencyChangeListener = listener;
}
@Override
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
{
if( mFrequencyCorrectionProcessor == null )
{
mFrequencyCorrectionProcessor = new FrequencyCorrectionProcessor();
}
return mFrequencyCorrectionProcessor;
}
public void setFrequencyChangeListener(Listener<SourceEvent> listener)
{
mFrequencyChangeListener = listener;
}
/**
* Receives notifications that the channel frequency correction has been
* applied and updates internal tracking value.
*/
public class FrequencyCorrectionProcessor implements Listener<FrequencyChangeEvent>
{
@Override
public void receive( FrequencyChangeEvent event )
{
switch( event.getEvent() )
{
case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
mFrequencyCorrection = event.getValue().intValue();
@Override
public Listener<SourceEvent> getSourceEventListener()
{
if(mFrequencyCorrectionProcessor == null)
{
mFrequencyCorrectionProcessor = new FrequencyCorrectionProcessor();
}
//Reset internal frequency tracking
mCoarseFrequencyCorrection = 0.0f;
mFineFrequencyCorrection = 0.0f;
break;
case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE:
case NOTIFICATION_SAMPLE_RATE_CHANGE:
mResetFrequencyTracker = true;
break;
default:
break;
}
}
}
return mFrequencyCorrectionProcessor;
}
/**
* Receives notifications that the channel frequency correction has been
* applied and updates internal tracking value.
*/
public class FrequencyCorrectionProcessor implements Listener<SourceEvent>
{
@Override
public void receive(SourceEvent event)
{
switch(event.getEvent())
{
case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
mFrequencyCorrection = event.getValue().intValue();
//Reset internal frequency tracking
mCoarseFrequencyCorrection = 0.0f;
mFineFrequencyCorrection = 0.0f;
break;
case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE:
case NOTIFICATION_SAMPLE_RATE_CHANGE:
mResetFrequencyTracker = true;
break;
default:
break;
}
}
}
}

View File

@@ -4,10 +4,10 @@ import alias.AliasList;
import instrument.Instrumentable;
import module.decode.Decoder;
import module.decode.DecoderType;
import source.tuner.frequency.IFrequencyChangeListener;
import source.tuner.frequency.IFrequencyChangeProvider;
import source.ISourceEventListener;
import source.ISourceEventProvider;
public abstract class P25Decoder extends Decoder implements IFrequencyChangeListener, IFrequencyChangeProvider, Instrumentable
public abstract class P25Decoder extends Decoder implements ISourceEventListener, ISourceEventProvider, Instrumentable
{
private P25MessageProcessor mMessageProcessor;

View File

@@ -21,7 +21,6 @@ package module.decode.p25;
import instrument.tap.Tap;
import instrument.tap.TapGroup;
import instrument.tap.stream.DibitTap;
import instrument.tap.stream.FloatBufferTap;
import instrument.tap.stream.FloatTap;
import java.util.ArrayList;
@@ -35,12 +34,7 @@ import alias.AliasList;
import sample.Listener;
import sample.real.IFilteredRealBufferListener;
import sample.real.RealBuffer;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.IFrequencyChangeListener;
import source.tuner.frequency.IFrequencyChangeProvider;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
import source.SourceEvent;
public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferListener
{
@@ -222,7 +216,7 @@ public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferLi
}
@Override
public void setFrequencyChangeListener( Listener<FrequencyChangeEvent> listener )
public void setSourceEventListener(Listener<SourceEvent> listener )
{
if( mSymbolFilter != null )
{
@@ -231,7 +225,7 @@ public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferLi
}
@Override
public void removeFrequencyChangeListener()
public void removeSourceEventListener()
{
if( mSymbolFilter != null )
{
@@ -240,11 +234,11 @@ public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferLi
}
@Override
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
public Listener<SourceEvent> getSourceEventListener()
{
if( mSymbolFilter != null )
{
return mSymbolFilter.getFrequencyChangeListener();
return mSymbolFilter.getSourceEventListener();
}
return null;

View File

@@ -42,7 +42,7 @@ import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB;
import dsp.gain.ComplexFeedForwardGainControl;
import dsp.psk.LSMDemodulator;
import dsp.psk.QPSKPolarSlicer;
import source.tuner.frequency.FrequencyChangeEvent;
import source.SourceEvent;
public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
{
@@ -109,22 +109,22 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
}
@Override
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
public void setSourceEventListener(Listener<SourceEvent> listener)
{
}
@Override
public void removeFrequencyChangeListener()
public void removeSourceEventListener()
{
}
@Override
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
public Listener<SourceEvent> getSourceEventListener()
{
return new Listener<FrequencyChangeEvent>()
return new Listener<SourceEvent>()
{
@Override
public void receive(FrequencyChangeEvent frequencyChangeEvent)
public void receive(SourceEvent sourceEvent)
{
//Ignored
}

View File

@@ -2,7 +2,6 @@ package sample;
public enum SampleType
{
COMPLEX, //Baseband
REAL, //Demodulated
COMPLEX_AND_REAL; //Baseband and Demodulated
COMPLEX, // I/Q
REAL; // Real
}

View File

@@ -1,3 +1,21 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source;
import sample.Provider;
@@ -6,8 +24,9 @@ import sample.complex.ComplexBuffer;
public abstract class ComplexSource extends Source implements Provider<ComplexBuffer>
{
public ComplexSource()
{
super( SampleType.COMPLEX );
}
@Override
public SampleType getSampleType()
{
return SampleType.COMPLEX;
}
}

View File

@@ -0,0 +1,35 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source;
import sample.Listener;
/**
* Interface for broadcasting changes to frequency, bandwidth, sample rate,
* and actual sample rate values to all registered listeners.
*
* Note: broadcasted frequency should be the uncorrected, or displayable
* frequency value. All frequency correction aspects should be handled within
* the device that implements the tuning of the frequency.
*/
public interface ISourceEventListener
{
Listener<SourceEvent> getSourceEventListener();
}

View File

@@ -0,0 +1,24 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source;
public interface ISourceEventProcessor
{
void process(SourceEvent event) throws SourceException;
}

View File

@@ -16,15 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source.tuner.frequency;
package source;
import sample.Listener;
/**
* Provider of Frequency Change Events
* Provider of Source Change Events
*/
public interface IFrequencyChangeProvider
public interface ISourceEventProvider
{
void setFrequencyChangeListener( Listener<FrequencyChangeEvent> listener );
void removeFrequencyChangeListener();
void setSourceEventListener(Listener<SourceEvent> listener );
void removeSourceEventListener();
}

View File

@@ -1,14 +1,32 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source;
import sample.Provider;
import sample.SampleType;
import sample.real.RealBuffer;
public abstract class RealSource extends Source implements Provider<RealBuffer>
{
public RealSource()
{
super( SampleType.REAL );
}
@Override
public SampleType getSampleType()
{
return SampleType.REAL;
}
}

View File

@@ -20,38 +20,38 @@ package source;
import module.Module;
import sample.SampleType;
import sample.real.IOverflowListener;
import source.tuner.frequency.IFrequencyChangeListener;
import source.tuner.frequency.IFrequencyChangeProvider;
/**
* Abstract class to define the minimum functionality of a sample data provider.
*/
public abstract class Source extends Module implements IFrequencyChangeListener, IFrequencyChangeProvider
public abstract class Source extends Module implements ISourceEventListener, ISourceEventProvider
{
protected SampleType mSampleType;
protected IOverflowListener mOverflowListener;
public Source( SampleType sampleType )
{
mSampleType = sampleType;
}
/**
* Indicates the type of samples provided by this source: real or complex
*/
public abstract SampleType getSampleType();
/**
* Sample rate provided by this source
*
* @throws SourceException if there is an issue determining the sample rate for this source
*/
public abstract int getSampleRate() throws SourceException;
/**
* Center frequency for this source in Hertz
*
* @throws SourceException if there is an issue in determining the center frequency for this source
*/
public abstract long getFrequency() throws SourceException;
/**
* Process any cleanup actions to prepare for garbage collection of this source
*/
public abstract void dispose();
public SampleType getSampleType()
{
return mSampleType;
}
public void setSampleType( SampleType sampleType )
{
mSampleType = sampleType;
}
/**
* Registers the listener to receive overflow state changes. Use null argument to clear the listener
*/
@@ -63,7 +63,7 @@ public abstract class Source extends Module implements IFrequencyChangeListener,
/**
* Broadcasts an overflow state
*
* @param overflow true if overlow, false if normal
* @param overflow true if overflow, false if normal
*/
protected void broadcastOverflowState(boolean overflow)
{

192
src/source/SourceEvent.java Normal file
View File

@@ -0,0 +1,192 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source;
public class SourceEvent
{
public enum Event
{
NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
NOTIFICATION_CHANNEL_SAMPLE_RATE_CHANGE,
NOTIFICATION_FREQUENCY_CHANGE,
NOTIFICATION_FREQUENCY_CORRECTION_CHANGE,
NOTIFICATION_POLYPHASE_CHANNEL_COUNT_CHANGE,
NOTIFICATION_SAMPLE_RATE_CHANGE,
REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
REQUEST_FREQUENCY_CHANGE,
REQUEST_START_SAMPLE_STREAM,
REQUEST_STOP_SAMPLE_STREAM;
}
private Event mEvent;
private Number mValue;
private Source mSource;
/**
* Private constructor. Use the static constructor methods to create an event.
*/
private SourceEvent(Event event, Number value)
{
mEvent = event;
mValue = value;
}
/**
* Private constructor. Use the static constructor methods to create an event.
*/
private SourceEvent(Event event, Source source)
{
mEvent = event;
mSource = source;
}
/**
* Event description.
*/
public Event getEvent()
{
return mEvent;
}
/**
* Value associated with the event.
*/
public Number getValue()
{
return mValue;
}
public boolean hasValue()
{
return mValue != null;
}
/**
* Source associated with the event
*/
public Source getSource()
{
return mSource;
}
/**
* Indicates if this event contains an optional source
*/
public boolean hasSource()
{
return mSource != null;
}
/**
* Creates a new frequency change event
*
* @param frequency in hertz
*/
public static SourceEvent frequencyChange(long frequency)
{
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_CHANGE, frequency);
}
/**
* Creates a new frequency correction change event
*
* @param frequencyCorrection in hertz
*/
public static SourceEvent frequencyCorrectionChange(long frequencyCorrection)
{
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_CORRECTION_CHANGE, frequencyCorrection);
}
/**
* Creates a new channel frequency correction change event
*
* @param frequencyCorrection in hertz
*/
public static SourceEvent channelFrequencyCorrectionChange(long frequencyCorrection)
{
return new SourceEvent(Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, frequencyCorrection);
}
/**
* Creates a new sample rate change event
*
* @param sampleRate in hertz
*/
public static SourceEvent sampleRateChange(int sampleRate)
{
return new SourceEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE, sampleRate);
}
/**
* Creates a new channel sample rate change event
*
* @param sampleRate in hertz
*/
public static SourceEvent channelSampleRateChange(int sampleRate)
{
return new SourceEvent(Event.NOTIFICATION_CHANNEL_SAMPLE_RATE_CHANGE, sampleRate);
}
/**
* Creates a new channel count change event
*
* @param channelCount
*/
public static SourceEvent channelCountChange(int channelCount)
{
return new SourceEvent(Event.NOTIFICATION_POLYPHASE_CHANNEL_COUNT_CHANGE, channelCount);
}
/**
* Creates a new frequency change request event
*
* @param frequency requested
*/
public static SourceEvent frequencyRequest(long frequency)
{
return new SourceEvent(Event.REQUEST_FREQUENCY_CHANGE, frequency);
}
/**
* Creates a new channel frequency correction change request event
*
* @param frequency requested
*/
public static SourceEvent channelFrequencyCorrectionRequest(long frequency)
{
return new SourceEvent(Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, frequency);
}
/**
* Creates a new start sample stream request event
*/
public static SourceEvent startSampleStream(Source source)
{
return new SourceEvent(Event.REQUEST_START_SAMPLE_STREAM, source);
}
/**
* Creates a new stop sample stream request event
*/
public static SourceEvent stopSampleStream(Source source)
{
return new SourceEvent(Event.REQUEST_STOP_SAMPLE_STREAM, source);
}
}

View File

@@ -1,70 +1,70 @@
/*******************************************************************************
* 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/>
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source.config;
import javax.xml.bind.annotation.XmlAttribute;
import source.SourceType;
import source.tuner.TunerChannel;
import source.tuner.TunerChannel.Type;
import javax.xml.bind.annotation.XmlAttribute;
public class SourceConfigRecording extends SourceConfiguration
{
private String mRecordingAlias;
private long mFrequency;
public SourceConfigRecording()
private String mRecordingAlias;
private long mFrequency;
public SourceConfigRecording()
{
super( SourceType.RECORDING );
super(SourceType.RECORDING);
}
@XmlAttribute( name = "recording_alias" )
public String getRecordingAlias()
{
return mRecordingAlias;
}
public void setRecordingAlias( String alias )
{
mRecordingAlias = alias;
}
@XmlAttribute( name = "frequency" )
public long getFrequency()
{
return mFrequency;
}
public void setFrequency( long frequency )
{
mFrequency = frequency;
}
@XmlAttribute(name = "recording_alias")
public String getRecordingAlias()
{
return mRecordingAlias;
}
@Override
public void setRecordingAlias(String alias)
{
mRecordingAlias = alias;
}
@XmlAttribute(name = "frequency")
public long getFrequency()
{
return mFrequency;
}
public void setFrequency(long frequency)
{
mFrequency = frequency;
}
@Override
public String getDescription()
{
return getRecordingAlias();
return getRecordingAlias();
}
public TunerChannel getTunerChannel()
{
/* Create a tuner channel with a default bandwidth of 15 kHz */
return new TunerChannel( Type.LOCKED, mFrequency, 15000 );
}
public TunerChannel getTunerChannel()
{
/* Create a tuner channel with a default bandwidth of 12.5 kHz */
return new TunerChannel(mFrequency, 12500);
}
}

View File

@@ -1,69 +1,68 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 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/>
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source.config;
import java.text.DecimalFormat;
import javax.xml.bind.annotation.XmlAttribute;
import source.SourceType;
import source.tuner.TunerChannel;
import source.tuner.TunerChannel.Type;
import javax.xml.bind.annotation.XmlAttribute;
import java.text.DecimalFormat;
public class SourceConfigTuner extends SourceConfiguration
{
private static DecimalFormat sFORMAT = new DecimalFormat( "0.00000" );
private static DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.00000");
private long mFrequency = 0;
private int mBandwidth = 12500;
public SourceConfigTuner()
private long mFrequency = 0;
private int mBandwidth = 12500;
public SourceConfigTuner()
{
super( SourceType.TUNER );
super(SourceType.TUNER);
}
public SourceConfigTuner( TunerChannel tunerChannel )
{
this();
mFrequency = tunerChannel.getFrequency();
mBandwidth = tunerChannel.getBandwidth();
}
@XmlAttribute( name = "frequency" )
public long getFrequency()
{
return mFrequency;
}
public void setFrequency( long frequency )
{
mFrequency = frequency;
}
@Override
public SourceConfigTuner(TunerChannel tunerChannel)
{
this();
mFrequency = tunerChannel.getFrequency();
mBandwidth = tunerChannel.getBandwidth();
}
@XmlAttribute(name = "frequency")
public long getFrequency()
{
return mFrequency;
}
public void setFrequency(long frequency)
{
mFrequency = frequency;
}
@Override
public String getDescription()
{
return sFORMAT.format( (double)mFrequency / 1000000.0d ) + " MHz";
return FREQUENCY_FORMAT.format((double)mFrequency / 1000000.0d) + " MHz";
}
public TunerChannel getTunerChannel()
{
return new TunerChannel(mFrequency, mBandwidth);
}
public TunerChannel getTunerChannel()
{
return new TunerChannel( Type.LOCKED, mFrequency, mBandwidth );
}
}

View File

@@ -36,7 +36,7 @@ import sample.real.RealBuffer;
import source.RealSource;
import source.SourceException;
import dsp.gain.AutomaticGainControl;
import source.tuner.frequency.FrequencyChangeEvent;
import source.SourceEvent;
public class RealMixerSource extends RealSource
{
@@ -80,19 +80,19 @@ public class RealMixerSource extends RealSource
}
@Override
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
public void setSourceEventListener(Listener<SourceEvent> listener)
{
//Not implemented
}
@Override
public void removeFrequencyChangeListener()
public void removeSourceEventListener()
{
//Not implemented
}
@Override
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
public Listener<SourceEvent> getSourceEventListener()
{
//Not implemented
return null;

View File

@@ -9,12 +9,12 @@ import source.SourceException;
import source.tuner.ITunerChannelProvider;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Event;
import source.tuner.frequency.IFrequencyChangeProcessor;
import source.SourceEvent;
import source.SourceEvent.Event;
import source.ISourceEventProcessor;
public class Recording implements Comparable<Recording>,
IFrequencyChangeProcessor,
ISourceEventProcessor,
ITunerChannelProvider
{
private RecordingConfiguration mConfiguration;
@@ -89,7 +89,7 @@ public class Recording implements Comparable<Recording>,
}
@Override
public void frequencyChanged( FrequencyChangeEvent event ) throws SourceException
public void process(SourceEvent event ) throws SourceException
{
if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE )
{
@@ -103,7 +103,7 @@ public class Recording implements Comparable<Recording>,
for( TunerChannelSource channel: mTunerChannels )
{
channel.frequencyChanged( event );
channel.process( event );
}
}
}

View File

@@ -0,0 +1,169 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source.tuner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.ComplexSource;
import source.ISourceEventProcessor;
import source.SourceEvent;
import source.SourceException;
import java.util.concurrent.ScheduledExecutorService;
/**
* Complex source that provides a complex sample stream at the channel's required sample rate
*/
public abstract class ComplexChannelSource extends ComplexSource implements ISourceEventProcessor
{
private final static Logger mLog = LoggerFactory.getLogger(ComplexChannelSource.class);
private SourceEventProcessor mSourceEventProcessor = new SourceEventProcessor();
private Listener<SourceEvent> mSourceEventListener;
private Listener<ComplexBuffer> mComplexBufferListener;
private ISourceEventProcessor mUpstreamSourceEventProcessor;
public ComplexChannelSource(ISourceEventProcessor upstreamSourceEventProcessor)
{
mUpstreamSourceEventProcessor = upstreamSourceEventProcessor;
}
/**
* Sets the source event listener to receive source events from this source
*/
@Override
public void setSourceEventListener(Listener<SourceEvent> listener)
{
mSourceEventListener = listener;
}
/**
* Removes the source event listener from receiving source events from this source
*/
@Override
public void removeSourceEventListener()
{
mSourceEventListener = null;
}
/**
* Broadcasts the source event to the registered source event listener
*/
protected void broadcast(SourceEvent sourceEvent)
{
if(mSourceEventListener != null)
{
mSourceEventListener.receive(sourceEvent);
}
}
/**
* Registers the listener to receive complex sample buffers from this channel source
*/
@Override
public void setListener(Listener<ComplexBuffer> listener)
{
mComplexBufferListener = listener;
}
@Override
public void removeListener(Listener<ComplexBuffer> listener)
{
mComplexBufferListener = null;
}
/**
* Dispatches the processed complex buffer to the downstream consumer.
* @param complexBuffer containing complex samples
*/
protected void broadcast(ComplexBuffer complexBuffer)
{
if(mComplexBufferListener != null)
{
mComplexBufferListener.receive(complexBuffer);
}
}
@Override
public void start(ScheduledExecutorService executor)
{
//TODO: start a runnable to process downstream buffer dispatching
if(mUpstreamSourceEventProcessor != null)
{
try
{
mUpstreamSourceEventProcessor.process(SourceEvent.startSampleStream(ComplexChannelSource.this));
}
catch(SourceException se)
{
mLog.error("Error starting complex channel source");
}
}
}
@Override
public void stop()
{
if(mUpstreamSourceEventProcessor != null)
{
try
{
mUpstreamSourceEventProcessor.process(SourceEvent.stopSampleStream(ComplexChannelSource.this));
}
catch(SourceException se)
{
mLog.error("Error starting complex channel source");
}
}
//TODO: cancel a runnable to process downstream buffer dispatching
}
/**
* Source Event processor interface for this source to receive source events
*/
@Override
public Listener<SourceEvent> getSourceEventListener()
{
return mSourceEventProcessor;
}
/**
* Adapter for implementing ISourceEventProcessor and dispatching source events to subclasses via the
* ISourceEventProcessor.process() method.
*/
public class SourceEventProcessor implements Listener<SourceEvent>
{
@Override
public void receive(SourceEvent sourceEvent)
{
try
{
process(sourceEvent);
}
catch(SourceException se)
{
mLog.error("Error processing source event", se);
}
}
}
}

View File

@@ -1,213 +1,239 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 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/>
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source.tuner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.ISourceEventProcessor;
import source.SourceEvent;
import source.SourceException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.RejectedExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.IFrequencyChangeProcessor;
/**
* Tuner - provides tuner channel sources, representing a channel frequency
*/
public abstract class Tuner implements ITunerChannelProvider
{
private final static Logger mLog = LoggerFactory.getLogger( Tuner.class );
private String mName;
private TunerController mTunerController;
/**
* Sample Listeners - these will typically be the DFT processor for spectral
* display, or it will be one or more tuner channel sources
*/
protected List<Listener<ComplexBuffer>> mSampleListeners =
new CopyOnWriteArrayList<>();
protected List<Listener<TunerEvent>> mTunerChangeListeners =
new ArrayList<>();
public Tuner( String name, TunerController tunerController )
{
mName = name;
mTunerController = tunerController;
private final static Logger mLog = LoggerFactory.getLogger(Tuner.class);
//Rebroadcast frequency and sample rate change events as tuner events
mTunerController.addListener( new IFrequencyChangeProcessor()
{
@Override
public void frequencyChanged( FrequencyChangeEvent event )
{
switch( event.getEvent() )
{
case NOTIFICATION_FREQUENCY_CHANGE:
broadcast( new TunerEvent( Tuner.this,
source.tuner.TunerEvent.Event.FREQUENCY ));
break;
case NOTIFICATION_SAMPLE_RATE_CHANGE:
broadcast( new TunerEvent( Tuner.this,
source.tuner.TunerEvent.Event.SAMPLE_RATE ));
break;
default:
break;
}
}
} );
}
public TunerController getTunerController()
{
return mTunerController;
}
public String toString()
{
return mName;
}
public void dispose()
{
mSampleListeners.clear();
}
public void setName( String name )
{
mName = name;
}
/**
* Unique identifier for this tuner, used to lookup a tuner configuration
* from the settings manager.
*
* @return - unique identifier like a serial number, or a usb bus location
* or ip address and port. Return some form of unique identification that
* allows this tuner to be identified from among several of the same types
* of tuners.
*/
public abstract String getUniqueID();
private String mName;
private TunerController mTunerController;
/**
* Sample Listeners - these will typically be the DFT processor for spectral
* display, or it will be one or more tuner channel sources
*/
protected List<Listener<ComplexBuffer>> mSampleListeners = new CopyOnWriteArrayList<>();
protected List<Listener<TunerEvent>> mTunerChangeListeners = new ArrayList<>();
protected Broadcaster<SourceEvent> mSourceEventBroadcaster = new Broadcaster<>();
/**
* @return - tuner class enum entry
*/
public abstract TunerClass getTunerClass();
/**
* @return - tuner type enum entry
*/
public abstract TunerType getTunerType();
/**
* Name of this tuner object
* @return - string name of this tuner object
*/
public String getName()
{
return mName;
}
/**
* Sample size in bits
*/
public abstract double getSampleSize();
/**
* Returns a tuner frequency channel source, tuned to the correct frequency
*
* Note: ensure the concrete implementation registers the returned
* tuner channel source as a listener on the tuner, to receive frequency
* change events.
*
* @param frequency - desired frequency
*
* @return - source for 48k sample rate
*/
public abstract TunerChannelSource getChannel( TunerChannel channel )
throws RejectedExecutionException, SourceException;
/**
* Releases the tuned channel resources
*
* Note: ensure the concrete implementation unregisters the tuner channel
* source from the tuner as a frequency change listener
*
* @param channel - previously obtained tuner channel
*/
public abstract void releaseChannel( TunerChannelSource source );
/**
* Registers the listener to receive complex float sample arrays
*/
public void addListener( Listener<ComplexBuffer> listener )
public Tuner(String name, TunerController tunerController)
{
mSampleListeners.add( listener );
mName = name;
mTunerController = tunerController;
//Rebroadcast frequency and sample rate change events as tuner events
mTunerController.addListener(new ISourceEventProcessor()
{
@Override
public void process(SourceEvent event)
{
switch(event.getEvent())
{
case NOTIFICATION_FREQUENCY_CHANGE:
broadcast(new TunerEvent(Tuner.this,
source.tuner.TunerEvent.Event.FREQUENCY));
break;
case NOTIFICATION_SAMPLE_RATE_CHANGE:
broadcast(new TunerEvent(Tuner.this,
source.tuner.TunerEvent.Event.SAMPLE_RATE));
break;
default:
break;
}
}
});
}
/**
* Removes the registered listener
*/
public void removeListener( Listener<ComplexBuffer> listener )
public TunerController getTunerController()
{
mSampleListeners.remove( listener );
return mTunerController;
}
public String toString()
{
return mName;
}
public void dispose()
{
mSampleListeners.clear();
}
public void setName(String name)
{
mName = name;
}
/**
* Unique identifier for this tuner, used to lookup a tuner configuration
* from the settings manager.
*
* @return - unique identifier like a serial number, or a usb bus location
* or ip address and port. Return some form of unique identification that
* allows this tuner to be identified from among several of the same types
* of tuners.
*/
public abstract String getUniqueID();
/**
* @return - tuner class enum entry
*/
public abstract TunerClass getTunerClass();
/**
* @return - tuner type enum entry
*/
public abstract TunerType getTunerType();
/**
* Name of this tuner object
*
* @return - string name of this tuner object
*/
public String getName()
{
return mName;
}
/**
* Sample size in bits
*/
public abstract double getSampleSize();
/**
* Returns a tuner frequency channel source, tuned to the correct frequency
*
* Note: ensure the concrete implementation registers the returned
* tuner channel source as a listener on the tuner, to receive frequency
* change events.
*
* @param channel - desired frequency and bandwidth
* @return - source for 48k sample rate
*/
public abstract TunerChannelSource getChannel(TunerChannel channel)
throws RejectedExecutionException, SourceException;
/**
* Releases the tuned channel resources
*
* Note: ensure the concrete implementation unregisters the tuner channel
* source from the tuner as a frequency change listener
*
* @param source - previously obtained tuner channel
*/
public abstract void releaseChannel(TunerChannelSource source);
/**
* Registers the listener to receive complex float sample arrays
*/
public void addListener(Listener<ComplexBuffer> listener)
{
mSampleListeners.add(listener);
}
/**
* Removes the registered listener
*/
public void removeListener(Listener<ComplexBuffer> listener)
{
mSampleListeners.remove(listener);
}
/**
* Broadcasts the samples to all registered listeners
*/
public void broadcast( ComplexBuffer sampleBuffer )
public void broadcast(ComplexBuffer sampleBuffer)
{
for( Listener<ComplexBuffer> listener: mSampleListeners )
{
listener.receive( sampleBuffer );
}
for(Listener<ComplexBuffer> listener : mSampleListeners)
{
listener.receive(sampleBuffer);
}
}
/**
* Registers the listener
*/
public void addTunerChangeListener( Listener<TunerEvent> listener )
/**
* Registers the listener
*/
public void addTunerChangeListener(Listener<TunerEvent> listener)
{
mTunerChangeListeners.add( listener );
mTunerChangeListeners.add(listener);
}
/**
* Removes the registered listener
*/
public void removeTunerChangeListener( Listener<TunerEvent> listener )
/**
* Removes the registered listener
*/
public void removeTunerChangeListener(Listener<TunerEvent> listener)
{
mTunerChangeListeners.remove( listener );
mTunerChangeListeners.remove(listener);
}
/**
* Broadcasts the tuner change event
*/
public void broadcast( TunerEvent event )
public void broadcast(TunerEvent tunerEvent)
{
for( Listener<TunerEvent> listener: mTunerChangeListeners )
{
listener.receive( event );
}
for(Listener<TunerEvent> listener : mTunerChangeListeners)
{
listener.receive(tunerEvent);
}
//Re-broadcast frequency and sample rate change events as source events
switch(tunerEvent.getEvent())
{
case FREQUENCY:
mSourceEventBroadcaster.broadcast(SourceEvent.frequencyChange(getTunerController().getFrequency()));
break;
case SAMPLE_RATE:
mSourceEventBroadcaster.broadcast(SourceEvent.sampleRateChange(getTunerController().getSampleRate()));
break;
}
}
/**
* Adds the listener to receive source events
*/
public void addSourceEventListener(Listener<SourceEvent> listener)
{
mSourceEventBroadcaster.addListener(listener);
}
/**
* Removes the listener from receiving source events
*/
public void removeSourceEventListener(Listener<SourceEvent> listener)
{
mSourceEventBroadcaster.removeListener(listener);
}
}

View File

@@ -1,122 +1,93 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 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/>
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source.tuner;
public class TunerChannel implements Comparable<TunerChannel>
{
private Type mType;
private long mFrequency;
private int mBandwidth;
public TunerChannel( Type type, long frequency, int bandwidth )
{
mType = type;
mFrequency = frequency;
mBandwidth = bandwidth;
}
public Type getType()
{
return mType;
}
public void setType( Type type )
{
mType = type;
}
private long mFrequency;
private int mBandwidth;
public long getFrequency()
{
return mFrequency;
}
public void setFrequency( long frequency )
{
mFrequency = frequency;
}
public int getBandwidth()
{
return mBandwidth;
}
public void setBandwidth( int bandwidth )
{
mBandwidth = bandwidth;
}
public long getMinFrequency()
{
return mFrequency - ( mBandwidth / 2 );
}
public long getMaxFrequency()
{
return mFrequency + ( mBandwidth / 2 );
}
/**
* Indicates if any part of this tuner channel overlaps the specified range
*/
public boolean overlaps( long minimum, long maximum )
{
long channelMin = getMinFrequency();
long channelMax = getMaxFrequency();
return ( ( minimum <= channelMin && channelMin <= maximum ) ||
( minimum <= channelMax && channelMax <= maximum ) ||
( minimum <= channelMin && channelMax <= maximum ) ||
( channelMin <= minimum && maximum <= channelMax ) );
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( "Channel: " );
sb.append( getMinFrequency() );
sb.append( "-" );
sb.append( getMaxFrequency() );
return sb.toString();
}
@Override
public int compareTo( TunerChannel otherLock )
public TunerChannel(long frequency, int bandwidth)
{
if( this.equals( otherLock ) )
{
return 0;
}
else if( mFrequency < otherLock.getFrequency() )
{
return -1;
}
else
{
return 1;
}
mFrequency = frequency;
mBandwidth = bandwidth;
}
public long getFrequency()
{
return mFrequency;
}
public void setFrequency(long frequency)
{
mFrequency = frequency;
}
public int getBandwidth()
{
return mBandwidth;
}
public void setBandwidth(int bandwidth)
{
mBandwidth = bandwidth;
}
public long getMinFrequency()
{
return mFrequency - (mBandwidth / 2);
}
public long getMaxFrequency()
{
return mFrequency + (mBandwidth / 2);
}
/**
* Indicates if any part of this tuner channel overlaps the specified range
*/
public boolean overlaps(long minimum, long maximum)
{
long channelMin = getMinFrequency();
long channelMax = getMaxFrequency();
return ((minimum <= channelMin && channelMin <= maximum) ||
(minimum <= channelMax && channelMax <= maximum) ||
(minimum <= channelMin && channelMax <= maximum) ||
(channelMin <= minimum && maximum <= channelMax));
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("Channel: ");
sb.append(getMinFrequency());
sb.append("-");
sb.append(getMaxFrequency());
return sb.toString();
}
@Override
public int compareTo(TunerChannel otherLock)
{
return Long.compare(getFrequency(), otherLock.getFrequency());
}
public enum Type
{
LOCKED, //Use for decoding channels, so that they cannot be tuned out of range
TRAFFIC, //Like locked, but for temporary allocated traffic channels
TUNABLE; //Tunable -- can be tuned out of range
}
}

View File

@@ -32,11 +32,10 @@ import sample.complex.ComplexBuffer;
import sample.real.IOverflowListener;
import source.ComplexSource;
import source.SourceException;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Event;
import source.tuner.frequency.IFrequencyChangeListener;
import source.tuner.frequency.IFrequencyChangeProcessor;
import source.tuner.frequency.IFrequencyChangeProvider;
import source.ISourceEventListener;
import source.ISourceEventProvider;
import source.SourceEvent;
import source.ISourceEventProcessor;
import java.util.ArrayList;
import java.util.List;
@@ -46,7 +45,7 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class TunerChannelSource extends ComplexSource implements IFrequencyChangeProcessor, Listener<ComplexBuffer>
public class TunerChannelSource extends ComplexSource implements ISourceEventProcessor, Listener<ComplexBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(TunerChannelSource.class);
@@ -66,7 +65,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
private Oscillator mMixer;
private ComplexPrimeCICDecimate mDecimationFilter;
private Listener<ComplexBuffer> mListener;
private IFrequencyChangeProcessor mFrequencyChangeProcessor;
private ISourceEventProcessor mFrequencyChangeProcessor;
private DownstreamProcessor mDownstreamFrequencyEventProcessor = new DownstreamProcessor();
private ScheduledFuture<?> mTaskHandle;
@@ -100,7 +99,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
{
mTuner = tuner;
mTunerChannel = tunerChannel;
mTuner.getTunerController().addListener((IFrequencyChangeProcessor) this);
mTuner.getTunerController().addListener((ISourceEventProcessor) this);
mTunerFrequency = mTuner.getTunerController().getFrequency();
mBuffer = new OverflowableTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD);
@@ -111,8 +110,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
mMixer = new Oscillator(frequencyOffset, mTuner.getTunerController().getSampleRate());
/* Fire a sample rate change event to setup the decimation chain */
frequencyChanged(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE,
mTuner.getTunerController().getSampleRate()));
process(SourceEvent.sampleRateChange(mTuner.getTunerController().getSampleRate()));
}
/**
@@ -202,7 +200,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
updateMixerFrequencyOffset();
mDownstreamFrequencyEventProcessor.broadcast(
new FrequencyChangeEvent( Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, mChannelFrequencyCorrection));
SourceEvent.channelFrequencyCorrectionChange(mChannelFrequencyCorrection));
}
public Tuner getTuner()
@@ -224,7 +222,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
}
}
public void setFrequencyChangeListener(IFrequencyChangeProcessor processor)
public void setFrequencyChangeListener(ISourceEventProcessor processor)
{
mFrequencyChangeProcessor = processor;
}
@@ -250,12 +248,12 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
* frequency correction events received from the channel consumer/listener
*/
@Override
public void frequencyChanged(FrequencyChangeEvent event) throws SourceException
public void process(SourceEvent event) throws SourceException
{
// Echo the event to the registered event listener
if(mFrequencyChangeProcessor != null)
{
mFrequencyChangeProcessor.frequencyChanged(event);
mFrequencyChangeProcessor.process(event);
}
switch(event.getEvent())
@@ -320,36 +318,36 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
}
/**
* Implements IFrequencyChangeProvider to enable this source to broadcast frequency change events to downstream
* Implements ISourceEventProvider to enable this source to broadcast frequency change events to downstream
* listeners.
*
* @param listener to receive downstream events
*/
@Override
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
public void setSourceEventListener(Listener<SourceEvent> listener)
{
mDownstreamFrequencyEventProcessor.setFrequencyChangeListener(listener);
mDownstreamFrequencyEventProcessor.setSourceEventListener(listener);
}
/**
* Implements IFrequencyChangeProvider to remove the frequency change listener from receiving down-stream frequency
* Implements ISourceEventProvider to remove the frequency change listener from receiving down-stream frequency
* change events.
*/
@Override
public void removeFrequencyChangeListener()
public void removeSourceEventListener()
{
mDownstreamFrequencyEventProcessor.removeFrequencyChangeListener();
mDownstreamFrequencyEventProcessor.removeSourceEventListener();
}
/**
* Implements IFrequencyChangeListener to receive frequency change events containing requests from downstream
* Implements ISourceEventListener to receive frequency change events containing requests from downstream
* listeners to change frequency values.
* @return listener
*/
@Override
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
public Listener<SourceEvent> getSourceEventListener()
{
return mDownstreamFrequencyEventProcessor.getFrequencyChangeListener();
return mDownstreamFrequencyEventProcessor.getSourceEventListener();
}
/**
@@ -358,17 +356,17 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
* notified of any frequency or sample rate change events and will also be able to request frequency correction
* updates.
*/
public class DownstreamProcessor implements IFrequencyChangeListener, IFrequencyChangeProvider,
Listener<FrequencyChangeEvent>
public class DownstreamProcessor implements ISourceEventListener, ISourceEventProvider,
Listener<SourceEvent>
{
//Listener to receive downstream events
private Listener<FrequencyChangeEvent> mListener;
private Listener<SourceEvent> mListener;
/**
* Broadcasts the frequency change event to the downstream frequency change listener
* @param event to broadcast
*/
public void broadcast(FrequencyChangeEvent event)
public void broadcast(SourceEvent event)
{
if(mListener != null)
{
@@ -384,7 +382,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
try
{
long frequency = getFrequency();
broadcast(new FrequencyChangeEvent(Event.NOTIFICATION_FREQUENCY_CHANGE, frequency));
broadcast(SourceEvent.frequencyChange(frequency));
}
catch(SourceException se)
{
@@ -400,7 +398,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
try
{
//Note: downstream sample rate is currently a fixed value -- it will change in the future
broadcast(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE, getSampleRate()));
broadcast(SourceEvent.sampleRateChange(getSampleRate()));
}
catch(SourceException se)
{
@@ -413,7 +411,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
* @param listener to receive events
*/
@Override
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
public void setSourceEventListener(Listener<SourceEvent> listener)
{
mListener = listener;
}
@@ -422,7 +420,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
* Removes the downstream listener from receiving frequency change events.
*/
@Override
public void removeFrequencyChangeListener()
public void removeSourceEventListener()
{
mListener = null;
}
@@ -431,7 +429,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
* Listener for receiving frequency change events from downstream components
*/
@Override
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
public Listener<SourceEvent> getSourceEventListener()
{
return this;
}
@@ -441,7 +439,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
* @param event to process
*/
@Override
public void receive(FrequencyChangeEvent event)
public void receive(SourceEvent event)
{
switch(event.getEvent())
{

View File

@@ -26,13 +26,13 @@ import org.slf4j.LoggerFactory;
import source.SourceException;
import source.tuner.configuration.TunerConfiguration;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Event;
import source.SourceEvent;
import source.SourceEvent.Event;
import source.tuner.frequency.FrequencyController;
import source.tuner.frequency.FrequencyController.Tunable;
import source.tuner.frequency.IFrequencyChangeProcessor;
import source.ISourceEventProcessor;
public abstract class TunerController implements Tunable, IFrequencyChangeProcessor
public abstract class TunerController implements Tunable, ISourceEventProcessor
{
private final static Logger mLog =
LoggerFactory.getLogger( TunerController.class );
@@ -79,7 +79,7 @@ public abstract class TunerController implements Tunable, IFrequencyChangeProces
* Responds to requests to set the frequency
*/
@Override
public void frequencyChanged( FrequencyChangeEvent event ) throws SourceException
public void process(SourceEvent event ) throws SourceException
{
if( event.getEvent() == Event.REQUEST_FREQUENCY_CHANGE )
{
@@ -381,7 +381,7 @@ public abstract class TunerController implements Tunable, IFrequencyChangeProces
* Note: this is normally used by the Tuner. Any additional listeners can
* be registered on the tuner.
*/
public void addListener( IFrequencyChangeProcessor processor )
public void addListener( ISourceEventProcessor processor )
{
mFrequencyController.addListener( processor );
}
@@ -389,7 +389,7 @@ public abstract class TunerController implements Tunable, IFrequencyChangeProces
/**
* Removes the frequency change listener
*/
public void removeListener( IFrequencyChangeProcessor processor )
public void removeListener( ISourceEventProcessor processor )
{
mFrequencyController.removeFrequencyChangeProcessor( processor );
}

View File

@@ -22,8 +22,8 @@ import gui.editor.Editor;
import net.miginfocom.swing.MigLayout;
import source.config.SourceConfigTuner;
import source.config.SourceConfiguration;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.IFrequencyChangeProcessor;
import source.SourceEvent;
import source.ISourceEventProcessor;
import controller.channel.Channel;
public class TunerSourceEditor extends Editor<Channel>
@@ -41,10 +41,10 @@ public class TunerSourceEditor extends Editor<Channel>
setLayout( new MigLayout( "insets 0 0 0 0", "[left]", "" ) );
mFrequencyControl = new JFrequencyControl();
mFrequencyControl.setEnabled( false );
mFrequencyControl.addListener( new IFrequencyChangeProcessor()
mFrequencyControl.addListener( new ISourceEventProcessor()
{
@Override
public void frequencyChanged( FrequencyChangeEvent event )
public void process(SourceEvent event )
{
setModified( true );
}

View File

@@ -1,128 +0,0 @@
package source.tuner.frequency;
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2016 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/>
******************************************************************************/
import java.util.concurrent.ScheduledExecutorService;
import module.Module;
import sample.Listener;
import sample.real.RealBuffer;
import source.tuner.frequency.FrequencyChangeEvent.Event;
public class DCTrackingFrequencyControl extends Module
implements Listener<RealBuffer>, IFrequencyChangeProvider
{
private static float COARSE_THRESHOLD = 0.1f;
private static float LOOP_GAIN = 0.1f;
private static float COARSE_GAIN = 1000.0f;
private static float FINE_GAIN = 100.0f;
private int mErrorCorrection = 0;
private int mMaximumFrequencyCorrection = 0;
private Listener<FrequencyChangeEvent> mListener;
/**
* DC offset Automatic Frequency Control. Monitors DC bias present in an FM
* demodulated audio stream and issues automatic frequency corrections to
* maintain the DC bias close to zero.
*
* @param maximum - maximum allowable (+/-) frequency correction value.
*/
public DCTrackingFrequencyControl( int maximum )
{
mMaximumFrequencyCorrection = maximum;
}
public void dispose()
{
mListener = null;
}
@Override
public void receive( RealBuffer buffer )
{
double sum = 0.0d;
for( float sample: buffer.getSamples() )
{
sum += sample;
}
float mean = (float)( sum / (double)buffer.getSamples().length );
float adjustment = 0.0f;
if( mean > COARSE_THRESHOLD || mean < - COARSE_THRESHOLD )
{
adjustment = mean * COARSE_GAIN;
}
else
{
adjustment = mean * FINE_GAIN;
}
setErrorCorrection( mErrorCorrection + (int)( (float)adjustment * LOOP_GAIN ) );
}
private void setErrorCorrection( int correction )
{
if( mListener != null )
{
if( correction > mMaximumFrequencyCorrection )
{
correction = mMaximumFrequencyCorrection;
}
else if( correction < -mMaximumFrequencyCorrection )
{
correction = -mMaximumFrequencyCorrection;
}
if( correction != mErrorCorrection )
{
mListener.receive( new FrequencyChangeEvent(
Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, correction ) );
}
}
}
@Override
public void setFrequencyChangeListener( Listener<FrequencyChangeEvent> listener )
{
mListener = listener;
}
@Override
public void removeFrequencyChangeListener()
{
mListener = null;
}
@Override
public void start( ScheduledExecutorService executor )
{
}
@Override
public void stop()
{
}
@Override
public void reset()
{
setErrorCorrection( 0 );
}
}

View File

@@ -0,0 +1,130 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source.tuner.frequency;
import module.Module;
import sample.Listener;
import sample.real.RealBuffer;
import source.ISourceEventProvider;
import source.SourceEvent;
import java.util.concurrent.ScheduledExecutorService;
public class DCTrackingSourceControl extends Module
implements Listener<RealBuffer>, ISourceEventProvider
{
private static float COARSE_THRESHOLD = 0.1f;
private static float LOOP_GAIN = 0.1f;
private static float COARSE_GAIN = 1000.0f;
private static float FINE_GAIN = 100.0f;
private int mErrorCorrection = 0;
private int mMaximumFrequencyCorrection = 0;
private Listener<SourceEvent> mListener;
/**
* DC offset Automatic Frequency Control. Monitors DC bias present in an FM
* demodulated audio stream and issues automatic frequency corrections to
* maintain the DC bias close to zero.
*
* @param maximum - maximum allowable (+/-) frequency correction value.
*/
public DCTrackingSourceControl(int maximum)
{
mMaximumFrequencyCorrection = maximum;
}
public void dispose()
{
mListener = null;
}
@Override
public void receive(RealBuffer buffer)
{
double sum = 0.0d;
for(float sample : buffer.getSamples())
{
sum += sample;
}
float mean = (float)(sum / (double)buffer.getSamples().length);
float adjustment = 0.0f;
if(mean > COARSE_THRESHOLD || mean < -COARSE_THRESHOLD)
{
adjustment = mean * COARSE_GAIN;
}
else
{
adjustment = mean * FINE_GAIN;
}
setErrorCorrection(mErrorCorrection + (int)((float)adjustment * LOOP_GAIN));
}
private void setErrorCorrection(int correction)
{
if(mListener != null)
{
if(correction > mMaximumFrequencyCorrection)
{
correction = mMaximumFrequencyCorrection;
}
else if(correction < -mMaximumFrequencyCorrection)
{
correction = -mMaximumFrequencyCorrection;
}
if(correction != mErrorCorrection)
{
mListener.receive(SourceEvent.channelFrequencyCorrectionRequest(correction));
}
}
}
@Override
public void setSourceEventListener(Listener<SourceEvent> listener)
{
mListener = listener;
}
@Override
public void removeSourceEventListener()
{
mListener = null;
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
}
@Override
public void reset()
{
setErrorCorrection(0);
}
}

View File

@@ -1,34 +0,0 @@
package source.tuner.frequency;
public class FrequencyChangeEvent
{
public enum Event
{
NOTIFICATION_FREQUENCY_CHANGE,
NOTIFICATION_FREQUENCY_CORRECTION_CHANGE,
NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
NOTIFICATION_SAMPLE_RATE_CHANGE,
REQUEST_FREQUENCY_CHANGE,
REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE;
}
private Event mEvent;
private Number mValue;
public FrequencyChangeEvent( Event event, Number value )
{
mEvent = event;
mValue = value;
}
public Event getEvent()
{
return mEvent;
}
public Number getValue()
{
return mValue;
}
}

View File

@@ -1,45 +0,0 @@
package source.tuner.frequency;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import source.SourceException;
import source.tuner.hackrf.HackRFTunerEditor;
public class FrequencyChangeProcessorWrapper
implements IFrequencyChangeListener, Listener<FrequencyChangeEvent>
{
private final static Logger mLog = LoggerFactory.getLogger( FrequencyChangeProcessorWrapper.class );
private IFrequencyChangeProcessor mProcessor;
/**
* Provides IFrequencyChangeListener wrapper for an IFrequencyChangeProcessor
*/
public FrequencyChangeProcessorWrapper( IFrequencyChangeProcessor handler )
{
mProcessor = handler;
}
@Override
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
{
return this;
}
@Override
public void receive( FrequencyChangeEvent event )
{
if( mProcessor != null )
{
try
{
mProcessor.frequencyChanged( event );
}
catch( SourceException se )
{
mLog.error("Error sending frequency change event to embedded processor", se);
}
}
}
}

View File

@@ -1,257 +1,253 @@
/*******************************************************************************
* 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/>
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source.tuner.frequency;
import java.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import source.ISourceEventProcessor;
import source.InvalidFrequencyException;
import source.Source;
import source.SourceEvent;
import source.SourceException;
import source.tuner.frequency.FrequencyChangeEvent.Event;
import java.util.ArrayList;
public class FrequencyController
{
private final static Logger mLog = LoggerFactory.getLogger( FrequencyController.class );
private final static Logger mLog = LoggerFactory.getLogger(FrequencyController.class);
private Tunable mTunable;
private long mFrequency = 101100000;
private long mTunedFrequency = 101100000;
private long mMinimumFrequency;
private long mMaximumFrequency;
private double mFrequencyCorrection = 0d;
private int mSampleRate = 0;
private ArrayList<IFrequencyChangeProcessor> mProcessors = new ArrayList<>();
public FrequencyController( Tunable tunable,
long minFrequency,
long maxFrequency,
double frequencyCorrection )
{
mTunable = tunable;
mMinimumFrequency = minFrequency;
mMaximumFrequency = maxFrequency;
mFrequencyCorrection = frequencyCorrection;
}
private Tunable mTunable;
private long mFrequency = 101100000;
private long mTunedFrequency = 101100000;
private long mMinimumFrequency;
private long mMaximumFrequency;
private double mFrequencyCorrection = 0d;
private int mSampleRate = 0;
/**
* Get bandwidth/sample rate in hertz
*/
public int getBandwidth()
{
return mSampleRate;
}
private ArrayList<ISourceEventProcessor> mProcessors = new ArrayList<>();
/**
* Set sample rate in hertz
*/
public void setSampleRate( int sampleRate ) throws SourceException
{
mSampleRate = sampleRate;
broadcastSampleRateChange();
}
public FrequencyController(Tunable tunable, long minFrequency, long maxFrequency, double frequencyCorrection)
{
mTunable = tunable;
mMinimumFrequency = minFrequency;
mMaximumFrequency = maxFrequency;
mFrequencyCorrection = frequencyCorrection;
}
/**
* Get frequency in hertz
*/
public long getFrequency()
{
return mFrequency;
}
/**
* Get bandwidth/sample rate in hertz
*/
public int getBandwidth()
{
return mSampleRate;
}
/**
* Set frequency
* @param frequency in hertz
* @throws SourceException if tunable doesn't support tuning a corrected
* version of the requested frequency
*/
public void setFrequency( long frequency ) throws SourceException
{
setFrequency( frequency, true );
}
/**
* Set sample rate in hertz
*/
public void setSampleRate(int sampleRate) throws SourceException
{
mSampleRate = sampleRate;
/**
* Set frequency with optional broadcast of frequency change event. This
* method supports changing the frequency correction value without
* broadcasting a frequency change event.
*/
private void setFrequency( long frequency, boolean broadcastChange ) throws SourceException
{
long tunedFrequency = getTunedFrequency( frequency );
if( tunedFrequency < mMinimumFrequency )
{
throw new InvalidFrequencyException( "Requested frequency not valid", frequency, mMinimumFrequency );
}
broadcastSampleRateChange();
}
if( tunedFrequency > mMaximumFrequency )
{
throw new InvalidFrequencyException( "Requested frequency not valid", frequency, mMaximumFrequency );
}
/**
* Get frequency in hertz
*/
public long getFrequency()
{
return mFrequency;
}
mFrequency = frequency;
/**
* Set frequency
*
* @param frequency in hertz
* @throws SourceException if tunable doesn't support tuning a corrected
* version of the requested frequency
*/
public void setFrequency(long frequency) throws SourceException
{
setFrequency(frequency, true);
}
mTunedFrequency = tunedFrequency;
/**
* Set frequency with optional broadcast of frequency change event. This
* method supports changing the frequency correction value without
* broadcasting a frequency change event.
*/
private void setFrequency(long frequency, boolean broadcastChange) throws SourceException
{
long tunedFrequency = getTunedFrequency(frequency);
if( mTunable != null )
{
mTunable.setTunedFrequency( mTunedFrequency );
}
if(tunedFrequency < mMinimumFrequency)
{
throw new InvalidFrequencyException("Requested frequency not valid", frequency, mMinimumFrequency);
}
if(tunedFrequency > mMaximumFrequency)
{
throw new InvalidFrequencyException("Requested frequency not valid", frequency, mMaximumFrequency);
}
mFrequency = frequency;
mTunedFrequency = tunedFrequency;
if(mTunable != null)
{
mTunable.setTunedFrequency(mTunedFrequency);
}
/* Broadcast to all listeners that the frequency has changed */
if( broadcastChange )
{
broadcastFrequencyChange();
}
}
public long getTunedFrequency()
{
return mTunedFrequency;
}
public long getMinimumFrequency()
{
return mMinimumFrequency;
}
public long getMaximumFrequency()
{
return mMaximumFrequency;
}
if(broadcastChange)
{
broadcastFrequencyChange();
}
}
public long getTunedFrequency()
{
return mTunedFrequency;
}
public long getMinimumFrequency()
{
return mMinimumFrequency;
}
public long getMaximumFrequency()
{
return mMaximumFrequency;
}
/**
* Calculate the tuned frequency by adding frequency correction to the
* corrected frequency.
*
* @param correctedFrequency
*/
private long getTunedFrequency( long correctedFrequency )
private long getTunedFrequency(long correctedFrequency)
{
return (long)( (double)correctedFrequency /
( 1.0 + ( mFrequencyCorrection / 1000000.0 ) ) );
return (long)((double)correctedFrequency /
(1.0 + (mFrequencyCorrection / 1000000.0)));
}
/**
* Calculate the corrected frequency by subtracting frequency correction
* Calculate the corrected frequency by subtracting frequency correction
* from the tuned frequency.
*
* @param tunedFrequency
*/
@SuppressWarnings("unused")
private long getCorrectedFrequency( long tunedFrequency )
private long getCorrectedFrequency(long tunedFrequency)
{
return (long)( (double)tunedFrequency /
( 1.0 - ( mFrequencyCorrection / 1000000.0 ) ) );
return (long)((double)tunedFrequency /
(1.0 - (mFrequencyCorrection / 1000000.0)));
}
public double getFrequencyCorrection()
{
return mFrequencyCorrection;
return mFrequencyCorrection;
}
public void setFrequencyCorrection( double correction ) throws SourceException
public void setFrequencyCorrection(double correction) throws SourceException
{
mFrequencyCorrection = correction;
mFrequencyCorrection = correction;
if( mFrequency > 0 )
{
setFrequency( mFrequency, true );
}
if(mFrequency > 0)
{
setFrequency(mFrequency, true);
}
broadcastFrequencyErrorChange();
broadcastFrequencyCorrectionChange();
}
/**
* Adds listener to receive frequency change events
*/
//TODO: rename this to addFrequencyChangeProcessor()
public void addListener( IFrequencyChangeProcessor processor )
{
if( !mProcessors.contains( processor ) )
{
mProcessors.add( processor );
}
}
/**
* Removes listener from receiving frequency change events
*/
public void removeFrequencyChangeProcessor( IFrequencyChangeProcessor processor )
{
mProcessors.remove( processor );
}
/**
* Broadcasts a change/update to the current (uncorrected) frequency or the
* Adds listener to receive frequency change events
*/
public void addListener(ISourceEventProcessor processor)
{
if(!mProcessors.contains(processor))
{
mProcessors.add(processor);
}
}
/**
* Removes listener from receiving frequency change events
*/
public void removeFrequencyChangeProcessor(ISourceEventProcessor processor)
{
mProcessors.remove(processor);
}
/**
* Broadcasts a change/update to the current (uncorrected) frequency or the
* bandwidth/sample rate value.
*/
protected void broadcastFrequencyChange() throws SourceException
{
broadcastFrequencyChangeEvent(
new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CHANGE, mFrequency ) );
broadcast(SourceEvent.frequencyChange(mFrequency));
}
/**
* Broadcast a frequency error/correction value change
*/
protected void broadcastFrequencyErrorChange() throws SourceException
protected void broadcastFrequencyCorrectionChange() throws SourceException
{
broadcastFrequencyChangeEvent(
new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CORRECTION_CHANGE, mFrequencyCorrection ) );
broadcast(SourceEvent.frequencyCorrectionChange((int)mFrequencyCorrection));
}
/**
* Broadcasts a sample rate change
*/
protected void broadcastSampleRateChange() throws SourceException
{
broadcastFrequencyChangeEvent(
new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mSampleRate ) );
broadcast(SourceEvent.sampleRateChange(mSampleRate));
}
public void broadcastFrequencyChangeEvent( FrequencyChangeEvent event ) throws SourceException
public void broadcast(SourceEvent event) throws SourceException
{
for( IFrequencyChangeProcessor processor: mProcessors )
{
processor.frequencyChanged( event );
}
for(ISourceEventProcessor processor : mProcessors)
{
processor.process(event);
}
}
public interface Tunable
{
/**
* Gets the tuned frequency of the device
*/
public long getTunedFrequency() throws SourceException;
/**
* Sets the tuned frequency of the device
*/
public void setTunedFrequency( long frequency ) throws SourceException;
/**
* Gets the current bandwidth setting of the device
*/
public int getCurrentSampleRate() throws SourceException;
}
public interface Tunable
{
/**
* Gets the tuned frequency of the device
*/
public long getTunedFrequency() throws SourceException;
/**
* Sets the tuned frequency of the device
*/
public void setTunedFrequency(long frequency) throws SourceException;
/**
* Gets the current bandwidth setting of the device
*/
public int getCurrentSampleRate() throws SourceException;
}
}

View File

@@ -1,34 +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 source.tuner.frequency;
import sample.Listener;
/**
* Interface for broadcasting changes to frequency, bandwidth, sample rate,
* and actual sample rate values to all registered listeners.
*
* Note: broadcasted frequency should be the uncorrected, or displayable
* frequency value. All frequency correction aspects should be handled within
* the device that implements the tuning of the frequency.
*/
public interface IFrequencyChangeListener
{
public Listener<FrequencyChangeEvent> getFrequencyChangeListener();
}

View File

@@ -1,8 +0,0 @@
package source.tuner.frequency;
import source.SourceException;
public interface IFrequencyChangeProcessor
{
public void frequencyChanged( FrequencyChangeEvent event ) throws SourceException;
}

View File

@@ -37,7 +37,7 @@ import sample.complex.ComplexBuffer;
import source.ComplexSource;
import source.IControllableFileSource;
import source.IFrameLocationListener;
import source.tuner.frequency.FrequencyChangeEvent;
import source.SourceEvent;
public class ComplexWaveSource extends ComplexSource implements IControllableFileSource
{
@@ -58,19 +58,19 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil
}
@Override
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
public void setSourceEventListener(Listener<SourceEvent> listener)
{
//Not implemented
}
@Override
public void removeFrequencyChangeListener()
public void removeSourceEventListener()
{
//Not implemented
}
@Override
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
public Listener<SourceEvent> getSourceEventListener()
{
//Not implemented
return null;

View File

@@ -36,7 +36,7 @@ import sample.real.RealBuffer;
import source.IControllableFileSource;
import source.IFrameLocationListener;
import source.RealSource;
import source.tuner.frequency.FrequencyChangeEvent;
import source.SourceEvent;
public class RealWaveSource extends RealSource
implements IControllableFileSource, AutoCloseable
@@ -58,19 +58,19 @@ public class RealWaveSource extends RealSource
}
@Override
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
public void setSourceEventListener(Listener<SourceEvent> listener)
{
//Not implemented
}
@Override
public void removeFrequencyChangeListener()
public void removeSourceEventListener()
{
//Not implemented
}
@Override
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
public Listener<SourceEvent> getSourceEventListener()
{
//Not implemented
return null;

View File

@@ -26,8 +26,8 @@ import settings.ColorSettingMenuItem;
import settings.Setting;
import settings.SettingChangeListener;
import settings.SettingsManager;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Event;
import source.SourceEvent;
import source.SourceEvent.Event;
import spectrum.converter.DFTResultsConverter;
import spectrum.converter.RealDecibelConverter;
import spectrum.menu.AveragingItem;
@@ -90,8 +90,7 @@ public class ChannelSpectrumPanel extends JPanel
mDFTConverter.addListener( mSpectrumPanel );
/* Set the DFTProcessor to the decimated 24kHz sample rate */
mDFTProcessor.frequencyChanged(
new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, 24000 ) );
mDFTProcessor.process(SourceEvent.sampleRateChange(24000));
initGui();
}

View File

@@ -28,8 +28,8 @@ import sample.Buffer;
import sample.Listener;
import sample.SampleType;
import sample.complex.ComplexBuffer;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.IFrequencyChangeProcessor;
import source.SourceEvent;
import source.ISourceEventProcessor;
import spectrum.converter.DFTResultsConverter;
import util.ThreadPool;
@@ -46,7 +46,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* 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<ComplexBuffer>, IFrequencyChangeProcessor, IDFTWidthChangeProcessor
public class DFTProcessor implements Listener<ComplexBuffer>, ISourceEventProcessor, IDFTWidthChangeProcessor
{
private final static Logger mLog = LoggerFactory.getLogger(DFTProcessor.class);
@@ -452,7 +452,7 @@ public class DFTProcessor implements Listener<ComplexBuffer>, IFrequencyChangePr
}
@Override
public void frequencyChanged(FrequencyChangeEvent event)
public void process(SourceEvent event)
{
switch(event.getEvent())
{

View File

@@ -31,8 +31,8 @@ import settings.Setting;
import settings.SettingChangeListener;
import settings.SettingsManager;
import source.tuner.TunerChannel;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.IFrequencyChangeProcessor;
import source.SourceEvent;
import source.ISourceEventProcessor;
import javax.swing.*;
import java.awt.*;
@@ -44,7 +44,7 @@ import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
public class OverlayPanel extends JPanel implements ChannelEventListener, IFrequencyChangeProcessor,
public class OverlayPanel extends JPanel implements ChannelEventListener, ISourceEventProcessor,
SettingChangeListener
{
private static final long serialVersionUID = 1L;
@@ -635,7 +635,7 @@ public class OverlayPanel extends JPanel implements ChannelEventListener, IFrequ
* Frequency change event handler
*/
@Override
public void frequencyChanged(FrequencyChangeEvent event)
public void process(SourceEvent event)
{
switch(event.getEvent())
{

View File

@@ -36,10 +36,9 @@ import sample.complex.ComplexBuffer;
import settings.ColorSetting.ColorSettingName;
import settings.ColorSettingMenuItem;
import settings.SettingsManager;
import source.ISourceEventProcessor;
import source.SourceEvent;
import source.tuner.Tuner;
import source.tuner.frequency.FrequencyChangeEvent;
import source.tuner.frequency.FrequencyChangeEvent.Event;
import source.tuner.frequency.IFrequencyChangeProcessor;
import spectrum.OverlayPanel.ChannelDisplay;
import spectrum.converter.ComplexDecibelConverter;
import spectrum.converter.DFTResultsConverter;
@@ -65,7 +64,7 @@ import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Hashtable;
public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuffer>, IFrequencyChangeProcessor, IDFTWidthChangeProcessor
public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuffer>, ISourceEventProcessor, IDFTWidthChangeProcessor
{
private static final long serialVersionUID = 1L;
@@ -223,7 +222,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
*
* @param zoom level, 0 - 6.
* @param frequency under the mouse to maintain while zooming
* @param xAxisOffset where to maintain the frequency under the mouse
* @param windowOffset where to maintain the frequency under the mouse
*/
public void setZoom(int zoom, long frequency, double windowOffset)
{
@@ -247,7 +246,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
//Calculate the bin offset into the newly sized zoom window that
//would place the frequency in the same proportional window location
//that it was in the previous zoom size
double windowBinOffset = (double) getZoomWindowSizeInBins() * windowOffset;
double windowBinOffset = (double)getZoomWindowSizeInBins() * windowOffset;
//Set the overall offset to place the reference frequency in the
//same location in the newly zoomed window
@@ -276,7 +275,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
offset = mDFTSize.getSize() - getZoomWindowSizeInBins();
}
mDFTZoomWindowOffset = (int) offset;
mDFTZoomWindowOffset = (int)offset;
mSpectrumPanel.setZoomWindowOffset(mDFTZoomWindowOffset);
mOverlayPanel.setZoomWindowOffset(mDFTZoomWindowOffset);
@@ -294,7 +293,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
public int getZoomMultiplier()
{
return (int) Math.pow(2.0, mZoom);
return (int)Math.pow(2.0, mZoom);
}
/**
@@ -310,9 +309,9 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
if(mOverlayPanel.containsFrequency(frequency))
{
offset = (double) mDFTSize.getSize() *
((double) (frequency - mOverlayPanel.getMinFrequency()) /
(double) mOverlayPanel.getBandwidth());
offset = (double)mDFTSize.getSize() *
((double)(frequency - mOverlayPanel.getMinFrequency()) /
(double)mOverlayPanel.getBandwidth());
}
return offset;
@@ -361,11 +360,11 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
double totalHeight = mLayeredPanel.getPreferredSize().getHeight() +
mWaterfallPanel.getPreferredSize().getHeight();
mLayeredPanel.setPreferredSize(new Dimension((int) mLayeredPanel
.getPreferredSize().getWidth(), (int) (totalHeight / 2.0d)));
mLayeredPanel.setPreferredSize(new Dimension((int)mLayeredPanel
.getPreferredSize().getWidth(), (int)(totalHeight / 2.0d)));
mWaterfallPanel.setPreferredSize(new Dimension((int) mWaterfallPanel
.getPreferredSize().getWidth(), (int) (totalHeight / 2.0d)));
mWaterfallPanel.setPreferredSize(new Dimension((int)mWaterfallPanel
.getPreferredSize().getWidth(), (int)(totalHeight / 2.0d)));
//Create the split pane to hold the layered pane and the waterfall
JideSplitPane splitPane = new JideSplitPane(JSplitPane.VERTICAL_SPLIT);
@@ -385,17 +384,17 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
mDFTConverter = new ComplexDecibelConverter();
mDFTProcessor.addConverter(mDFTConverter);
mDFTConverter.addListener((DFTResultsListener) mSpectrumPanel);
mDFTConverter.addListener((DFTResultsListener) mWaterfallPanel);
mDFTConverter.addListener((DFTResultsListener)mSpectrumPanel);
mDFTConverter.addListener((DFTResultsListener)mWaterfallPanel);
}
/**
* Receives frequency change events -- primarily from tuner components.
*/
public void frequencyChanged(FrequencyChangeEvent event)
public void process(SourceEvent event)
{
mOverlayPanel.frequencyChanged(event);
mDFTProcessor.frequencyChanged(event);
mOverlayPanel.process(event);
mDFTProcessor.process(event);
}
/**
@@ -425,19 +424,15 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
mTuner.getTunerController().addListener(this);
//Register the dft processor to receive samples from the tuner
mTuner.addListener((Listener<ComplexBuffer>) mDFTProcessor);
mTuner.addListener((Listener<ComplexBuffer>)mDFTProcessor);
mSpectrumPanel.setSampleSize(mTuner.getSampleSize());
//Fire frequency and sample rate change events so that the spectrum
//and overlay panels can synchronize
frequencyChanged(new FrequencyChangeEvent(
Event.NOTIFICATION_FREQUENCY_CHANGE,
mTuner.getTunerController().getFrequency()));
process(SourceEvent.frequencyChange(mTuner.getTunerController().getFrequency()));
frequencyChanged(new FrequencyChangeEvent(
Event.NOTIFICATION_SAMPLE_RATE_CHANGE,
mTuner.getTunerController().getSampleRate()));
process(SourceEvent.sampleRateChange(mTuner.getTunerController().getSampleRate()));
}
}
@@ -452,7 +447,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
mTuner.getTunerController().removeListener(this);
//Deregister the dft processor from receiving samples
mTuner.removeListener((Listener<ComplexBuffer>) mDFTProcessor);
mTuner.removeListener((Listener<ComplexBuffer>)mDFTProcessor);
mTuner = null;
}
}
@@ -508,7 +503,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
long frequency = mOverlayPanel.getFrequencyFromAxis(e.getX());
double windowOffset = (double) e.getX() / (double) getWidth();
double windowOffset = (double)e.getX() / (double)getWidth();
setZoom(zoom, frequency, windowOffset);
}
@@ -526,9 +521,9 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
int dragDistance = mDragStartX - event.getX();
double binDistance = (double) dragDistance / mPixelsPerBin;
double binDistance = (double)dragDistance / mPixelsPerBin;
int offset = (int) (mDFTZoomWindowOffsetAtDragStart + binDistance);
int offset = (int)(mDFTZoomWindowOffsetAtDragStart + binDistance);
if(offset < 0)
{
@@ -552,9 +547,9 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
mDFTZoomWindowOffsetAtDragStart = mDFTZoomWindowOffset;
mPixelsPerBin = (double) getWidth() /
((double) (mDFTSize.getSize()) /
(double) getZoomMultiplier());
mPixelsPerBin = (double)getWidth() /
((double)(mDFTSize.getSize()) /
(double)getZoomMultiplier());
}
/**
@@ -774,11 +769,11 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
}
/*
* Zoom menu
* Zoom menu
*/
JMenuItem zoomMenu = new JMenu("Zoom");
double windowOffset = (double) event.getX() / (double) getWidth();
double windowOffset = (double)event.getX() / (double)getWidth();
zoomMenu.add(new ZoomItem(frequency, windowOffset));