From 7c6b7494b8b7f2f79a880e88edf25036e7118db5 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 21 Apr 2017 12:17:55 -0400 Subject: [PATCH] wip-channelizer --- src/channel/state/ChannelState.java | 26 +- .../traffic/TrafficChannelManager.java | 8 +- src/controller/channel/Channel.java | 16 +- src/dsp/filter/FilterFactory.java | 6 +- .../AbstractComplexPolyphaseChannelizer.java | 128 +++ src/dsp/filter/channelizer/Channelizer.java | 135 +++ .../filter/channelizer/ChannelizerViewer.java | 12 +- .../ComplexPolyphaseChannelizer.java | 83 +- .../ComplexPolyphaseChannelizerM2.java | 77 +- .../channelizer/PolyphaseChannelSource.java | 201 +++++ .../filter/fir/FIRFilterSpecification.java | 2 +- .../remez/PolyphaseChannelizerDesigner.java | 2 +- src/dsp/psk/LSMDemodulator.java | 12 +- src/gui/control/JFrequencyControl.java | 127 ++- src/module/ProcessingChain.java | 40 +- src/module/decode/p25/C4FMSymbolFilter.java | 846 +++++++++--------- src/module/decode/p25/P25Decoder.java | 6 +- src/module/decode/p25/P25_C4FMDecoder.java | 16 +- src/module/decode/p25/P25_LSMDecoder.java | 12 +- src/sample/SampleType.java | 5 +- src/source/ComplexSource.java | 27 +- src/source/ISourceEventListener.java | 35 + src/source/ISourceEventProcessor.java | 24 + ...rovider.java => ISourceEventProvider.java} | 10 +- src/source/RealSource.java | 28 +- src/source/Source.java | 44 +- src/source/SourceEvent.java | 192 ++++ src/source/config/SourceConfigRecording.java | 106 +-- src/source/config/SourceConfigTuner.java | 103 ++- src/source/mixer/RealMixerSource.java | 8 +- src/source/recording/Recording.java | 12 +- src/source/tuner/ComplexChannelSource.java | 169 ++++ src/source/tuner/Tuner.java | 372 ++++---- src/source/tuner/TunerChannel.java | 195 ++-- src/source/tuner/TunerChannelSource.java | 64 +- src/source/tuner/TunerController.java | 14 +- src/source/tuner/TunerSourceEditor.java | 8 +- .../frequency/DCTrackingFrequencyControl.java | 128 --- .../frequency/DCTrackingSourceControl.java | 130 +++ .../tuner/frequency/FrequencyChangeEvent.java | 34 - .../FrequencyChangeProcessorWrapper.java | 45 - .../tuner/frequency/FrequencyController.java | 374 ++++---- .../frequency/IFrequencyChangeListener.java | 34 - .../frequency/IFrequencyChangeProcessor.java | 8 - src/source/wave/ComplexWaveSource.java | 8 +- src/source/wave/RealWaveSource.java | 8 +- src/spectrum/ChannelSpectrumPanel.java | 7 +- src/spectrum/DFTProcessor.java | 8 +- src/spectrum/OverlayPanel.java | 8 +- src/spectrum/SpectralDisplayPanel.java | 67 +- 50 files changed, 2360 insertions(+), 1670 deletions(-) create mode 100644 src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java create mode 100644 src/dsp/filter/channelizer/Channelizer.java create mode 100644 src/dsp/filter/channelizer/PolyphaseChannelSource.java create mode 100644 src/source/ISourceEventListener.java create mode 100644 src/source/ISourceEventProcessor.java rename src/source/{tuner/frequency/IFrequencyChangeProvider.java => ISourceEventProvider.java} (79%) create mode 100644 src/source/SourceEvent.java create mode 100644 src/source/tuner/ComplexChannelSource.java delete mode 100644 src/source/tuner/frequency/DCTrackingFrequencyControl.java create mode 100644 src/source/tuner/frequency/DCTrackingSourceControl.java delete mode 100644 src/source/tuner/frequency/FrequencyChangeEvent.java delete mode 100644 src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java delete mode 100644 src/source/tuner/frequency/IFrequencyChangeListener.java delete mode 100644 src/source/tuner/frequency/IFrequencyChangeProcessor.java diff --git a/src/channel/state/ChannelState.java b/src/channel/state/ChannelState.java index d9407c0f..eb3600a4 100644 --- a/src/channel/state/ChannelState.java +++ b/src/channel/state/ChannelState.java @@ -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 getFrequencyChangeListener() + public Listener 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 + public class SourceChangeListener implements Listener { @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)); } diff --git a/src/channel/traffic/TrafficChannelManager.java b/src/channel/traffic/TrafficChannelManager.java index c2568de8..d7fac5a2 100644 --- a/src/channel/traffic/TrafficChannelManager.java +++ b/src/channel/traffic/TrafficChannelManager.java @@ -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) { diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index 79be174b..b3e49224 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -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 +public class Channel extends Configuration implements Listener { // Standard channels are persisted and traffic channels are temporary public enum ChannelType @@ -418,15 +417,14 @@ public class Channel extends Configuration implements Listener + * + ******************************************************************************/ +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, ISourceEventListener +{ + private final static Logger mLog = LoggerFactory.getLogger(AbstractComplexPolyphaseChannelizer.class); + + private Broadcaster mSourceChangeBroadcaster = new Broadcaster(); + private List 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 getSourceEventListener() + { + return mSourceChangeBroadcaster; + } +} diff --git a/src/dsp/filter/channelizer/Channelizer.java b/src/dsp/filter/channelizer/Channelizer.java new file mode 100644 index 00000000..a66e32d7 --- /dev/null +++ b/src/dsp/filter/channelizer/Channelizer.java @@ -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 + * + ******************************************************************************/ +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 +{ + private final static Logger mLog = LoggerFactory.getLogger(Channelizer.class); + + private Tuner mTuner; + private List 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) + { + + } +} diff --git a/src/dsp/filter/channelizer/ChannelizerViewer.java b/src/dsp/filter/channelizer/ChannelizerViewer.java index b6064c62..09d80c3f 100644 --- a/src/dsp/filter/channelizer/ChannelizerViewer.java +++ b/src/dsp/filter/channelizer/ChannelizerViewer.java @@ -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 { - 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); } diff --git a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizer.java b/src/dsp/filter/channelizer/ComplexPolyphaseChannelizer.java index 408f844d..c4977b1e 100644 --- a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizer.java +++ b/src/dsp/filter/channelizer/ComplexPolyphaseChannelizer.java @@ -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 +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 if(mFilterPointer == 0) { calculate(); - mFilterPointer = mChannelCount - 1; + mFilterPointer = getChannelCount() - 1; } else { @@ -78,20 +74,9 @@ public class ComplexPolyphaseChannelizer implements Listener } } - @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 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 */ 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 } } - 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 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); diff --git a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java b/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java index 4ee0f32d..3b8673b8 100644 --- a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java +++ b/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java @@ -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 +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 * 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 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 } } - @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 /** * 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 { 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 */ 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 } } - 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 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); diff --git a/src/dsp/filter/channelizer/PolyphaseChannelSource.java b/src/dsp/filter/channelizer/PolyphaseChannelSource.java new file mode 100644 index 00000000..6050491b --- /dev/null +++ b/src/dsp/filter/channelizer/PolyphaseChannelSource.java @@ -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 + * + ******************************************************************************/ +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() + { + + } +} diff --git a/src/dsp/filter/fir/FIRFilterSpecification.java b/src/dsp/filter/fir/FIRFilterSpecification.java index 2ef0d820..2ffc10a0 100644 --- a/src/dsp/filter/fir/FIRFilterSpecification.java +++ b/src/dsp/filter/fir/FIRFilterSpecification.java @@ -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() diff --git a/src/dsp/filter/fir/remez/PolyphaseChannelizerDesigner.java b/src/dsp/filter/fir/remez/PolyphaseChannelizerDesigner.java index f7436798..01d8a8ff 100644 --- a/src/dsp/filter/fir/remez/PolyphaseChannelizerDesigner.java +++ b/src/dsp/filter/fir/remez/PolyphaseChannelizerDesigner.java @@ -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 * diff --git a/src/dsp/psk/LSMDemodulator.java b/src/dsp/psk/LSMDemodulator.java index 5142d0d9..11c5de0d 100644 --- a/src/dsp/psk/LSMDemodulator.java +++ b/src/dsp/psk/LSMDemodulator.java @@ -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 ) ); // } // diff --git a/src/gui/control/JFrequencyControl.java b/src/gui/control/JFrequencyControl.java index ca2053aa..aed75ab0 100644 --- a/src/gui/control/JFrequencyControl.java +++ b/src/gui/control/JFrequencyControl.java @@ -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 + * ******************************************************************************/ 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 mProcessors = new ArrayList<>(); + private List mProcessors = new ArrayList<>(); private Color mHighlightColor = Color.YELLOW; @@ -62,7 +54,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess private Cursor mBlankCursor; - private HashMap mDigits = new HashMap(); + private HashMap mDigits = new HashMap(); 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 it = mProcessors.iterator(); + Iterator 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(); diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index a018e360..f820df09 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -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 mChannelEventBroadcaster = new Broadcaster<>(); private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); private Broadcaster mDecoderStateEventBroadcaster = new Broadcaster<>(); - private Broadcaster mFrequencyChangeEventBroadcaster = new Broadcaster<>(); + private Broadcaster mFrequencyChangeEventBroadcaster = new Broadcaster<>(); private Broadcaster mMessageBroadcaster = new Broadcaster<>(); private Broadcaster mFilteredRealBufferBroadcaster = new Broadcaster<>(); private Broadcaster 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 listener) + public void addFrequencyChangeListener(Listener 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 listener) + public void removeFrequencyChangeListener(Listener listener) { mFrequencyChangeEventBroadcaster.removeListener(listener); } diff --git a/src/module/decode/p25/C4FMSymbolFilter.java b/src/module/decode/p25/C4FMSymbolFilter.java index 51e7b78d..70a772e0 100644 --- a/src/module/decode/p25/C4FMSymbolFilter.java +++ b/src/module/decode/p25/C4FMSymbolFilter.java @@ -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, IFrequencyChangeListener, RealSampleProvider, Instrumentable +import java.util.ArrayList; +import java.util.List; + +public class C4FMSymbolFilter implements Listener, 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 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 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 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 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 listener ) - { - mFrequencyChangeListener = listener; - } - - @Override - public Listener getFrequencyChangeListener() - { - if( mFrequencyCorrectionProcessor == null ) - { - mFrequencyCorrectionProcessor = new FrequencyCorrectionProcessor(); - } - - return mFrequencyCorrectionProcessor; - } + public void setFrequencyChangeListener(Listener listener) + { + mFrequencyChangeListener = listener; + } - /** - * Receives notifications that the channel frequency correction has been - * applied and updates internal tracking value. - */ - public class FrequencyCorrectionProcessor implements Listener - { - @Override - public void receive( FrequencyChangeEvent event ) - { - switch( event.getEvent() ) - { - case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE: - mFrequencyCorrection = event.getValue().intValue(); + @Override + public Listener 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 + { + @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; + } + } + } } diff --git a/src/module/decode/p25/P25Decoder.java b/src/module/decode/p25/P25Decoder.java index 0ca1f8a9..79ab8f7b 100644 --- a/src/module/decode/p25/P25Decoder.java +++ b/src/module/decode/p25/P25Decoder.java @@ -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; diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java index 2e7b1114..cbae323d 100644 --- a/src/module/decode/p25/P25_C4FMDecoder.java +++ b/src/module/decode/p25/P25_C4FMDecoder.java @@ -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 listener ) + public void setSourceEventListener(Listener 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 getFrequencyChangeListener() + public Listener getSourceEventListener() { if( mSymbolFilter != null ) { - return mSymbolFilter.getFrequencyChangeListener(); + return mSymbolFilter.getSourceEventListener(); } return null; diff --git a/src/module/decode/p25/P25_LSMDecoder.java b/src/module/decode/p25/P25_LSMDecoder.java index 1b135eb5..3e30e9ae 100644 --- a/src/module/decode/p25/P25_LSMDecoder.java +++ b/src/module/decode/p25/P25_LSMDecoder.java @@ -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 listener) + public void setSourceEventListener(Listener listener) { } @Override - public void removeFrequencyChangeListener() + public void removeSourceEventListener() { } @Override - public Listener getFrequencyChangeListener() + public Listener getSourceEventListener() { - return new Listener() + return new Listener() { @Override - public void receive(FrequencyChangeEvent frequencyChangeEvent) + public void receive(SourceEvent sourceEvent) { //Ignored } diff --git a/src/sample/SampleType.java b/src/sample/SampleType.java index 09e9196b..8aedd8e2 100644 --- a/src/sample/SampleType.java +++ b/src/sample/SampleType.java @@ -2,7 +2,6 @@ package sample; public enum SampleType { - COMPLEX, //Baseband - REAL, //Demodulated - COMPLEX_AND_REAL; //Baseband and Demodulated + COMPLEX, // I/Q + REAL; // Real } diff --git a/src/source/ComplexSource.java b/src/source/ComplexSource.java index e7e1939c..b864aae8 100644 --- a/src/source/ComplexSource.java +++ b/src/source/ComplexSource.java @@ -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 + * + ******************************************************************************/ package source; import sample.Provider; @@ -6,8 +24,9 @@ import sample.complex.ComplexBuffer; public abstract class ComplexSource extends Source implements Provider { - public ComplexSource() - { - super( SampleType.COMPLEX ); - } + @Override + public SampleType getSampleType() + { + return SampleType.COMPLEX; + } } diff --git a/src/source/ISourceEventListener.java b/src/source/ISourceEventListener.java new file mode 100644 index 00000000..feb65ddb --- /dev/null +++ b/src/source/ISourceEventListener.java @@ -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 + * + ******************************************************************************/ +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 getSourceEventListener(); +} diff --git a/src/source/ISourceEventProcessor.java b/src/source/ISourceEventProcessor.java new file mode 100644 index 00000000..45f74e20 --- /dev/null +++ b/src/source/ISourceEventProcessor.java @@ -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 + * + ******************************************************************************/ +package source; + +public interface ISourceEventProcessor +{ + void process(SourceEvent event) throws SourceException; +} diff --git a/src/source/tuner/frequency/IFrequencyChangeProvider.java b/src/source/ISourceEventProvider.java similarity index 79% rename from src/source/tuner/frequency/IFrequencyChangeProvider.java rename to src/source/ISourceEventProvider.java index 354e9af9..6e6a1bed 100644 --- a/src/source/tuner/frequency/IFrequencyChangeProvider.java +++ b/src/source/ISourceEventProvider.java @@ -16,15 +16,15 @@ * along with this program. If not, see * ******************************************************************************/ -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 listener ); - void removeFrequencyChangeListener(); + void setSourceEventListener(Listener listener ); + void removeSourceEventListener(); } diff --git a/src/source/RealSource.java b/src/source/RealSource.java index b512bfbc..8b08ebca 100644 --- a/src/source/RealSource.java +++ b/src/source/RealSource.java @@ -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 + * + ******************************************************************************/ package source; import sample.Provider; import sample.SampleType; import sample.real.RealBuffer; - public abstract class RealSource extends Source implements Provider { - public RealSource() - { - super( SampleType.REAL ); - } + @Override + public SampleType getSampleType() + { + return SampleType.REAL; + } } diff --git a/src/source/Source.java b/src/source/Source.java index 932c2749..8df74669 100644 --- a/src/source/Source.java +++ b/src/source/Source.java @@ -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) { diff --git a/src/source/SourceEvent.java b/src/source/SourceEvent.java new file mode 100644 index 00000000..5a9dd3b7 --- /dev/null +++ b/src/source/SourceEvent.java @@ -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 + * + ******************************************************************************/ +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); + } +} diff --git a/src/source/config/SourceConfigRecording.java b/src/source/config/SourceConfigRecording.java index 801fb211..f1b7c1c3 100644 --- a/src/source/config/SourceConfigRecording.java +++ b/src/source/config/SourceConfigRecording.java @@ -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 + * 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 + * ******************************************************************************/ 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); + } + } diff --git a/src/source/config/SourceConfigTuner.java b/src/source/config/SourceConfigTuner.java index d6d9a8d4..2f31457d 100644 --- a/src/source/config/SourceConfigTuner.java +++ b/src/source/config/SourceConfigTuner.java @@ -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 + * 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 + * ******************************************************************************/ 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 ); - } } diff --git a/src/source/mixer/RealMixerSource.java b/src/source/mixer/RealMixerSource.java index cfc96997..c92a9a5a 100644 --- a/src/source/mixer/RealMixerSource.java +++ b/src/source/mixer/RealMixerSource.java @@ -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 listener) + public void setSourceEventListener(Listener listener) { //Not implemented } @Override - public void removeFrequencyChangeListener() + public void removeSourceEventListener() { //Not implemented } @Override - public Listener getFrequencyChangeListener() + public Listener getSourceEventListener() { //Not implemented return null; diff --git a/src/source/recording/Recording.java b/src/source/recording/Recording.java index 9a228fc0..a89f77eb 100644 --- a/src/source/recording/Recording.java +++ b/src/source/recording/Recording.java @@ -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, - IFrequencyChangeProcessor, + ISourceEventProcessor, ITunerChannelProvider { private RecordingConfiguration mConfiguration; @@ -89,7 +89,7 @@ public class Recording implements Comparable, } @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, for( TunerChannelSource channel: mTunerChannels ) { - channel.frequencyChanged( event ); + channel.process( event ); } } } diff --git a/src/source/tuner/ComplexChannelSource.java b/src/source/tuner/ComplexChannelSource.java new file mode 100644 index 00000000..f505980f --- /dev/null +++ b/src/source/tuner/ComplexChannelSource.java @@ -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 + * + ******************************************************************************/ +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 mSourceEventListener; + private Listener 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 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 listener) + { + mComplexBufferListener = listener; + } + + @Override + public void removeListener(Listener 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 getSourceEventListener() + { + return mSourceEventProcessor; + } + + /** + * Adapter for implementing ISourceEventProcessor and dispatching source events to subclasses via the + * ISourceEventProcessor.process() method. + */ + public class SourceEventProcessor implements Listener + { + @Override + public void receive(SourceEvent sourceEvent) + { + try + { + process(sourceEvent); + } + catch(SourceException se) + { + mLog.error("Error processing source event", se); + } + } + } +} diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index cd6a3e4a..730a0bab 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -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 + * 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 + * ******************************************************************************/ 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> mSampleListeners = - new CopyOnWriteArrayList<>(); - - protected List> 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> mSampleListeners = new CopyOnWriteArrayList<>(); + protected List> mTunerChangeListeners = new ArrayList<>(); + protected Broadcaster 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 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 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 listener) + { + mSampleListeners.add(listener); + } + + /** + * Removes the registered listener + */ + public void removeListener(Listener listener) + { + mSampleListeners.remove(listener); } /** * Broadcasts the samples to all registered listeners */ - public void broadcast( ComplexBuffer sampleBuffer ) + public void broadcast(ComplexBuffer sampleBuffer) { - for( Listener listener: mSampleListeners ) - { - listener.receive( sampleBuffer ); - } + for(Listener listener : mSampleListeners) + { + listener.receive(sampleBuffer); + } } - /** - * Registers the listener - */ - public void addTunerChangeListener( Listener listener ) + /** + * Registers the listener + */ + public void addTunerChangeListener(Listener listener) { - mTunerChangeListeners.add( listener ); + mTunerChangeListeners.add(listener); } - /** - * Removes the registered listener - */ - public void removeTunerChangeListener( Listener listener ) + /** + * Removes the registered listener + */ + public void removeTunerChangeListener(Listener listener) { - mTunerChangeListeners.remove( listener ); + mTunerChangeListeners.remove(listener); } /** * Broadcasts the tuner change event */ - public void broadcast( TunerEvent event ) + public void broadcast(TunerEvent tunerEvent) { - for( Listener listener: mTunerChangeListeners ) - { - listener.receive( event ); - } + for(Listener 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 listener) + { + mSourceEventBroadcaster.addListener(listener); + } + + /** + * Removes the listener from receiving source events + */ + public void removeSourceEventListener(Listener listener) + { + mSourceEventBroadcaster.removeListener(listener); } } diff --git a/src/source/tuner/TunerChannel.java b/src/source/tuner/TunerChannel.java index 568e5424..f2b383b3 100644 --- a/src/source/tuner/TunerChannel.java +++ b/src/source/tuner/TunerChannel.java @@ -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 + * 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 + * ******************************************************************************/ package source.tuner; public class TunerChannel implements Comparable { - 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 - } } diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index 7556e7b5..a4d403de 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -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 +public class TunerChannelSource extends ComplexSource implements ISourceEventProcessor, Listener { 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 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 listener) + public void setSourceEventListener(Listener 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 getFrequencyChangeListener() + public Listener 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 + public class DownstreamProcessor implements ISourceEventListener, ISourceEventProvider, + Listener { //Listener to receive downstream events - private Listener mListener; + private Listener 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 listener) + public void setSourceEventListener(Listener 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 getFrequencyChangeListener() + public Listener 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()) { diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index 017231bd..23af0118 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -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 ); } diff --git a/src/source/tuner/TunerSourceEditor.java b/src/source/tuner/TunerSourceEditor.java index 4973e6b7..8d92360f 100644 --- a/src/source/tuner/TunerSourceEditor.java +++ b/src/source/tuner/TunerSourceEditor.java @@ -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 @@ -41,10 +41,10 @@ public class TunerSourceEditor extends Editor 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 ); } diff --git a/src/source/tuner/frequency/DCTrackingFrequencyControl.java b/src/source/tuner/frequency/DCTrackingFrequencyControl.java deleted file mode 100644 index 7cfe5648..00000000 --- a/src/source/tuner/frequency/DCTrackingFrequencyControl.java +++ /dev/null @@ -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 - ******************************************************************************/ -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, 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 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 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 ); - } -} diff --git a/src/source/tuner/frequency/DCTrackingSourceControl.java b/src/source/tuner/frequency/DCTrackingSourceControl.java new file mode 100644 index 00000000..09b48831 --- /dev/null +++ b/src/source/tuner/frequency/DCTrackingSourceControl.java @@ -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 + * + ******************************************************************************/ +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, 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 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 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); + } +} diff --git a/src/source/tuner/frequency/FrequencyChangeEvent.java b/src/source/tuner/frequency/FrequencyChangeEvent.java deleted file mode 100644 index 9ceb3685..00000000 --- a/src/source/tuner/frequency/FrequencyChangeEvent.java +++ /dev/null @@ -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; - } -} diff --git a/src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java b/src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java deleted file mode 100644 index 8aa66445..00000000 --- a/src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java +++ /dev/null @@ -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 -{ - 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 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); - } - } - } -} diff --git a/src/source/tuner/frequency/FrequencyController.java b/src/source/tuner/frequency/FrequencyController.java index 7262317b..7a4d83de 100644 --- a/src/source/tuner/frequency/FrequencyController.java +++ b/src/source/tuner/frequency/FrequencyController.java @@ -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 + * 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 + * ******************************************************************************/ 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 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 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; + } } diff --git a/src/source/tuner/frequency/IFrequencyChangeListener.java b/src/source/tuner/frequency/IFrequencyChangeListener.java deleted file mode 100644 index 99b75804..00000000 --- a/src/source/tuner/frequency/IFrequencyChangeListener.java +++ /dev/null @@ -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 - ******************************************************************************/ -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 getFrequencyChangeListener(); -} diff --git a/src/source/tuner/frequency/IFrequencyChangeProcessor.java b/src/source/tuner/frequency/IFrequencyChangeProcessor.java deleted file mode 100644 index 788c46ac..00000000 --- a/src/source/tuner/frequency/IFrequencyChangeProcessor.java +++ /dev/null @@ -1,8 +0,0 @@ -package source.tuner.frequency; - -import source.SourceException; - -public interface IFrequencyChangeProcessor -{ - public void frequencyChanged( FrequencyChangeEvent event ) throws SourceException; -} diff --git a/src/source/wave/ComplexWaveSource.java b/src/source/wave/ComplexWaveSource.java index 8d80ce1d..b99289e9 100644 --- a/src/source/wave/ComplexWaveSource.java +++ b/src/source/wave/ComplexWaveSource.java @@ -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 listener) + public void setSourceEventListener(Listener listener) { //Not implemented } @Override - public void removeFrequencyChangeListener() + public void removeSourceEventListener() { //Not implemented } @Override - public Listener getFrequencyChangeListener() + public Listener getSourceEventListener() { //Not implemented return null; diff --git a/src/source/wave/RealWaveSource.java b/src/source/wave/RealWaveSource.java index 4b9b120b..ec038471 100644 --- a/src/source/wave/RealWaveSource.java +++ b/src/source/wave/RealWaveSource.java @@ -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 listener) + public void setSourceEventListener(Listener listener) { //Not implemented } @Override - public void removeFrequencyChangeListener() + public void removeSourceEventListener() { //Not implemented } @Override - public Listener getFrequencyChangeListener() + public Listener getSourceEventListener() { //Not implemented return null; diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java index 397c3604..87fabc1d 100644 --- a/src/spectrum/ChannelSpectrumPanel.java +++ b/src/spectrum/ChannelSpectrumPanel.java @@ -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(); } diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index 0f9e066c..73515b87 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -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, IFrequencyChangeProcessor, IDFTWidthChangeProcessor +public class DFTProcessor implements Listener, ISourceEventProcessor, IDFTWidthChangeProcessor { private final static Logger mLog = LoggerFactory.getLogger(DFTProcessor.class); @@ -452,7 +452,7 @@ public class DFTProcessor implements Listener, IFrequencyChangePr } @Override - public void frequencyChanged(FrequencyChangeEvent event) + public void process(SourceEvent event) { switch(event.getEvent()) { diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index b064b804..d78101e2 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -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()) { diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index 505c17e0..9c871aea 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -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, IFrequencyChangeProcessor, IDFTWidthChangeProcessor +public class SpectralDisplayPanel extends JPanel implements Listener, ISourceEventProcessor, IDFTWidthChangeProcessor { private static final long serialVersionUID = 1L; @@ -223,7 +222,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener) mDFTProcessor); + mTuner.addListener((Listener)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) mDFTProcessor); + mTuner.removeListener((Listener)mDFTProcessor); mTuner = null; } } @@ -508,7 +503,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener