diff --git a/src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java b/src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java index 5aee3dcf..0505c1b0 100644 --- a/src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java +++ b/src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java @@ -25,8 +25,8 @@ import sample.Listener; import sample.complex.ComplexBuffer; import source.ISourceEventListener; import source.SourceEvent; +import source.tuner.channel.polyphase.PolyphaseChannelSource; -import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -36,7 +36,7 @@ public abstract class AbstractComplexPolyphaseChannelizer implements Listener mSourceChangeBroadcaster = new Broadcaster(); private List mChannels = new CopyOnWriteArrayList<>(); - private int mSampleRate; + private double mSampleRate; private int mChannelCount; private double mChannelSampleRate; @@ -45,7 +45,7 @@ public abstract class AbstractComplexPolyphaseChannelizer implements Listener - * - ******************************************************************************/ -package dsp.filter.channelizer; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import source.tuner.channel.TunerChannel; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; - -public class ChannelCalculator -{ - private final static Logger mLog = LoggerFactory.getLogger(ChannelCalculator.class); - - /** - * Policy to determine desired corrective action for methods that calculate an index for a frequency or a frequency - * from an index where the frequency may exist either at the overlapping boundary of two indexes, or in the middle - * of the wrap-around (N/2) index. - */ - private enum IndexBoundaryPolicy - { - ADJUST_POSITIVE, - ADJUST_NEGATIVE - } - - private int mSampleRate; - private int mChannelCount; - private double mCenterFrequency; - private double mChannelBandwidth; - private double mOversampling = 1.0; - - /** - * Calculates channel index(es) from a polyphase channelizer output to source a DDC polyphase channel source - * with a specified channel center frequency and bandwidth. - * - * Polyphase channel indexes are not linear/consecutive owing to the use of the FFT to rotate channels to their - * center frequencies following filtering in the polyphase filter bank. As such, this calculator provides methods - * to determine the min/center/max frequency of each channel that is adjusted for the current center frequency - * value and to determine the channel index(es) that need to be targeted to produce a requested output channel - * with a specific frequency and bandwdith. - * - * @param sampleRate in hertz for the input sample rate from the sample source (ie tuner) - * @param channelCount number of channels in the polyphase filter bank - * @param centerFrequency center frequency for the input sample stream - * @param oversampling set to 1.0 for no oversample, or greater than 1.0 for oversampling - */ - public ChannelCalculator(int sampleRate, int channelCount, double centerFrequency, double oversampling) - { - mSampleRate = sampleRate; - mChannelCount = channelCount; - mCenterFrequency = centerFrequency; - mOversampling = oversampling; - - updateChannelBandwidth();; - } - - /** - * Calculates the channel bandwidth from the sample rate and channel count. - */ - private void updateChannelBandwidth() - { - mChannelBandwidth = (double)mSampleRate / (double)mChannelCount; - } - - /** - * Center frequency for the channelizer - */ - public double getCenterFrequency() - { - return mCenterFrequency; - } - - /** - * Updates the center frequency for the channelizer - */ - public void setCenterFrequency(double frequency) - { - mCenterFrequency = frequency; - } - - /** - * Number of channels being channelized by the polyphase channelizer - */ - public int getChannelCount() - { - return mChannelCount; - } - - /** - * Updates the number of channels being channelized by the polyphase channelizer - */ - public void setChannelCount(int channelCount) - { - mChannelCount = channelCount; - updateChannelBandwidth(); - } - - /** - * Identifies the index of the wrap-around channel meaning that the lower half of the channel exists at the upper - * end of the spectrum and the upper half of the channel exists at the lower end of the spectrum - * - * @return wrap-around channel index - */ - public int getWrapAroundIndex() - { - return mChannelCount / 2; - } - - /** - * PolyphaseChannelManager input sample rate - * @return input sample rate in hertz - */ - public int getSampleRate() - { - return mSampleRate; - } - - /** - * Updates the channel count using the sample rate argument to calculate the number of channels from the - * already established channel bandwidth. - * - * @param sampleRate to use for channel count - */ - public void setSampleRate(int sampleRate) - { - setChannelCount((int)((double)sampleRate / getChannelBandwidth())); - } - - /** - * Channel bandwidth. Note: this value may be smaller than the channel sample rate when oversampling - * is being used. - */ - public double getChannelBandwidth() - { - return mChannelBandwidth; - } - - /** - * Half of the channel bandwidth. - */ - public double getHalfChannelBandwidth() - { - return mChannelBandwidth / 2.0; - } - - /** - * Oversampling rate produced by the channelizer. - * - * @return 1.0 for no oversampling, or a value greater than 1.0 when oversampling is applied. - */ - public double getOversampling() - { - return mOversampling; - } - - /** - * Sets the oversampling rate being applied by the polyphase channelizer - * @param oversampling rate where 1.0 indicates no oversampling, and a value greater than 1.0 indicates - * that the polyphase channelizer is oversampling each channel. - */ - public void setOversampling(double oversampling) - { - mOversampling = oversampling; - } - - /** - * Channel sample rate. This value is the product of the filtered channel bandwidth times the - * oversampling rate. - */ - public double getChannelSampleRate() - { - return (double)getChannelBandwidth() * getOversampling(); - } - - /** - * PolyphaseChannelManager input minimum frequency - * @return minimum frequency in hertz - */ - public long getMinimumFrequency() - { - return (long)(getCenterFrequency() - (getSampleRate() / 2.0)); - } - - /** - * PolyphaseChannelManager input maximum frequency - * @return maximum frequency in hertz - */ - public long getMaximumFrequency() - { - return (long)(getCenterFrequency() + (getSampleRate() / 2.0)); - } - - /** - * Identifies the channelizer channel index(es) required to produce a tuner channel with the specified center - * frequency and channel bandwidth. - * - * @param tunerChannel specifying the desired channel frequency and bandwidth - * - * @return an array of one or more polyphase channelizer index(es) to target in order to produce an output channel - * for the desired frequency and bandwidth. - * - * @throws IllegalArgumentException if the tuner channel's minimum or maximum frequency is not contained - * within the channelizer's minimum or maximum frequency range - */ - public List getChannelIndexes(TunerChannel tunerChannel) throws IllegalArgumentException - { - if(tunerChannel.getMinFrequency() < getMinimumFrequency() || - tunerChannel.getMaxFrequency() > getMaximumFrequency()) - { - throw new IllegalArgumentException("Requested channel cannot be provided by this " + - "channelizer. Requested channel [" + tunerChannel.getMinFrequency() + " - " + - tunerChannel.getMaxFrequency() + "] exceeds current channelizer frequency range [" + - getMinimumFrequency() + " - " + getMaximumFrequency() + "]"); - } - - int minIndex = getIndexForFrequency(tunerChannel.getMinFrequency(), IndexBoundaryPolicy.ADJUST_POSITIVE); - int maxIndex = getIndexForFrequency(tunerChannel.getMaxFrequency(), IndexBoundaryPolicy.ADJUST_NEGATIVE); - - //Detect the unsourceable situation where the wrap around index would be included in both the minimum index - //and the maximum index, meaning that the tuner channel bandwidth is nearly the same as the input sample rate. - if(minIndex == getWrapAroundIndex() && maxIndex == getWrapAroundIndex()) - { - throw new IllegalArgumentException("Requested tuner channel cannot be provided. Requested bandwidth is " + - "within two channel bandwidths of the sample rate."); - } - - List indexes = new ArrayList<>(); - - indexes.add(minIndex); - - if(minIndex != maxIndex) - { - //Ensure our min/max indexes are within the correct bounds 0 <> N-1 to safeguard the while loop below - if(minIndex < 0 || minIndex >= getChannelCount() || maxIndex < 0 || maxIndex >= getChannelCount()) - { - throw new IllegalArgumentException("Something went wrong while calculating the min and max polyphase " + - "channel indexes for tuner channel: " + tunerChannel); - } - - int pointer = minIndex + 1; - - if(pointer >= getChannelCount()) - { - pointer -= getChannelCount(); - } - - while(pointer != maxIndex) - { - indexes.add(pointer); - - pointer++; - - if(pointer >= getChannelCount()) - { - pointer -= getChannelCount(); - } - } - - indexes.add(maxIndex); - } - - return indexes; - } - - /** - * Calculates the index for the specified frequency. Certain frequencies can exist as both the maximum frequency - * of one channel and the minimum frequency of an adjacent channel. The index boundary policy identifies your - * choice to select the higher frequency channel index (ADJUST_POSITIVE) or the lower frequency channel - * index (ADJUST_NEGATIVE). - * - * @param frequency to determine channel index for - * @param indexBoundaryPolicy to determine the behavior when the frequency overlaps two channel boundaries - * @return index for the frequency - */ - public int getIndexForFrequency(long frequency, IndexBoundaryPolicy indexBoundaryPolicy) - { - double offset = frequency - getCenterFrequency(); - - if(Math.abs(offset) < getHalfChannelBandwidth()) - { - return 0; //Center index - } - - if(offset > 0) - { - offset += getHalfChannelBandwidth(); - } - else - { - offset -= getHalfChannelBandwidth(); - } - - int indexOffset = (int)(offset / getChannelBandwidth()); - - if(indexOffset < 0) - { - indexOffset += getChannelCount(); - } - - if(indexBoundaryPolicy == IndexBoundaryPolicy.ADJUST_POSITIVE && - isOverlapFrequency(frequency, indexOffset, indexOffset + 1)) - { - indexOffset = normalize(indexOffset + 1); - } - else if(indexBoundaryPolicy == IndexBoundaryPolicy.ADJUST_NEGATIVE && - isOverlapFrequency(frequency, indexOffset - 1, indexOffset)) - { - indexOffset = normalize(indexOffset - 1); - } - - return indexOffset; - } - - /** - * Indicates if the frequency is an overlapping frequency between channel index 1 and channel index 2 where the - * maximum frequency of index 1 is the same as the minimum frequency of index 2 and both of these frequencies are - * the same as the frequency argument. - * - * Note: both index 1 and index 2 will be normalized to the range of 0 <> N-1 prior to determining overlap - * - * @param frequency to test for overlap between index 1 and 2 - * @param index1 which is the lower channel index located next to index 2. - * @param index2 which is the higher channel index located next to index 1. - * @return true if frequency = index1 maximum frequency = index2 minimum frequency - */ - public boolean isOverlapFrequency(long frequency, int index1, int index2) - { - index1 = normalize(index1); - index2 = normalize(index2); - - //Check that these two indexes are side by side - int delta = normalize(index2 - index1); - - if(delta != 1) - { - return false; - } - - long index1MaximumFrequency = (long)getIndexMaximumFrequency(index1, IndexBoundaryPolicy.ADJUST_POSITIVE); - long index2MinimumFrequency = (long)getIndexMinimumFrequency(index2, IndexBoundaryPolicy.ADJUST_NEGATIVE); - - if(index1 == getWrapAroundIndex()) - { - index1MaximumFrequency = (long)getIndexMaximumFrequency(index1, IndexBoundaryPolicy.ADJUST_NEGATIVE); - } - - if(index2 == getWrapAroundIndex()) - { - index2MinimumFrequency = (long)getIndexMinimumFrequency(index2, IndexBoundaryPolicy.ADJUST_POSITIVE); - } - - return frequency == index1MaximumFrequency && frequency == index2MinimumFrequency; - } - - /** - * Normalizes the index to the range of 0 <> N-1 channel indexes - */ - private int normalize(int index) - { - while(index < 0) - { - index += getChannelCount(); - } - - while(index >= getChannelCount()) - { - index -= getChannelCount(); - } - - return index; - } - - /** - * Calculates the center frequency for the channel identified by the index in the channel results array. - * - * @param index to determine the channel center frequency - * @return center frequency for the indexed channel - */ - public double getIndexCenterFrequency(int index, IndexBoundaryPolicy indexBoundaryPolicy) - { - if(index < 0 || index >= getChannelCount()) - { - throw new IllegalArgumentException("Illegal channel index"); - } - - int wrapAroundIndex = getWrapAroundIndex(); - - if(index == wrapAroundIndex) - { - if(indexBoundaryPolicy == IndexBoundaryPolicy.ADJUST_POSITIVE) - { - return getCenterFrequency() + (index * getChannelBandwidth()); - } - else - { - return getCenterFrequency() - (index * getChannelBandwidth()); - } - } - else if(index < wrapAroundIndex) - { - return getCenterFrequency() + (index * getChannelBandwidth()); - } - else - { - return getCenterFrequency() - ((getChannelCount() - index) * getChannelBandwidth()); - } - } - - /** - * Calculates the minimum frequency band edge for the channel identified by the index in the channel results - * array. - * @param index to calculate the minimum band edge frequency for the channel. - * @param indexBoundaryPolicy for the wrap around index to indicate if this method should return the minimum - * frequency of the upper spectrum half of the wrap around channel (ADJUST_POSITIVE) or the minimum frequency of the - * lower spectrum half of the wrap around channel (ADJUST_NEGATIVE) - * @return frequency of the minimum band edge - */ - public double getIndexMinimumFrequency(int index, IndexBoundaryPolicy indexBoundaryPolicy) - { - if(index < 0 || index >= getChannelCount()) - { - throw new IllegalArgumentException("Can't determine minimum frequency - illegal channel index [" + - index + "]"); - } - - int wrapAroundIndex = getWrapAroundIndex(); - - if(index == wrapAroundIndex) - { - if(indexBoundaryPolicy == IndexBoundaryPolicy.ADJUST_POSITIVE) - { - return getCenterFrequency() + ((double)index * getChannelBandwidth()) - getHalfChannelBandwidth(); - } - else - { - return getIndexCenterFrequency(index, indexBoundaryPolicy); - } - } - else if(index <= wrapAroundIndex) - { - return getCenterFrequency() + ((double)index * getChannelBandwidth()) - getHalfChannelBandwidth(); - } - else - { - return getCenterFrequency() - ((double)(getChannelCount() - index) * getChannelBandwidth()) - - getHalfChannelBandwidth(); - } - } - - /** - * Calculates the maximum frequency band edge for the channel identified by the index in the channel results - * array. - * @param index to calculate the maximum band edge frequency for the channel. - * @param indexBoundaryPolicy for the wrap around index to indicate if this method should return the maximum - * frequency of the upper spectrum half of the wrap around channel (ADJUST_POSITIVE) or the maximum frequency of the - * lower spectrum half of the wrap around channel (ADJUST_NEGATIVE) - * @return frequency of the maximum band edge - */ - public double getIndexMaximumFrequency(int index, IndexBoundaryPolicy indexBoundaryPolicy) - { - if(index < 0 || index >= getChannelCount()) - { - throw new IllegalArgumentException("Can't determine maximum frequency - illegal channel index [" + - index + "]"); - } - - int wrapAroundIndex = getWrapAroundIndex(); - - if(index == wrapAroundIndex) - { - if(indexBoundaryPolicy == IndexBoundaryPolicy.ADJUST_POSITIVE) - { - return getIndexCenterFrequency(index, indexBoundaryPolicy); - } - else - { - return getCenterFrequency() - ((double)index * getChannelBandwidth() - getHalfChannelBandwidth()); - } - } - else if(index <= wrapAroundIndex) - { - return getCenterFrequency() + ((double)index * getChannelBandwidth()) + getHalfChannelBandwidth(); - } - else - { - return getCenterFrequency() - ((double)(getChannelCount() - index) * getChannelBandwidth()) + - getHalfChannelBandwidth(); - } - } - - /** - * Calculates the center frequency for a channel that will be synthesized from the channel indexes. - * @param indexes that are contiguous in the spectrum - * @return center frequency in hertz - */ - public long getCenterFrequencyForIndexes(List indexes) - { - if(indexes.isEmpty()) - { - throw new IllegalArgumentException("Indexes cannot be empty"); - } - - int middleIndex = indexes.get(indexes.size() / 2); - - if(indexes.size() % 2 == 0) - { - //Even number of indexes - return (long)getIndexMinimumFrequency(middleIndex, IndexBoundaryPolicy.ADJUST_POSITIVE); - } - else - { - //Odd number of indexes - return (long)getIndexCenterFrequency(middleIndex, IndexBoundaryPolicy.ADJUST_POSITIVE); - } - } - - /** - * String representation of the channel calculator settings - */ - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append("Channels:").append(getChannelCount()); - sb.append(" Sample Rate:").append(getSampleRate()); - sb.append(" Channel Bandwidth:").append(getChannelBandwidth()); - sb.append(" Channel Rate:").append(getChannelSampleRate()); - sb.append(" Min:").append(getMinimumFrequency()); - sb.append(" Max:").append(getMaximumFrequency()); - return sb.toString(); - } - - - public static void main(String[] args) - { - mLog.debug("Starting ...."); - - int channelBandwidth = 12500; - int sampleRate = 100000; //Airspy - int channelCount = sampleRate / channelBandwidth; - double centerFrequency = 455000000; - - ChannelCalculator calculator = new ChannelCalculator(sampleRate, channelCount, centerFrequency, 2.0); - - mLog.debug(calculator.toString()); - - DecimalFormat decimalFormat = new DecimalFormat("000,000,000.0"); - - int wrapAroundIndex = calculator.getWrapAroundIndex(); - - IndexBoundaryPolicy indexBoundaryPolicy = IndexBoundaryPolicy.ADJUST_NEGATIVE; - - for(int index = wrapAroundIndex; index < calculator.getChannelCount(); index++) - { - mLog.debug("Index:" + index + " Min:" + decimalFormat.format(calculator.getIndexMinimumFrequency(index, indexBoundaryPolicy)) + - " Center:" + decimalFormat.format(calculator.getIndexCenterFrequency(index, indexBoundaryPolicy)) + - " Max:" + decimalFormat.format(calculator.getIndexMaximumFrequency(index, indexBoundaryPolicy))); - } - - indexBoundaryPolicy = IndexBoundaryPolicy.ADJUST_POSITIVE; - - for(int index = 0; index <= wrapAroundIndex; index++) - { - mLog.debug("Index:" + index + " Min:" + decimalFormat.format(calculator.getIndexMinimumFrequency(index, indexBoundaryPolicy)) + - " Center:" + decimalFormat.format(calculator.getIndexCenterFrequency(index, indexBoundaryPolicy)) + - " Max:" + decimalFormat.format(calculator.getIndexMaximumFrequency(index, indexBoundaryPolicy))); - } - - mLog.debug("Finished"); - } -} diff --git a/src/dsp/filter/channelizer/ChannelDistributor.java b/src/dsp/filter/channelizer/ChannelDistributor.java deleted file mode 100644 index a3cca8ac..00000000 --- a/src/dsp/filter/channelizer/ChannelDistributor.java +++ /dev/null @@ -1,107 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package dsp.filter.channelizer; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Broadcaster; -import sample.Listener; -import sample.complex.ComplexBuffer; - -import java.util.HashMap; -import java.util.Map; -import java.util.Queue; - -public class ChannelDistributor -{ - private final static Logger mLog = LoggerFactory.getLogger(ChannelDistributor.class); - private Map mListenerMap = new HashMap<>(); - private int mBufferSize; - private int mChannelCount; - - public ChannelDistributor(int bufferSize, int channelCount) - { - mBufferSize = bufferSize; - mChannelCount = channelCount; - - } - - public void receive(float[] channels) - { - for(int channel = 0; channel < channels.length / 2; channel++) - { - if(mListenerMap.containsKey(channel)) - { - ComplexSampleAssembler channelAssembler = mListenerMap.get(channel); - - int index = channel * 2; - channelAssembler.receive(channels[index], channels[index + 1]); - } - } - } - - public void addListener(int channel, Listener listener) - { - ComplexSampleAssembler channelAssembler = mListenerMap.get(channel); - - if(channelAssembler == null) - { - channelAssembler = new ComplexSampleAssembler(mBufferSize); - mListenerMap.put(channel, channelAssembler); - } - - channelAssembler.addListener(listener); - } - - public class ComplexSampleAssembler - { - private Broadcaster mBroadcaster = new Broadcaster<>(); - private float[] mBuffer; - private int mBufferPointer; - private int mBufferSize; - private boolean mEnableLogging; - - public ComplexSampleAssembler(int bufferSize) - { - mBufferSize = bufferSize; - mBuffer = new float[mBufferSize]; - } - - public void receive(float inphase, float quadrature) - { - mBuffer[mBufferPointer++] = inphase; - mBuffer[mBufferPointer++] = quadrature; - - if(mBufferPointer >= mBufferSize) - { - float[] bufferCopy = new float[mBuffer.length]; - System.arraycopy(mBuffer,0, bufferCopy, 0, mBuffer.length); - - mBroadcaster.receive(new ComplexBuffer(bufferCopy)); - - mBufferPointer = 0; - } - } - - public void addListener(Listener listener) - { - mBroadcaster.addListener(listener); - } - } -} diff --git a/src/dsp/filter/channelizer/ChannelizerViewer.java b/src/dsp/filter/channelizer/ChannelizerViewer.java deleted file mode 100644 index c072a9d0..00000000 --- a/src/dsp/filter/channelizer/ChannelizerViewer.java +++ /dev/null @@ -1,342 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package dsp.filter.channelizer; - -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.SampleType; -import sample.complex.ComplexBuffer; -import settings.SettingsManager; -import source.ISourceEventProcessor; -import source.SourceEvent; -import source.SourceException; -import source.tuner.channel.TunerChannel; -import source.tuner.channel.TunerChannelSource; -import source.tuner.configuration.TunerConfigurationModel; -import source.tuner.test.TestTuner; -import spectrum.DFTProcessor; -import spectrum.DFTSize; -import spectrum.SpectrumPanel; -import spectrum.converter.ComplexDecibelConverter; -import util.ThreadPool; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.*; - -public class ChannelizerViewer extends JFrame -{ - private final static Logger mLog = LoggerFactory.getLogger(ChannelizerViewer.class); - - private static final int CHANNEL_BANDWIDTH = 12500; - private static final int CHANNEL_FFT_FRAME_RATE = 20; - - private SettingsManager mSettingsManager = new SettingsManager(new TunerConfigurationModel()); - private JPanel mPrimaryPanel; - private JPanel mControlPanel; - private PrimarySpectrumPanel mPrimarySpectrumPanel; - private ChannelArrayPanel mChannelPanel; - private int mChannelCount; - private int mChannelsPerRow; - private long mToneFrequency; - private DFTSize mMainPanelDFTSize = DFTSize.FFT32768; - private DFTSize mChannelPanelDFTSize = DFTSize.FFT01024; - private TestTuner mTestTuner; - - /** - * GUI Test utility for researching polyphase channelizers. - * - * @param channelsPerRow - */ - public ChannelizerViewer(int channelsPerRow) - { - mTestTuner = new TestTuner(); - - mChannelCount = (int)(mTestTuner.getTunerController().getSampleRate() / CHANNEL_BANDWIDTH); - mChannelsPerRow = channelsPerRow; - - mToneFrequency = mTestTuner.getTunerController().getFrequency() + 19200; - - mTestTuner.getTunerController().setToneFrequency(mToneFrequency); - - init(); - } - - private void init() - { - setTitle("TunerChannelizer Viewer"); - setSize(1000, 800); - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); - setLocationRelativeTo(null); - add(getPrimaryPanel()); - } - - private JPanel getPrimaryPanel() - { - if(mPrimaryPanel == null) - { - mPrimaryPanel = new JPanel(); - mPrimaryPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[][][grow,fill]")); - mPrimaryPanel.add(getSpectrumPanel(), "wrap"); - mPrimaryPanel.add(getControlPanel(), "wrap"); - mPrimaryPanel.add(getChannelArrayPanel()); - } - - return mPrimaryPanel; - } - - private PrimarySpectrumPanel getSpectrumPanel() - { - if(mPrimarySpectrumPanel == null) - { - mPrimarySpectrumPanel = new PrimarySpectrumPanel(mSettingsManager, - mTestTuner.getTunerController().getSampleRate()); - mPrimarySpectrumPanel.setPreferredSize(new Dimension(1000, 200)); - mPrimarySpectrumPanel.setDFTSize(mMainPanelDFTSize); - mTestTuner.getSourceManager().addComplexBufferListener(mPrimarySpectrumPanel); - } - - return mPrimarySpectrumPanel; - } - - private JPanel getControlPanel() - { - if(mControlPanel == null) - { - mControlPanel = new JPanel(); - mControlPanel.setLayout(new MigLayout("insets 0 0 0 0", "", "")); - - mControlPanel.add(new JLabel("Tone:"), "align left"); - int maximumFrequencyOffset = (int)(mTestTuner.getTunerController().getSampleRate() / 2); - long minimumFrequency = mTestTuner.getTunerController().getFrequency() - maximumFrequencyOffset; - long maximumFrequency = mTestTuner.getTunerController().getFrequency() + maximumFrequencyOffset; - - SpinnerNumberModel model = new SpinnerNumberModel(mToneFrequency, minimumFrequency, maximumFrequency, - 100 ); - - model.addChangeListener(new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent e) - { - int toneFrequency = model.getNumber().intValue(); - mTestTuner.getTunerController().setToneFrequency(toneFrequency); - } - }); - - JSpinner spinner = new JSpinner(model); - - mControlPanel.add(spinner); - mControlPanel.add(new JLabel("Hz")); - - mControlPanel.add(new JLabel("Sample Rate: " + mTestTuner.getTunerController().getSampleRate() + " Hz"), "push,align right"); - - mControlPanel.add(new JLabel("Max: " + (mTestTuner.getTunerController().getSampleRate() / 2) + " Hz"), "push,align right"); - mControlPanel.add(new JLabel("Channels: " + mChannelCount), "push,align right"); - } - - return mControlPanel; - } - - private ChannelArrayPanel getChannelArrayPanel() - { - if(mChannelPanel == null) - { - mChannelPanel = new ChannelArrayPanel(); - } - - return mChannelPanel; - } - - public class ChannelArrayPanel extends JPanel - { - public ChannelArrayPanel() - { - int bufferSize = CHANNEL_BANDWIDTH / CHANNEL_FFT_FRAME_RATE; - if(bufferSize % 2 == 1) - { - bufferSize++; - } - - init(); - } - - private void init() - { - setLayout(new MigLayout("insets 0 0 0 0", "fill", "fill")); - - double spectralBandwidth = mTestTuner.getTunerController().getSampleRate(); - double halfSpectralBandwidth = spectralBandwidth / 2.0; - - int channelToLog = -1; - - for(int x = 0; x < mChannelCount; x++) - { - //place the channels left and right of 10.0 MHz - long frequency = (long)(mTestTuner.getTunerController().getFrequency() + - ((x * CHANNEL_BANDWIDTH) - halfSpectralBandwidth) + (CHANNEL_BANDWIDTH / 2)); - - mLog.debug("Channel " + x + " Frequency: " + frequency); - - ChannelPanel channelPanel = new ChannelPanel(mSettingsManager, CHANNEL_BANDWIDTH * 2, frequency, CHANNEL_BANDWIDTH, (x == channelToLog)); - channelPanel.setDFTSize(mChannelPanelDFTSize); - - if(x % mChannelsPerRow == mChannelsPerRow - 1) - { - add(channelPanel, "grow,push,wrap 2px"); - } - else - { - add(channelPanel, "grow,push"); - } - } - } - } - - public class PrimarySpectrumPanel extends JPanel implements Listener, ISourceEventProcessor - { - private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.COMPLEX); - private ComplexDecibelConverter mComplexDecibelConverter = new ComplexDecibelConverter(); - private SpectrumPanel mSpectrumPanel; - - public PrimarySpectrumPanel(SettingsManager settingsManager, double sampleRate) - { - setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); - mSpectrumPanel = new SpectrumPanel(settingsManager); - add(mSpectrumPanel); - - mDFTProcessor.addConverter(mComplexDecibelConverter); - mDFTProcessor.process(SourceEvent.sampleRateChange(sampleRate)); - mComplexDecibelConverter.addListener(mSpectrumPanel); - } - - public void setDFTSize(DFTSize dftSize) - { - mDFTProcessor.setDFTSize(dftSize); - } - - @Override - public void receive(ComplexBuffer complexBuffer) - { - mDFTProcessor.receive(complexBuffer); - } - - @Override - public void process(SourceEvent event) throws SourceException - { - mLog.debug("Source Event! Add handler support for this to channelizer viewer"); - } - } - - public class ChannelPanel extends JPanel implements Listener, ISourceEventProcessor - { - private TunerChannelSource mSource; - private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.COMPLEX); - private ComplexDecibelConverter mComplexDecibelConverter = new ComplexDecibelConverter(); - private SpectrumPanel mSpectrumPanel; - private long mFrequency; - - public ChannelPanel(SettingsManager settingsManager, double sampleRate, long frequency, int bandwidth, boolean enableLogging) - { - setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); - mSpectrumPanel = new SpectrumPanel(settingsManager); - add(mSpectrumPanel); - - mDFTProcessor.addConverter(mComplexDecibelConverter); - mDFTProcessor.process(SourceEvent.sampleRateChange(sampleRate)); - - mComplexDecibelConverter.addListener(mSpectrumPanel); - - mFrequency = frequency; - - TunerChannel tunerChannel = new TunerChannel(frequency, bandwidth); - mSource = mTestTuner.getSourceManager().getSource(tunerChannel); - - if(mSource != null) - { - if(enableLogging) - { - mSource.setListener(new Listener() - { - @Override - public void receive(ComplexBuffer complexBuffer) - { - mLog.debug("Sending samples to DFT processor: " + complexBuffer.getSamples().length); - mDFTProcessor.receive(complexBuffer); - } - }); - } - else - { - mSource.setListener(mDFTProcessor); - } - - mSource.start(); - } - else - { - mLog.error("Couldn't get a source from the tuner for frequency: " + frequency); - } - } - - public TunerChannelSource getSource() - { - return mSource; - } - - public void setDFTSize(DFTSize dftSize) - { - mDFTProcessor.setDFTSize(dftSize); - } - - @Override - public void receive(ComplexBuffer complexBuffer) - { - mLog.debug("Got a buffer for the channel dft processor"); - mDFTProcessor.receive(complexBuffer); - } - - @Override - public void process(SourceEvent event) throws SourceException - { - mLog.debug("Source Event! Add handler support for this to channelizer viewer"); - } - } - - public static void main(String[] args) - { - int channelsPerRow = 4; - - final ChannelizerViewer frame = new ChannelizerViewer(channelsPerRow); - - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - frame.setVisible(true); - } - }); - } - -} diff --git a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java b/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java index ebd8eb5f..d3bdded6 100644 --- a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java +++ b/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java @@ -78,7 +78,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann * @param sampleRate to be channelized. * @param tapsPerFilter to use when designing the filter */ - public ComplexPolyphaseChannelizerM2(int sampleRate, int tapsPerFilter) throws FilterDesignException + public ComplexPolyphaseChannelizerM2(double sampleRate, int tapsPerFilter) throws FilterDesignException { super(sampleRate, (int)(sampleRate / DEFAULT_MINIMUM_CHANNEL_BANDWIDTH)); @@ -94,7 +94,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann * @param sampleRate in hertz */ @Override - public void setSampleRate(int sampleRate) + public void setSampleRate(double sampleRate) { try { diff --git a/src/dsp/filter/channelizer/PolyphaseChannelManager.java b/src/dsp/filter/channelizer/PolyphaseChannelManager.java deleted file mode 100644 index 985174a7..00000000 --- a/src/dsp/filter/channelizer/PolyphaseChannelManager.java +++ /dev/null @@ -1,366 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package dsp.filter.channelizer; - -import dsp.filter.channelizer.output.IPolyphaseChannelOutputProcessor; -import dsp.filter.channelizer.output.OneChannelOutputProcessor; -import dsp.filter.channelizer.output.TwoChannelOutputProcessor; -import dsp.filter.design.FilterDesignException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.complex.IComplexBufferProvider; -import source.Source; -import source.SourceEvent; -import source.SourceException; -import source.tuner.channel.TunerChannel; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -public class PolyphaseChannelManager implements Listener -{ - private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelManager.class); - private static final int CHANNEL_BANDWIDTH = 12500; - private static final double CHANNEL_OVERSAMPLING = 2.0; - private static final int CHANNEL_SAMPLE_RATE = (int)(CHANNEL_BANDWIDTH * CHANNEL_OVERSAMPLING); - private static final int POLYPHASE_FILTER_TAPS_PER_CHANNEL = 17; - - private IComplexBufferProvider mComplexBufferProvider; - private List mChannelSources = new CopyOnWriteArrayList<>(); - private ChannelCalculator mChannelCalculator; - private ComplexPolyphaseChannelizerM2 mPolyphaseChannelizer; - private ChannelSourceEventListener mChannelSourceEventListener = new ChannelSourceEventListener(); - private ScheduledBufferProcessor mBufferProcessor; - - /** - * Polyphase Channel Manager is a DDC channel manager and complex buffer queue/processor for a tuner. This class - * provides DDC polyphase channel sources and wraps a polyphase channelizer processing sample buffers produced by - * the tuner and distributing channelized sample buffers to each allocated DDC polyphase channel source. This - * class is responsible for monitoring the tuner for changes in center frequency and/or sample rate and updating - * active DDC polyphase channel sources accordingly. This class also monitors source event requests and - * notifications received from active DDC polyphase channel sources to adjust sample streams as required. - * - * Note: add this channel manager as a source event listener to the complex buffer provider to ensure this manager - * adapts to changes in source frequency and sample rate. - * - * @param complexBufferProvider that supports register/deregister of this channel manager - * @param frequency of the provided complex buffer samples - * @param sampleRate of the provided complex buffer samples - */ - public PolyphaseChannelManager(IComplexBufferProvider complexBufferProvider, long frequency, int sampleRate) - { - if(complexBufferProvider == null) - { - throw new IllegalArgumentException("Complex buffer provider argument cannot be null"); - } - - mComplexBufferProvider = complexBufferProvider; - - //Ensure that sample rate is a multiple of channel sample rate since polyphase channelizer is M2 - if(sampleRate % (CHANNEL_SAMPLE_RATE) != 0) - { - throw new IllegalArgumentException("Tuner sample rate [" + sampleRate + "] must be an even multiple of " + - CHANNEL_SAMPLE_RATE + " Hz"); - } - - int channelCount = sampleRate / CHANNEL_BANDWIDTH; - - mChannelCalculator = new ChannelCalculator(sampleRate, channelCount, frequency, CHANNEL_OVERSAMPLING); - - mBufferProcessor = new ScheduledBufferProcessor(500, 100, 50, 50); - - updateChannelizer(sampleRate, null); - } - - /** - * Provides a Digital Drop Channel (DDC) for the specified tuner channel or returns null if the channel can't be - * sourced due to the current center frequency and/or sample rate. - * @param tunerChannel specifying center frequency and bandwidth. - * @return source or null. - */ - public Source getChannel(TunerChannel tunerChannel) - { - PolyphaseChannelSource channelSource = null; - - try - { - List polyphaseIndexes = mChannelCalculator.getChannelIndexes(tunerChannel); - - IPolyphaseChannelOutputProcessor outputProcessor = getOutputProcessor(polyphaseIndexes); - - if(outputProcessor != null) - { - channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener, - CHANNEL_SAMPLE_RATE); - - mChannelSources.add(channelSource); - } - } - catch(IllegalArgumentException iae) - { - mLog.info("Can't provide DDC for " + tunerChannel.toString() + " due to channelizer frequency [" + - mChannelCalculator.getCenterFrequency() + "] and sample rate [" + - (mChannelCalculator.getChannelCount() * mChannelCalculator.getChannelBandwidth()) + "]"); - } - - return channelSource; - } - - /** - * Creates a processor to process the channelizer channel indexes into a composite output stream providing - * channelized complex sample buffers to a registered source listener. - * @param indexes to target by the output processor - * @return output processor compatible with the number of indexes to monitor - */ - private IPolyphaseChannelOutputProcessor getOutputProcessor(List indexes) - { - switch(indexes.size()) - { - case 1: - return new OneChannelOutputProcessor(CHANNEL_SAMPLE_RATE, indexes); - case 2: - return new TwoChannelOutputProcessor(CHANNEL_SAMPLE_RATE, indexes); - default: - //TODO: create output processor for greater than 2 input channels - mLog.error("Request to create an output processor for unexpected channel index size:" + indexes.size()); - 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) - { - synchronized(mBufferProcessor) - { - mPolyphaseChannelizer.addChannel(channelSource); - - if(mPolyphaseChannelizer.getRegisteredChannelCount() == 1) - { - mComplexBufferProvider.addComplexBufferListener(mBufferProcessor); - mBufferProcessor.start(); - } - } - } - - /** - * Stops/removes the channel source from receiving channelized sample buffers and deregisters from the tuner - * when this is the last channel being sourced. - * - * @param channelSource to stop - */ - private void stopChannelSource(PolyphaseChannelSource channelSource) - { - synchronized(mBufferProcessor) - { - mChannelSources.remove(channelSource); - - if(mPolyphaseChannelizer.getRegisteredChannelCount() == 0) - { - mComplexBufferProvider.removeComplexBufferListener(mBufferProcessor); - mBufferProcessor.stop(); - } - } - } - - /** - * Implements the Listener interface for receiving notifications of frequency and sample rate - * changes from the tuner. - */ - @Override - public void receive(SourceEvent sourceEvent) - { - switch(sourceEvent.getEvent()) - { - case NOTIFICATION_FREQUENCY_CHANGE: - //Update the index calculator and each of the output processors to update each channel frequency - mChannelCalculator.setCenterFrequency(sourceEvent.getValue().longValue()); - updateOutputProcessors(sourceEvent); - break; - case NOTIFICATION_SAMPLE_RATE_CHANGE: - //Update the channelizer (and the output processors) for the new sample rate - updateChannelizer(sourceEvent.getValue().intValue(), sourceEvent); - break; - } - } - - /** - * Creates or updates the channelizer to process the incoming sample rate and updates any channel processors. - * - * @param sampleRate to use for the channelizer - * @param sourceEvent containing a notification of sample rate change (optional - can be null). This source - * event will be passed to all output processors to ensure the change is fully promulgated before restarting the - * buffer processor if it was already running. - */ - private void updateChannelizer(int sampleRate, SourceEvent sourceEvent) - { - mChannelCalculator.setSampleRate(sampleRate); - - //Lock on the buffer processor to block any channel create/start/stop requests while we configure - synchronized(mBufferProcessor) - { - //Get current processor state so we can restart it after we update the channelizer - boolean bufferProcessorWasRunning = mBufferProcessor.isRunning(); - - //Stop will be ignored if it is not currently running - mBufferProcessor.stop(); - - try - { - if(mPolyphaseChannelizer == null) - { - mPolyphaseChannelizer = new ComplexPolyphaseChannelizerM2(sampleRate, - POLYPHASE_FILTER_TAPS_PER_CHANNEL); - - mBufferProcessor.setListener(mPolyphaseChannelizer); - } - else - { - mPolyphaseChannelizer.setSampleRate(sampleRate); - } - } - catch(IllegalArgumentException iae) - { - mLog.error("Could not create polyphase channelizer for sample rate [" + sampleRate + "]", iae); - } - catch(FilterDesignException fde) - { - mLog.error("Could not create filter for polyphase channelizer for sample rate [" + sampleRate + "]", fde); - } - - if(sourceEvent != null && sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_SAMPLE_RATE_CHANGE) - { - updateOutputProcessors(sourceEvent); - } - - if(bufferProcessorWasRunning) - { - mBufferProcessor.start(); - } - } - } - - /** - * Updates each of the output processors for any changes in the tuner's center frequency or sample rate, which - * would cause the output processors to change the polyphase channelizer results channel(s) that the processor is - * consuming - * - * @param sourceEvent that requires an update to the output processors - */ - private void updateOutputProcessors(SourceEvent sourceEvent) - { - for(PolyphaseChannelSource channelSource: mChannelSources) - { - updateOutputProcessor(channelSource); - - //Notify the channel source that the frequency or sample rate has changed so that it can reset - //any frequency correction value. - if(sourceEvent != null) - { - try - { - channelSource.process(sourceEvent); - } - catch(SourceException se) - { - mLog.error("Error while notifying polyphase channel source of a source event", se); - } - } - } - } - - /** - * Updates the polyphase channel source's output processor due to a change in the center frequency or sample - * rate for the source providing sample buffers to the polyphase channelizer, or whenever the DDC channel's - * center tuned frequency changes. - * - * @param channelSource that requires an update to its output processor - */ - private void updateOutputProcessor(PolyphaseChannelSource channelSource) - { - try - { - List indexes = mChannelCalculator.getChannelIndexes(channelSource.getTunerChannel()); - - long centerFrequency = mChannelCalculator.getCenterFrequencyForIndexes(indexes); - - //If the indexes size is the same then update the current processor, otherwise create a new one - if(channelSource.getPolyphaseChannelOutputProcessor().getInputChannelCount() == indexes.size()) - { - channelSource.getPolyphaseChannelOutputProcessor().setPolyphaseChannelIndices(indexes); - channelSource.setFrequency(centerFrequency); - } - else - { - channelSource.setPolyphaseChannelOutputProcessor(getOutputProcessor(indexes), centerFrequency); - } - } - catch(IllegalArgumentException iae) - { - mLog.error("Error updating polyphase channel source - can't determine output channel indexes for " + - "updated tuner center frequency and sample rate. Stopping channel source", iae); - - stopChannelSource(channelSource); - } - } - - /** - * Internal class for handling requests for start/stop sample stream from polyphase channel sources - */ - private class ChannelSourceEventListener implements Listener - { - @Override - public void receive(SourceEvent sourceEvent) - { - switch(sourceEvent.getEvent()) - { - case REQUEST_START_SAMPLE_STREAM: - if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource) - { - startChannelSource((PolyphaseChannelSource)sourceEvent.getSource()); - } - else - { - mLog.error("Request to start sample stream for unrecognized source: " + - (sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source")); - } - break; - case REQUEST_STOP_SAMPLE_STREAM: - if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource) - { - stopChannelSource((PolyphaseChannelSource)sourceEvent.getSource()); - } - else - { - mLog.error("Request to stop sample stream for unrecognized source: " + - (sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source")); - } - break; - default: - mLog.error("Received unrecognized source event from polyphase channel source [" + - sourceEvent.getEvent() + "]"); - } - } - } -} diff --git a/src/dsp/filter/channelizer/PolyphaseChannelSource.java b/src/dsp/filter/channelizer/PolyphaseChannelSource.java deleted file mode 100644 index 6fa882f7..00000000 --- a/src/dsp/filter/channelizer/PolyphaseChannelSource.java +++ /dev/null @@ -1,225 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package dsp.filter.channelizer; - -import dsp.filter.channelizer.output.IPolyphaseChannelOutputProcessor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.complex.ComplexBuffer; -import sample.complex.ComplexToComplexBufferAssembler; -import sample.real.IOverflowListener; -import source.SourceEvent; -import source.tuner.channel.TunerChannel; -import source.tuner.channel.TunerChannelSource; - -public class PolyphaseChannelSource extends TunerChannelSource -{ - private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class); - - private ComplexToComplexBufferAssembler mComplexBufferAssembler = new ComplexToComplexBufferAssembler(2500); - private IPolyphaseChannelOutputProcessor mPolyphaseChannelOutputProcessor; - private double mChannelSampleRate; - private long mCenterFrequency; - private long mFrequencyCorrection; - - /** - * Polyphase channelizer tuner channel source 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 describing the desired channel frequency and bandwidth/minimum sample rate - * @param outputProcessor - to process polyphase channelizer channel results into a channel stream - * @param producerSourceEventListener to receive source event requests (e.g. start/stop sample stream) - * @param channelSampleRate for the downstream sample output - */ - public PolyphaseChannelSource(TunerChannel tunerChannel, IPolyphaseChannelOutputProcessor outputProcessor, - Listener producerSourceEventListener, double channelSampleRate) - { - super(producerSourceEventListener, tunerChannel); - mPolyphaseChannelOutputProcessor = outputProcessor; - mChannelSampleRate = channelSampleRate; - } - - /** - * Registers the listener to receive complex sample buffers from this channel source - */ - @Override - public void setListener(final Listener listener) - { - mComplexBufferAssembler.setListener(listener); - } - - /** - * Removes the listener from receiving complex sample buffers from this channel source - */ - @Override - public void removeListener(Listener listener) - { - mComplexBufferAssembler.setListener(null); - } - - /** - * Channel output processor used by this channel source to convert polyphase channel results into a specific - * channel complex buffer output stream. - */ - public IPolyphaseChannelOutputProcessor getPolyphaseChannelOutputProcessor() - { - return mPolyphaseChannelOutputProcessor; - } - - /** - * Sets/updates the output processor for this channel source, replacing the existing output processor. - * - * @param outputProcessor to use - * @param frequency center for the channels processed by the output processor - */ - public void setPolyphaseChannelOutputProcessor(IPolyphaseChannelOutputProcessor outputProcessor, long frequency) - { - //Lock on the channel output processor to block the channel results processor thread from servicing the - //channel output processor's buffer while we change out the processors. - synchronized(mPolyphaseChannelOutputProcessor) - { - IPolyphaseChannelOutputProcessor existingProcessor = mPolyphaseChannelOutputProcessor; - - //Swap out the processor so that incoming samples can accumulate in the new channel output processor - mPolyphaseChannelOutputProcessor = outputProcessor; - - //Remove the buffer overflow listener and clear any previous buffer overflow state - existingProcessor.setSourceOverflowListener(null); - broadcastOverflowState(false); - - //Register to receive buffer overflow notifications so we can push them up through the source chain - mPolyphaseChannelOutputProcessor.setSourceOverflowListener(this); - - //Fully process the residual channel results buffer of the previous channel output processor - if(existingProcessor != null) - { - existingProcessor.processChannelResults(mComplexBufferAssembler); - } - - //Finally, setup the frequency offset for the output processor and reset the frequency correction value - //to allow consumers to adjust and calculate a new frequency correction value, if needed. - mCenterFrequency = frequency; - mFrequencyCorrection = 0; - mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset()); - broadcastConsumerSourceEvent(SourceEvent.frequencyCorrectionChange(mFrequencyCorrection)); - } - } - - /** - * Primary method for receiving channel results output from a polyphase channelizer. The results array will be - * queued for processing to extract the target channel samples, process them for frequency correction and/or - * channel aggregation, and dispatch the results to the downstream sample listener/consumer. - * - * @param channelResultsBuffer containing the polyphase channelizer output for a single complex sample period. - */ - public void receiveChannelResults(float[] channelResultsBuffer) - { - mPolyphaseChannelOutputProcessor.receiveChannelResults(channelResultsBuffer); - } - - /** - * Downstream channel sample rate - * - * @return sample rate in Hertz - */ - @Override - public double getSampleRate() - { - return mChannelSampleRate; - } - - /** - * Sets the downstream channel sample rate - * @param sampleRate in hertz - */ - @Override - protected void setSampleRate(double sampleRate) - { - mChannelSampleRate = sampleRate; - } - - @Override - public void dispose() - { - mLog.info("Polyphase channel source received request to dispose"); - } - - @Override - public long getFrequencyCorrection() - { - return mFrequencyCorrection; - } - - /** - * Adjusts the frequency correction value that is being applied to the channelized output stream by the - * polyphase channel output processor. - * - * @param value to apply for frequency correction in hertz - */ - protected void setFrequencyCorrection(long value) - { - mFrequencyCorrection = value; - updateFrequencyOffset(); - broadcastConsumerSourceEvent(SourceEvent.frequencyCorrectionChange(mFrequencyCorrection)); - } - - /** - * Sets the center frequency for the incoming sample stream channel results and resets frequency correction to zero. - * @param frequency in hertz - */ - @Override - protected void setFrequency(long frequency) - { - mCenterFrequency = frequency; - setFrequencyCorrection(0); - } - - @Override - protected void processSamples() - { - //Lock on the output processor so that it can't be changed out in the middle of processing - synchronized(mPolyphaseChannelOutputProcessor) - { - mPolyphaseChannelOutputProcessor.processChannelResults(mComplexBufferAssembler); - } - } - - /** - * Calculates the frequency offset required to mix the incoming signal to the desired output baseband - */ - private long getFrequencyOffset() - { - return getTunerChannel().getFrequency() - mCenterFrequency + mFrequencyCorrection; - } - - /** - * Updates the frequency offset being applied by the output processor. Calculates the offset using the center - * frequency of the incoming polyphase channel results and the desired output channel frequency adjusted by any - * requested frequency correction value. - */ - private void updateFrequencyOffset() - { - synchronized(mPolyphaseChannelOutputProcessor) - { - mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset()); - } - } -} diff --git a/src/dsp/filter/channelizer/ScheduledBufferProcessor.java b/src/dsp/filter/channelizer/ScheduledBufferProcessor.java deleted file mode 100644 index adc7ac9b..00000000 --- a/src/dsp/filter/channelizer/ScheduledBufferProcessor.java +++ /dev/null @@ -1,171 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package dsp.filter.channelizer; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.OverflowableTransferQueue; -import sample.real.IOverflowListener; -import util.ThreadPool; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -public class ScheduledBufferProcessor implements Listener -{ - private final static Logger mLog = LoggerFactory.getLogger(ScheduledBufferProcessor.class); - - private OverflowableTransferQueue mQueue; - private Listener mListener; - private ScheduledFuture mScheduledFuture; - private long mDistributionInterval; - private int mMaxBuffersPerInterval; - private AtomicBoolean mRunning = new AtomicBoolean(); - - /** - * Scheduled Buffer Processor combines an internal overflowable buffer with a scheduled runnable processing task - * for periodically distributing internally queued elements to the registered listener. This processor provides - * a convenient way to create a thread-safe buffer for receiving elements from one thread/runnable and then - * distributing those elements to a registered listener where distribution occurs on a separate scheduled thread - * pool runnable thread. This allows the calling input thread to quickly return without incurring any subsequent - * processing workload. - * - * The internal queue is an overflowable queue implementation that allows a listener to be registered to receive - * notifications of overflow and reset state. Queue sizing parameters are specified in the constructor. - * - * @param maximumSize of the internal queue (overflow happens when this is exceeded) - * @param resetThreshold of the internal queue (overflow reset happens once queue size falls below this threshold - * @param distributionInterval in milliseconds specifying how often the internal scheduled thread pool buffer - * distributor should run - * @param maxBuffersPerInterval maximum number of buffers to pull from the internal queue during each distribution - * interval - */ - public ScheduledBufferProcessor(int maximumSize, int resetThreshold, long distributionInterval, int maxBuffersPerInterval) - { - mQueue = new OverflowableTransferQueue<>(maximumSize, resetThreshold); - - mQueue.setOverflowListener(new IOverflowListener() - { - @Override - public void sourceOverflow(boolean overflow) - { - mLog.error("Overflow state changed - overflow:" + overflow); - } - }); - mDistributionInterval = distributionInterval; - mMaxBuffersPerInterval = maxBuffersPerInterval; - } - - /** - * Sets the listener to receive notifications of buffer overflow and/or reset. Note: this method can also be - * invoked with a null argument to clear the previously registered overflow listener. - * - * @param listener to receive overflow/reset notifications. - */ - public void setOverflowListener(IOverflowListener listener) - { - mQueue.setOverflowListener(listener); - } - - /** - * Sets or changes the listener to receive buffers from this processor. - * @param listener to receive buffers - */ - public void setListener(Listener listener) - { - mListener = listener; - } - - /** - * Primary input method for adding buffers to this processor. Note: incoming buffers will be ignored if this - * processor is in a stopped state. You must invoke start() to allow incoming buffers and initiate buffer - * processing. - * - * @param e to enqueue for distribution to a registered listener - */ - @Override - public void receive(E e) - { - mQueue.offer(e); - } - - /** - * Starts this buffer processor and allows queuing of incoming buffers. - */ - public void start() - { - if(mRunning.compareAndSet(false, true)) - { - mScheduledFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new Processor(), 0, mDistributionInterval, TimeUnit.MILLISECONDS); - } - } - - /** - * Stops this buffer processor, stops queuing of incoming buffers and clears the internal buffer queue. - */ - public void stop() - { - if(mRunning.compareAndSet(true, false)) - { - if(mScheduledFuture != null) - { - mScheduledFuture.cancel(true); - mScheduledFuture = null; - } - - mQueue.clear(); - } - } - - /** - * Indicates if this processor is currently running - */ - public boolean isRunning() - { - return mRunning.get(); - } - - /** - * Processor to service the buffer queue and distribute the buffers to the registered listener - */ - class Processor implements Runnable - { - private List mBuffers = new ArrayList<>(); - - @Override - public void run() - { - mQueue.drainTo(mBuffers, mMaxBuffersPerInterval); - - if(mListener != null) - { - for(E buffer: mBuffers) - { - mListener.receive(buffer); - } - } - - mBuffers.clear(); - } - } -} diff --git a/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java deleted file mode 100644 index c1504f7f..00000000 --- a/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java +++ /dev/null @@ -1,170 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package dsp.filter.channelizer.output; - -import dsp.mixer.Oscillator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.OverflowableTransferQueue; -import sample.complex.Complex; -import sample.complex.ComplexSampleListener; -import sample.real.IOverflowListener; -import source.Source; - -import java.util.ArrayList; -import java.util.List; - -public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputProcessor -{ - private final static Logger mLog = LoggerFactory.getLogger(ChannelOutputProcessor.class); - - private OverflowableTransferQueue mChannelResultsQueue; - private List mChannelResultsToProcess = new ArrayList<>(); - private int mMaxResultsToProcess; - - private int mInputChannelCount; -//TODO: swap this out and use the LowPhaseNoiseOscillator - private Oscillator mFrequencyCorrectionMixer; - private boolean mFrequencyCorrectionEnabled; - - /** - * Base class for polyphase channelizer output channel processing. Provides built-in frequency translation - * oscillator support to apply frequency correction to the channel sample stream as requested by sample consumer. - * - * @param inputChannelCount is the number of input channels for this output processor - * @param sampleRate of the output channel. This is used to match the oscillator's sample rate to the output - * channel sample rate for frequency translation/correction. - */ - public ChannelOutputProcessor(int inputChannelCount, double sampleRate) - { - mInputChannelCount = inputChannelCount; - mFrequencyCorrectionMixer = new Oscillator(0, sampleRate); - mMaxResultsToProcess = (int)(sampleRate / 10) * 2; //process at 100 millis interval, twice the expected inflow rate - - mChannelResultsQueue = new OverflowableTransferQueue<>((int)(sampleRate * 3), (int)(sampleRate * 0.5)); - } - - /** - * Sets the frequency offset to apply to the incoming samples to mix the desired signal to baseband. - * @param frequencyOffset in hertz - */ - @Override - public void setFrequencyOffset(long frequencyOffset) - { - mFrequencyCorrectionMixer.setFrequency(frequencyOffset); - mFrequencyCorrectionEnabled = (frequencyOffset != 0); - } - - @Override - public void receiveChannelResults(float[] channelResults) - { - mChannelResultsQueue.offer(channelResults); - } - - /** - * Processes all enqueued polyphase channelizer results until the internal queue is empty - * @param listener to receive the processed channel results - */ - @Override - public void processChannelResults(ComplexSampleListener listener) - { - try - { - int count = mChannelResultsQueue.drainTo(mChannelResultsToProcess, mMaxResultsToProcess); - - if(count > 0) - { - process(mChannelResultsToProcess, listener); - } - } - catch(Throwable throwable) - { - mLog.error("Error while processing polyphase channel samples", throwable); - } - - mChannelResultsToProcess.clear(); - } - - /** - * Sub-class implementation to process one polyphase channelizer result array. - * @param channelResults to process - * @param listener to receive the channel complex sample - */ - public abstract void process(List channelResults, ComplexSampleListener listener); - - - /** - * Sets the overflow listener to monitor the internal channelizer channel results queue overflow state - */ - public void setSourceOverflowListener(Source source) - { - mChannelResultsQueue.setSourceOverflowListener(source); - } - - /** - * Removes the overflow listener from monitoring the internal channelizer channel results queue overflow state - */ - public void removeOverflowListener(IOverflowListener listener) - { - mChannelResultsQueue.setOverflowListener(null); - } - - @Override - public int getInputChannelCount() - { - return mInputChannelCount; - } - - /** - * Provides frequency correction of the inphase component or simply returns the inphase argument value when no - * frequency correction value has been specified. - * - * @param inphase value of the sample to translate/correct - * @param quadrature value of the sample to translate/correct - * @return corrected inphase value - */ - protected float getFrequencyCorrectedInphase(float inphase, float quadrature) - { - return Complex.multiplyInphase(inphase, quadrature, mFrequencyCorrectionMixer.inphase(), - mFrequencyCorrectionMixer.quadrature()); - } - - /** - * Provides frequency correction of the quadrature component or simply returns the quadrature argument value when no - * frequency correction value has been applied, - * - * @param inphase value of the sample to translate/correct - * @param quadrature value of the sample to translate/correct - * @return translated inphase value - */ - protected float getFrequencyCorrectedQuadrature(float inphase, float quadrature) - { - return Complex.multiplyQuadrature(inphase, quadrature, mFrequencyCorrectionMixer.inphase(), - mFrequencyCorrectionMixer.quadrature()); - } - - /** - * Indicates if this channel has a frequency correction offset - */ - public boolean hasFrequencyCorrection() - { - return mFrequencyCorrectionEnabled; - } - -} diff --git a/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java deleted file mode 100644 index e285fd70..00000000 --- a/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package dsp.filter.channelizer.output; - -import sample.complex.ComplexSampleListener; -import sample.real.IOverflowListener; -import source.Source; - -import java.util.List; - -public interface IPolyphaseChannelOutputProcessor -{ - /** - * Receive and enqueue output results from the polyphase analysis channelizer - * @param channelResults to enqueue - */ - void receiveChannelResults(float[] channelResults); - - /** - * Process the channel output channel results queue and deliver the output to the listener - * - * @param listener to receive the processed channel results - */ - void processChannelResults(ComplexSampleListener listener); - - /** - * Sets the desired frequency offset from center. The samples will be mixed with an oscillator set to this offset - * frequency to produce an output where the desired signal is centered in the passband. - * - * @param frequency in hertz - */ - void setFrequencyOffset(long frequency); - - /** - * Indicates the number of input channels processed by this output processor - * @return - */ - int getInputChannelCount(); - - /** - * Updates the input polyphase channel index(es) used by this output processor - * @param indexes - */ - void setPolyphaseChannelIndices(List indexes); - - /** - * Sets the listener to receive buffer overflow notifications - * @param source to receive overflow notifications - */ - void setSourceOverflowListener(Source source); -} diff --git a/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java deleted file mode 100644 index aa3e7503..00000000 --- a/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java +++ /dev/null @@ -1,97 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package dsp.filter.channelizer.output; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.complex.ComplexSampleListener; - -import java.util.Arrays; -import java.util.List; - -public class OneChannelOutputProcessor extends ChannelOutputProcessor -{ - private final static Logger mLog = LoggerFactory.getLogger(OneChannelOutputProcessor.class); - - private int mChannelOffset; - - /** - * Processor to extract a single channel from a polyphase channelizer and produce an output I/Q complex sample - * from each polyphase channelizer output results array. - * - * @param sampleRate of the output sample stream. - * @param channelIndexes containing a single channel index. - */ - public OneChannelOutputProcessor(double sampleRate, List channelIndexes) - { - super(1, sampleRate); - setPolyphaseChannelIndices(channelIndexes); - mLog.debug("Sample Rate:" + sampleRate + " PolyPhase Channel Indexes:" + channelIndexes); - } - - /** - * Updates this processor to extract a single, specified channel index. - * @param indexes containing a single channel index value. - * @throws IllegalArgumentException if the list of indexes does not contain a single channel index. - */ - public void setPolyphaseChannelIndices(List indexes) - { - if(indexes.size() != 1) - { - throw new IllegalArgumentException("Single channel output processor requires a single index to " + - "process - provided indexes " + indexes.toString()); - } - - //Set the channelized output results offset to twice the channel index to account for each channel having - //an I/Q pair - mChannelOffset = indexes.get(0) * 2; - } - - /** - * Extract the channel from the channel results array, apply frequency translation, and deliver the - * extracted frequency-corrected channel I/Q sample set to the complex sample listener. - * - * @param channelResults to process containing a list of channel array of I/Q sample pairs (I0,Q0,I1,Q1...In,Qn) - * @param listener to receive the extracted, frequency-translated channel results - */ - @Override - public void process(List channelResults, ComplexSampleListener listener) - { - for(float[] channels: channelResults) - { - if(channels.length < mChannelOffset + 1) - { - throw new IllegalArgumentException("Polyphase channelizer output channels array is not large enough to " + - "cover this single channel output processor with channel offset [" + mChannelOffset + "]"); - } - - float i = channels[mChannelOffset]; - float q = channels[mChannelOffset + 1]; - - if(hasFrequencyCorrection()) - { - listener.receive(getFrequencyCorrectedInphase(i, q), getFrequencyCorrectedQuadrature(i, q)); - } - else - { - listener.receive(i, q); - } - } - } -} diff --git a/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java deleted file mode 100644 index ba0c3e15..00000000 --- a/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java +++ /dev/null @@ -1,91 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package dsp.filter.channelizer.output; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.complex.ComplexSampleListener; - -import java.util.List; - -public class TwoChannelOutputProcessor extends ChannelOutputProcessor -{ - private final static Logger mLog = LoggerFactory.getLogger(TwoChannelOutputProcessor.class); - - private int mChannelOffset1; - private int mChannelOffset2; - - /** - * Processor to extract two channels from a polyphase channelizer, synthesize/recombine the channels, apply - * frequency translation and frequency correction, down-sample by a factor of two and output an I/Q complex sample. - * - * @param sampleRate of the output sample stream. - * @param channelIndexes containing two channel indices. - */ - public TwoChannelOutputProcessor(int sampleRate, List channelIndexes) - { - //Set the frequency correction oscillator to 2 x output sample rate since we'll be correcting the frequency - //after synthesizing both input channels - super(2, sampleRate * 2); - setPolyphaseChannelIndices(channelIndexes); - } - - /** - * Updates this processor to extract the two specified channel indexes. - * - * @param indexes containing a single channel index value. - * @throws IllegalArgumentException if the list of indexes does not contain a single channel index. - */ - public void setPolyphaseChannelIndices(List indexes) - { - if(indexes.size() != 2) - { - throw new IllegalArgumentException("Double channel output processor requires two indexes to " + - "process - provided indexes " + indexes.toString()); - } - - //Set the channelized output results offsets to twice the channel index to account for each channel having - //an I/Q pair - mChannelOffset1 = indexes.get(0) * 2; - mChannelOffset2 = indexes.get(1) * 2; - } - - /** - * Extract the channel from the channel results array, apply frequency translation, and deliver the - * extracted frequency-corrected channel I/Q sample set to the complex sample listener. - * - * @param channelResults to process containing an array of channel I/Q sample pairs (I0,Q0,I1,Q1...In,Qn) - * @param listener to receive the extracted, frequency-translated channel results - */ - @Override - public void process(List channelResults, ComplexSampleListener listener) - { - mLog.debug("I got channel results - doing nothing !!"); -// if(channels.length < mChannelOffset + 1) -// { -// throw new IllegalArgumentException("Polyphase channelizer output channels array is not large enough to " + -// "cover this single channel output processor with channel offset [" + mChannelOffset + "]"); -// } -// -// float i = channels[mChannelOffset]; -// float q = channels[mChannelOffset + 1]; - -// listener.receive(getFrequencyCorrectedInphase(i, q), getFrequencyCorrectedQuadrature(i, q)); - } -} diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index 516d71c8..fb4a8722 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -611,8 +611,7 @@ public class ProcessingChain implements IChannelEventListener } /** - * Stops processing if the chain is currently processing. Invocations on - * an already stopped chain have no effect. + * Stops processing if the chain is currently processing. Invocations on an already stopped chain have no effect. */ public void stop() { @@ -639,15 +638,14 @@ public class ProcessingChain implements IChannelEventListener ((RealSource) mSource).removeListener(mFilteredRealBufferBroadcaster); break; default: - throw new IllegalArgumentException("Unrecognized source " - + "sample type - cannot start processing chain"); + throw new IllegalArgumentException("Unrecognized source sample type - cannot start processing " + + "chain"); } /* Release the source */ mSource.dispose(); mSource = null; } - } } diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index 13370fb3..ee475f4e 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -22,18 +22,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; -import sample.complex.ComplexBuffer; -import sample.complex.IComplexBufferProvider; import source.ISourceEventProcessor; import source.SourceEvent; -import source.SourceException; -import source.tuner.manager.AbstractSourceManager; -import source.tuner.manager.TunerSourceManager; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.RejectedExecutionException; /** * Tuner - provides tuner channel sources, representing a channel frequency @@ -45,7 +35,6 @@ public abstract class Tuner protected Broadcaster mTunerEventBroadcaster = new Broadcaster<>(); private TunerController mTunerController; private String mName; - private AbstractSourceManager mTunerSourceManager; /** * Abstract tuner class. @@ -57,8 +46,6 @@ public abstract class Tuner mName = name; mTunerController = tunerController; - mTunerSourceManager = new TunerSourceManager(mTunerController); - //Rebroadcast frequency and sample rate change events as tuner events mTunerController.addListener(new ISourceEventProcessor() { @@ -80,14 +67,6 @@ public abstract class Tuner }); } - /** - * Source Manager. Provides access to registering for complex buffer samples and source event notifications. - */ - public AbstractSourceManager getSourceManager() - { - return mTunerSourceManager; - } - public TunerController getTunerController() { return mTunerController; diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index 70dee706..24043844 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -21,25 +21,23 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; -import sample.complex.Complex; import sample.complex.ComplexBuffer; import sample.complex.IComplexBufferProvider; import source.ISourceEventListener; import source.ISourceEventProcessor; import source.SourceEvent; -import source.SourceEvent.Event; import source.SourceEventListenerToProcessorAdapter; import source.SourceException; -import source.tuner.channel.CICTunerChannelSource; +import source.tuner.channel.ChannelManager; +import source.tuner.channel.cic.CICTunerChannelSource; import source.tuner.channel.TunerChannel; -import source.tuner.channel.TunerChannelSource; +import source.tuner.channel.polyphase.PolyphaseChannelManager; import source.tuner.configuration.TunerConfiguration; -import source.tuner.frequency.FrequencyController; -import source.tuner.frequency.FrequencyController.Tunable; +import source.tuner.channel.FrequencyController; +import source.tuner.channel.FrequencyController.Tunable; import java.util.SortedSet; import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.RejectedExecutionException; public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener, IComplexBufferProvider, Listener @@ -49,9 +47,8 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, /* List of currently tuned channels being served to demod channels */ private SortedSet mTunedChannels = new ConcurrentSkipListSet<>(); - protected FrequencyController mFrequencyController; - private int mMiddleUnusable; - private double mUsableBandwidthPercentage; + private FrequencyController mFrequencyController; + private ChannelManager mChannelManager; private Listener mSourceEventListener; /** @@ -65,14 +62,32 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, * @throws SourceException - for any issues related to constructing the * class, tuning a frequency, or setting the bandwidth */ - public TunerController(long minimumFrequency, long maximumFrequency, int middleUnusable, double usableBandwidth) + public TunerController(long minimumFrequency, long maximumFrequency, long middleUnusable, double usableBandwidth) { - mFrequencyController = new FrequencyController(this, minimumFrequency, maximumFrequency, 0.0d); - mMiddleUnusable = middleUnusable; - mUsableBandwidthPercentage = usableBandwidth; + mFrequencyController = new FrequencyController(this, minimumFrequency, maximumFrequency, + 0.0d, middleUnusable, usableBandwidth); + + mChannelManager = new PolyphaseChannelManager(mFrequencyController, this); + mSourceEventListener = new SourceEventListenerToProcessorAdapter(this); } + /** + * Frequency controller for managing frequency and sample rate. + */ + public FrequencyController getFrequencyController() + { + return mFrequencyController; + } + + /** + * Channel manager for managing Digital Drop Chanel (DDC) tuner channel sources + */ + public ChannelManager getChannelManager() + { + return mChannelManager; + } + /** * Implements the ISourceEventListener interface to receive requests from sample consumers */ @@ -96,18 +111,18 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, switch(sourceEvent.getEvent()) { case REQUEST_FREQUENCY_CHANGE: - setFrequency( sourceEvent.getValue().longValue() ); + getFrequencyController().setFrequency( sourceEvent.getValue().longValue() ); break; case REQUEST_START_SAMPLE_STREAM: - if(sourceEvent.getSource() instanceof Listener) + if(sourceEvent.getSource() instanceof CICTunerChannelSource) { - addComplexBufferListener((Listener)sourceEvent.getSource()); + addComplexBufferListener((CICTunerChannelSource)sourceEvent.getSource()); } break; case REQUEST_STOP_SAMPLE_STREAM: - if(sourceEvent.getSource() instanceof Listener) + if(sourceEvent.getSource() instanceof CICTunerChannelSource) { - removeComplexBufferListener((Listener)sourceEvent.getSource()); + removeComplexBufferListener((CICTunerChannelSource)sourceEvent.getSource()); } break; default: @@ -115,285 +130,10 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, sourceEvent.getSource().getClass() + "]" ); } } - - public int getBandwidth() - { - return mFrequencyController.getBandwidth(); - } - - /** - * Sets the center frequency of the local oscillator. - * - * @param frequency in hertz - * @throws SourceException - if the tuner has any issues - */ - public void setFrequency(long frequency) throws SourceException - { - mFrequencyController.setFrequency(frequency); - } - - /** - * Gets the center frequency of the local oscillator - * - * @return frequency in hertz - */ - public long getFrequency() - { - return mFrequencyController.getFrequency(); - } - @Override public boolean canTune(long frequency) { - return mFrequencyController.canTune(frequency); - } - - public double getSampleRate() - { - return mFrequencyController.getSampleRate(); - } - - public double getFrequencyCorrection() - { - return mFrequencyController.getFrequencyCorrection(); - } - - public void setFrequencyCorrection(double correction) throws SourceException - { - mFrequencyController.setFrequencyCorrection(correction); - } - - public long getMinFrequency() - { - return mFrequencyController.getMinimumFrequency(); - } - - public long getMaxFrequency() - { - return mFrequencyController.getMaximumFrequency(); - } - - private long getMinTunedFrequency() throws SourceException - { - return mFrequencyController.getFrequency() - (getUsableBandwidth() / 2); - } - - private long getMaxTunedFrequency() throws SourceException - { - return mFrequencyController.getFrequency() + (getUsableBandwidth() / 2); - } - - /** - * Indicates if channel along with all of the other currently sourced - * channels can fit within the tunable bandwidth. - */ - private boolean canTune(TunerChannel channel) - { - //Make sure we're within the tunable frequency range of this tuner - if(getMinFrequency() < channel.getMinFrequency() && getMaxFrequency() > channel.getMaxFrequency()) - { - //If this is the first lock, then we're good - if(mTunedChannels.isEmpty()) - { - return true; - } - else - { - int usableBandwidth = getUsableBandwidth(); - long minLockedFrequency = mTunedChannels.first().getMinFrequency(); - long maxLockedFrequency = mTunedChannels.last().getMaxFrequency(); - - //Requested channel is within current locked channel frequency range - if(minLockedFrequency <= channel.getMinFrequency() && channel.getMaxFrequency() <= maxLockedFrequency) - { - return true; - } - - //Requested channel is higher than min locked frequency - if(channel.getMaxFrequency() > minLockedFrequency && - channel.getMaxFrequency() - minLockedFrequency <= usableBandwidth) - { - return true; - } - - //Requested channel is lower than the max locked frequency - if(channel.getMinFrequency() <= maxLockedFrequency && - maxLockedFrequency - channel.getMinFrequency() <= usableBandwidth) - { - return true; - } - } - } - - return false; - } - - /** - * Constructs a digital drop channel (DDC) as a tuner channel from from the tuner, or returns null if the channel - * cannot be sourced from the tuner. - * - * This controller can't provide a channel if the channel, along with the current set of sourced channels, can't be - * accomodated within the current tuner bandwidth, or if a center frequency cannot be calculated that will ensure - * all of the channels can be accomodated and any defined central DC spike avoided. - * - * @param tuner to source the channel from - * @param channel with defined center frequency and bandwidth - * @return fully constructed tuner channel or null - * @throws RejectedExecutionException if the decimation processor has an error - */ - public TunerChannelSource getChannel(Tuner tuner, TunerChannel channel) throws RejectedExecutionException - { - TunerChannelSource source = null; - - if(canTune(channel)) - { - try - { - mTunedChannels.add(channel); - - if(requiresLOUpdate(channel)) - { - updateLOFrequency(); - } - - source = new CICTunerChannelSource(tuner, channel); - } - catch(SourceException se) - { - mTunedChannels.remove(channel); - source = null; - } - } - - return source; - } - - public int getChannelCount() - { - return mTunedChannels.size(); - } - - /** - * Indicates if the tuner's LO frequency must be updated in order to accommodate the tuner channel - */ - private boolean requiresLOUpdate(TunerChannel channel) throws SourceException - { - boolean isBelow = channel.getMinFrequency() < getMinTunedFrequency(); - boolean isAbove = channel.getMaxFrequency() > getMaxTunedFrequency(); - boolean overlapsCenter = mMiddleUnusable > 0 && - channel.overlaps(getFrequency() - mMiddleUnusable, getFrequency() + mMiddleUnusable); - - return isAbove || isBelow || overlapsCenter; - } - - /** - * Releases the currently sourced tuner channel from this tuner and shuts down the tuner if no other sources exist. - */ - public void releaseChannel(TunerChannelSource tunerChannelSource) - { - if(tunerChannelSource != null) - { - mTunedChannels.remove(tunerChannelSource.getTunerChannel()); - } - } - - /** - * Sets the Local Oscillator frequency to accomodate the current set of tuned channels. - * - * If there is only a single tuned channel, it is placed immediately to the right of the usable bandwidth right - * of the central DC spike. - * - * Otherwise, it places the highest channel frequency at the upper end of the tuner bandwidth, and then iteratively - * moves the center frequency higher until all channels fit within the bandwidth and none of the channels overlap - * any defined central DC spike unusable region. If a center tune frequency cannot be calculated, throw an - * exception so that the most recently added channel can be removed. - * - * Note: the tuned frequency is not changed until a legitimate new frequency can be calculated. If an exception - * is thrown, the current frequency is retained, so that the recently added channel can be removed and all other - * channels can continue as previously arranged. - * - * @throws SourceException if the set of tuner channels, including a recently added channel, cannot be tuned - * within the current tuner bandwidth and any central DC spike unusable region. - */ - private void updateLOFrequency() throws SourceException - { - long candidateFrequency; - - boolean isValidCandidateFrequency = true; - - //If there is only 1 channel set the center frequency so that the channel is positioned to the right of center - if(mTunedChannels.size() == 1) - { - candidateFrequency = mTunedChannels.first().getMinFrequency() - mMiddleUnusable + 1; - } - else - { - long minLockedFrequency = mTunedChannels.first().getMinFrequency(); - long maxLockedFrequency = mTunedChannels.last().getMaxFrequency(); - - //Start by placing the highest frequency channel at the high end of the spectrum - candidateFrequency = maxLockedFrequency - (getUsableBandwidth() / 2); - - //Iterate the channels and make sure that none of them overlap the center DC spike buffer, if one exists - if(mMiddleUnusable > 0) - { - boolean processingRequired = true; - - while(isValidCandidateFrequency && processingRequired) - { - processingRequired = false; - - long minAvoid = candidateFrequency - mMiddleUnusable; - long maxAvoid = candidateFrequency + mMiddleUnusable; - - //If any of the center channel(s) overlap the central DC spike avoid area, we'll iteratively - //increase the tuned frequency causing the set of channels to move left in the tuned bandwidth until - //we either find a good center tune frequency, or we walk the lowest frequency channel out of the - //minimum tuned range, in which case we'll throw an exception indicating we don't have a solution. - for(TunerChannel channel : mTunedChannels) - { - if(channel.overlaps(minAvoid, maxAvoid)) - { - //Calculate a tuned frequency adjustment that places this overlapping channel just to the - //left of the central DC spike avoid zone - long adjustment = channel.getMaxFrequency() - minAvoid + 1; - - //If the candidate frequency doesn't push the lowest channel out of bounds, make adjustment - if(candidateFrequency + adjustment - (getUsableBandwidth() / 2) <= minLockedFrequency) - { - candidateFrequency += adjustment; - processingRequired = true; - } - //Otherwise, punt and indicate that we can't find a center frequency or add the channel - else - { - isValidCandidateFrequency = false; - } - - //break out of the for/each loop, so that we can start over again with all of the channels - break; - } - } - } - } - } - - if(isValidCandidateFrequency) - { - mFrequencyController.setFrequency(candidateFrequency); - } - else - { - throw new SourceException("Couldn't calculate viable center frequency from set of tuner channels"); - } - } - - /** - * Usable bandwidth - total bandwidth minus the unusable space at either end of the spectrum. - */ - private int getUsableBandwidth() - { - return (int)(getBandwidth() * mUsableBandwidthPercentage); + return getFrequencyController().canTune(frequency); } /** diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index f1a0d972..f1ffa2d9 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -264,7 +264,7 @@ public class TunerEditor extends Editor //Set the displayed frequency without adjusting the tuner's frequency mFrequencyControl.setFrequency( - tuner.getTunerController().getFrequency(), false ); + tuner.getTunerController().getFrequencyController().getFrequency(), false ); } else { diff --git a/src/source/tuner/TunerModel.java b/src/source/tuner/TunerModel.java index 5f4bea95..426ad719 100644 --- a/src/source/tuner/TunerModel.java +++ b/src/source/tuner/TunerModel.java @@ -220,13 +220,13 @@ public class TunerModel extends AbstractTableModel implements Listener + * + ******************************************************************************/ +package source.tuner.channel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sample.Listener; +import source.Source; +import source.SourceEvent; +import source.SourceException; +import source.tuner.Tuner; +import source.tuner.channel.cic.CICTunerChannelSource; + +import java.util.SortedSet; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.RejectedExecutionException; + +public abstract class ChannelManager implements Listener +{ + private final static Logger mLog = LoggerFactory.getLogger(ChannelManager.class); + + private FrequencyController mFrequencyController; + private SortedSet mTunedChannels = new ConcurrentSkipListSet<>(); + + /** + * Channel manager provides access to Digital Drop Channel (DDC) tuner channel sources and automatic updating of + * a frequency controller to ensure that the center tuned frequency is correct for the provided DDC channels. + * + * @param frequencyController with a center tuned frequency that can be adjusted + */ + public ChannelManager(FrequencyController frequencyController) + { + mFrequencyController = frequencyController; + } + + protected FrequencyController getFrequencyController() + { + return mFrequencyController; + } + + /** + * Creates a tuner channel source for the tuner channel if possible. + * + * @param tunerChannel to source + * @return tuner channel source or null if the tuner channel cannot be sourced by this channel manager + */ + public TunerChannelSource getSource(TunerChannel tunerChannel) + { + TunerChannelSource tunerChannelSource = null; + + if(canTune(tunerChannel)) + { + //Add the channel so that we can calculate the new center frequency + mTunedChannels.add(tunerChannel); + + long centerFrequency = calculateCenterFrequency(); + + if(centerFrequency != getFrequencyController().getFrequency()) + { + try + { + getFrequencyController().setLockedFrequency(centerFrequency); + tunerChannelSource = getTunerChannelSource(tunerChannel); + } + catch(SourceException se) + { + mLog.error("Error setting tuner frequency while creating new tuner channel source for channel: " + + tunerChannel, se); + } + } + } + + //If we're not successful, remove the tuner channel from the sorted set + if(tunerChannelSource == null && mTunedChannels.contains(tunerChannel)) + { + mTunedChannels.remove(tunerChannel); + } + + return tunerChannelSource; + } + + /** + * Releases the source and performs any cleanup. + * @param source to release that was previously provided by this channel manager + */ + public void releaseSource(TunerChannelSource source) + { + mTunedChannels.remove(source.getTunerChannel()); + } + + /** + * Determine the center frequency to use for the current set of tuner channels. + */ + protected abstract long calculateCenterFrequency(); + + /** + * Constructs the actual tuner channel source for the tuner channel. This method assumes that the tuner channel + * can be tuned and that the tuner is correctly center tuned to source the channel. + * @param tunerChannel to source + * @return tuner channel source or null if there are any errors in creating the tuner channel source + */ + protected abstract TunerChannelSource getTunerChannelSource(TunerChannel tunerChannel); + + /** + * Indicates if channel along with all of the other currently sourced channels can fit within the tunable bandwidth. + */ + private boolean canTune(TunerChannel channel) + { + //Make sure we're within the tunable frequency range of this tuner + if(getFrequencyController().getMinimumFrequency() < channel.getMinFrequency() && + getFrequencyController().getMaximumFrequency() > channel.getMaxFrequency()) + { + //If this is the first lock, then we're good + if(mTunedChannels.isEmpty()) + { + return true; + } + else + { + int usableBandwidth = getFrequencyController().getUsableBandwidth(); + long minLockedFrequency = mTunedChannels.first().getMinFrequency(); + long maxLockedFrequency = mTunedChannels.last().getMaxFrequency(); + + //Requested channel is within current locked channel frequency range + if(minLockedFrequency <= channel.getMinFrequency() && channel.getMaxFrequency() <= maxLockedFrequency) + { + return true; + } + + //Requested channel is higher than min locked frequency + if(channel.getMaxFrequency() > minLockedFrequency && + channel.getMaxFrequency() - minLockedFrequency <= usableBandwidth) + { + return true; + } + + //Requested channel is lower than the max locked frequency + if(channel.getMinFrequency() <= maxLockedFrequency && + maxLockedFrequency - channel.getMinFrequency() <= usableBandwidth) + { + return true; + } + } + } + + return false; + } + + /** + * Constructs a digital drop channel (DDC) as a tuner channel from from the tuner, or returns null if the channel + * cannot be sourced from the tuner. + * + * This controller can't provide a channel if the channel, along with the current set of sourced channels, can't be + * accomodated within the current tuner bandwidth, or if a center frequency cannot be calculated that will ensure + * all of the channels can be accomodated and any defined central DC spike avoided. + * + * @param tuner to source the channel from + * @param channel with defined center frequency and bandwidth + * @return fully constructed tuner channel or null + * @throws RejectedExecutionException if the decimation processor has an error + */ + public TunerChannelSource getChannel(Tuner tuner, TunerChannel channel) throws RejectedExecutionException + { + TunerChannelSource source = null; + + if(canTune(channel)) + { + try + { + mTunedChannels.add(channel); + + if(requiresLOUpdate(channel)) + { + updateLOFrequency(); + } + + source = new CICTunerChannelSource(tuner, channel); + } + catch(SourceException se) + { + mTunedChannels.remove(channel); + source = null; + } + } + + return source; + } + + public int getChannelCount() + { + return mTunedChannels.size(); + } + + /** + * Indicates if the tuner's LO frequency must be updated in order to accommodate the tuner channel + */ + private boolean requiresLOUpdate(TunerChannel channel) throws SourceException + { + int usableBandwidth = getFrequencyController().getUsableBandwidth(); + + long minimumTunedFrequency = getFrequencyController().getFrequency() - (usableBandwidth / 2); + boolean isBelow = channel.getMinFrequency() < minimumTunedFrequency; + + long maximumTunedFrequency = getFrequencyController().getFrequency() + (usableBandwidth / 2); + boolean isAbove = channel.getMaxFrequency() > maximumTunedFrequency; + + long centerBlackoutBandwidth = getFrequencyController().getCenterBlackoutBandwidth(); + boolean overlapsCenter = centerBlackoutBandwidth > 0 && + channel.overlaps(getFrequencyController().getFrequency() - centerBlackoutBandwidth, + getFrequencyController().getFrequency() + centerBlackoutBandwidth); + + return isAbove || isBelow || overlapsCenter; + } + + /** + * Releases the currently sourced tuner channel from this tuner and shuts down the tuner if no other sources exist. + */ + public void releaseChannel(TunerChannelSource tunerChannelSource) + { + if(tunerChannelSource != null) + { + mTunedChannels.remove(tunerChannelSource.getTunerChannel()); + } + } + + /** + * Sets the Local Oscillator frequency to accomodate the current set of tuned channels. + * + * If there is only a single tuned channel, it is placed immediately to the right of the usable bandwidth right + * of the central DC spike. + * + * Otherwise, it places the highest channel frequency at the upper end of the tuner bandwidth, and then iteratively + * moves the center frequency higher until all channels fit within the bandwidth and none of the channels overlap + * any defined central DC spike unusable region. If a center tune frequency cannot be calculated, throw an + * exception so that the most recently added channel can be removed. + * + * Note: the tuned frequency is not changed until a legitimate new frequency can be calculated. If an exception + * is thrown, the current frequency is retained, so that the recently added channel can be removed and all other + * channels can continue as previously arranged. + * + * @throws SourceException if the set of tuner channels, including a recently added channel, cannot be tuned + * within the current tuner bandwidth and any central DC spike unusable region. + */ + private void updateLOFrequency() throws SourceException + { + long candidateFrequency; + + boolean isValidCandidateFrequency = true; + + //If there is only 1 channel set the center frequency so that the channel is positioned to the right of center + if(mTunedChannels.size() == 1) + { + candidateFrequency = mTunedChannels.first().getMinFrequency() - + getFrequencyController().getCenterBlackoutBandwidth() + 1; + } + else + { + long minLockedFrequency = mTunedChannels.first().getMinFrequency(); + long maxLockedFrequency = mTunedChannels.last().getMaxFrequency(); + + //Start by placing the highest frequency channel at the high end of the spectrum + candidateFrequency = maxLockedFrequency - (getFrequencyController().getUsableBandwidth() / 2); + + //Iterate the channels and make sure that none of them overlap the center DC spike buffer, if one exists + if(getFrequencyController().getCenterBlackoutBandwidth() > 0) + { + boolean processingRequired = true; + + while(isValidCandidateFrequency && processingRequired) + { + processingRequired = false; + + long centerBlackout = getFrequencyController().getCenterBlackoutBandwidth(); + + long minAvoid = candidateFrequency - centerBlackout; + long maxAvoid = candidateFrequency + centerBlackout; + + //If any of the center channel(s) overlap the central DC spike avoid area, we'll iteratively + //increase the tuned frequency causing the set of channels to move left in the tuned bandwidth until + //we either find a good center tune frequency, or we walk the lowest frequency channel out of the + //minimum tuned range, in which case we'll throw an exception indicating we don't have a solution. + for(TunerChannel channel : mTunedChannels) + { + if(channel.overlaps(minAvoid, maxAvoid)) + { + //Calculate a tuned frequency adjustment that places this overlapping channel just to the + //left of the central DC spike avoid zone + long adjustment = channel.getMaxFrequency() - minAvoid + 1; + + //If the candidate frequency doesn't push the lowest channel out of bounds, make adjustment + if(candidateFrequency + adjustment - + (getFrequencyController().getUsableBandwidth() / 2) <= minLockedFrequency) + { + candidateFrequency += adjustment; + processingRequired = true; + } + //Otherwise, punt and indicate that we can't find a center frequency or add the channel + else + { + isValidCandidateFrequency = false; + } + + //break out of the for/each loop, so that we can start over again with all of the channels + break; + } + } + } + } + } + + if(isValidCandidateFrequency) + { + mFrequencyController.setLockedFrequency(candidateFrequency); + } + else + { + throw new SourceException("Couldn't calculate viable center frequency from set of tuner channels"); + } + } + +} diff --git a/src/source/tuner/frequency/FrequencyController.java b/src/source/tuner/channel/FrequencyController.java similarity index 73% rename from src/source/tuner/frequency/FrequencyController.java rename to src/source/tuner/channel/FrequencyController.java index ce64fc36..8ec85198 100644 --- a/src/source/tuner/frequency/FrequencyController.java +++ b/src/source/tuner/channel/FrequencyController.java @@ -16,7 +16,7 @@ * along with this program. If not, see * ******************************************************************************/ -package source.tuner.frequency; +package source.tuner.channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +26,7 @@ import source.SourceEvent; import source.SourceException; import java.util.ArrayList; +import java.util.List; public class FrequencyController { @@ -38,37 +39,59 @@ public class FrequencyController private long mMaximumFrequency; private double mFrequencyCorrection = 0.0d; private double mSampleRate = 0.0d; + private long mCenterBlackoutBandwidth; + private double mUsableBandwidthPercentage; + private boolean mLocked; - private ArrayList mProcessors = new ArrayList<>(); + private List mProcessors = new ArrayList<>(); - public FrequencyController(Tunable tunable, long minFrequency, long maxFrequency, double frequencyCorrection) + public FrequencyController(Tunable tunable, long minFrequency, long maxFrequency, double frequencyCorrection, + long centerBlackoutBandwidth, double usableBandwidthPercentage) { mTunable = tunable; mMinimumFrequency = minFrequency; mMaximumFrequency = maxFrequency; mFrequencyCorrection = frequencyCorrection; + mCenterBlackoutBandwidth = centerBlackoutBandwidth; + mUsableBandwidthPercentage = usableBandwidthPercentage; } - /** - * Get bandwidth in hertz - */ - public int getBandwidth() + public boolean isLocked() { - return (int)mSampleRate; + return mLocked; } /** - * Set sample rate in hertz + * Sets the locked status. When this control is locked, the sample rate cannot be changed and the center + * tuned frequency can only be changed via the setLockedFrequency() method. + * + * Note: this method is package-private and is intended to be used (only) by the ChannelManager. + * + * @param locked + */ + void setLocked(boolean locked) + { + mLocked = locked; + } + + + /** + * Set sample rate/bandwidth in hertz */ public void setSampleRate(int sampleRate) throws SourceException { + if(isLocked()) + { + throw new FrequencyControllerLockedException(); + } + mSampleRate = sampleRate; broadcastSampleRateChange(); } /** - * Get sample rate in hertz + * Get sample rate/bandwidth in hertz */ public double getSampleRate() { @@ -91,6 +114,23 @@ public class FrequencyController * version of the requested frequency */ public void setFrequency(long frequency) throws SourceException + { + if(isLocked()) + { + throw new FrequencyControllerLockedException(); + } + + setFrequency(frequency, true); + } + + /** + * Sets the frequency when the frequency controller is locked. Note: this method is package-private and is + * only intended to be used by the ChannelManager. + * + * @param frequency to set as the center tuned frequency + * @throws SourceException if there is an error while setting the frequency + */ + void setLockedFrequency(long frequency) throws SourceException { setFrequency(frequency, true); } @@ -250,6 +290,34 @@ public class FrequencyController } } + /** + * Identifies the bandwidth of a blackout frequency range centered about the center frequency. This is + * used to block the frequency range where a DC spike may exist. + * @return center blackout bandwidth in hertz + */ + public long getCenterBlackoutBandwidth() + { + return mCenterBlackoutBandwidth; + } + + /** + * Percentage of the available bandwidth that is usable. Sources with filter roll-off at the ends of the + * spectral range can limit the usable bandwidth to avoid providing samples at these spectral ends. + * + * @return bandwidth percentage (0.0 <> 1.0) + */ + public double getUsableBandwidthPercentage() + { + return mUsableBandwidthPercentage; + } + + /** + * Usable bandwidth in hertz calculated as the sample rate constrained by the usable bandwidth percentage. + */ + public int getUsableBandwidth() + { + return (int)(getSampleRate() * getUsableBandwidthPercentage()); + } public interface Tunable { diff --git a/src/source/tuner/channel/TunerChannelProvider.java b/src/source/tuner/channel/FrequencyControllerLockedException.java similarity index 61% rename from src/source/tuner/channel/TunerChannelProvider.java rename to src/source/tuner/channel/FrequencyControllerLockedException.java index 56b85aa1..9ad9f09a 100644 --- a/src/source/tuner/channel/TunerChannelProvider.java +++ b/src/source/tuner/channel/FrequencyControllerLockedException.java @@ -18,28 +18,18 @@ ******************************************************************************/ package source.tuner.channel; -import java.util.concurrent.RejectedExecutionException; - import source.SourceException; -public interface TunerChannelProvider +public class FrequencyControllerLockedException extends SourceException { - /** - * Returns a tuner frequency channel source, tuned to the correct frequency - * - * @param frequency - desired frequency - * - * @return - source for 48k sample rate - */ - public abstract TunerChannelSource getChannel( TunerChannel channel ) - throws RejectedExecutionException, SourceException; + private static final long serialVersionUID = 1L; - /** - * Releases the tuned channel resources - * - * @param channel - previously obtained tuner channel - */ - public abstract void releaseChannel( TunerChannelSource source ); - - + /** + * Indicates an attempt to adjust frequency or sample rate on a locked frequency controller. + */ + public FrequencyControllerLockedException() + { + super( "Frequency controller is locked -- frequency and sample rate cannot be changed until the controller" + + " is unlocked by the channel manager" ); + } } diff --git a/src/source/tuner/channel/TunerChannelSource.java b/src/source/tuner/channel/TunerChannelSource.java index c88c7b4d..fef2a0eb 100644 --- a/src/source/tuner/channel/TunerChannelSource.java +++ b/src/source/tuner/channel/TunerChannelSource.java @@ -41,6 +41,7 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc private Listener mProducerSourceEventListener; private Listener mConsumerSourceEventListener; private BufferProcessor mBufferProcessor = new BufferProcessor(); + private boolean mExpended = false; /** * Tuner Channel Source is a Digital Drop Channel (DDC) abstract class that defines the minimum functionality @@ -116,6 +117,12 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc */ public void start() { + if(mExpended) + { + throw new IllegalStateException("Cannot start an expended tuner channel source. You must request a new" + + " tuner channel source each time. Tuner channel sources are one-use only"); + } + //Broadcast current frequency and sample rate so consumer can configure correctly broadcastConsumerSourceEvent(SourceEvent.frequencyChange(getFrequency())); broadcastConsumerSourceEvent(SourceEvent.sampleRateChange(getSampleRate())); @@ -134,6 +141,8 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc */ public void stop() { + mExpended = true; + if(mProducerSourceEventListener != null) { mProducerSourceEventListener.receive(SourceEvent.stopSampleStream(this)); @@ -146,7 +155,11 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc @Override public void reset() { - //Reset is not valid for a tuner channel source - ignored + if(mExpended) + { + throw new IllegalStateException("Cannot reset an expended tuner channel source. You must request a new" + + " tuner channel source each time. Tuner channel sources are one-use only"); + } } /** diff --git a/src/source/tuner/channel/CICTunerChannelSource.java b/src/source/tuner/channel/cic/CICTunerChannelSource.java similarity index 94% rename from src/source/tuner/channel/CICTunerChannelSource.java rename to src/source/tuner/channel/cic/CICTunerChannelSource.java index 50515ce7..a5086ae2 100644 --- a/src/source/tuner/channel/CICTunerChannelSource.java +++ b/src/source/tuner/channel/cic/CICTunerChannelSource.java @@ -16,7 +16,7 @@ * along with this program. If not, see * ******************************************************************************/ -package source.tuner.channel; +package source.tuner.channel.cic; import dsp.filter.FilterFactory; import dsp.filter.Window; @@ -31,11 +31,13 @@ import sample.complex.Complex; import sample.complex.ComplexBuffer; import source.SourceEvent; import source.tuner.Tuner; +import source.tuner.channel.TunerChannel; +import source.tuner.channel.TunerChannelSource; import java.util.ArrayList; import java.util.List; -public class CICTunerChannelSource extends TunerChannelSource +public class CICTunerChannelSource extends TunerChannelSource implements Listener { private final static Logger mLog = LoggerFactory.getLogger(CICTunerChannelSource.class); @@ -77,12 +79,13 @@ public class CICTunerChannelSource extends TunerChannelSource mBuffer.setSourceOverflowListener(this); //Setup the frequency translator to the current source frequency - mTunerFrequency = tuner.getTunerController().getFrequency(); + mTunerFrequency = tuner.getTunerController().getFrequencyController().getFrequency(); long frequencyOffset = mTunerFrequency - getTunerChannel().getFrequency(); - mFrequencyCorrectionMixer = new Oscillator(frequencyOffset, tuner.getTunerController().getSampleRate()); + mFrequencyCorrectionMixer = new Oscillator(frequencyOffset, + tuner.getTunerController().getFrequencyController().getSampleRate()); //Setup the decimation filter chain - setSampleRate(tuner.getTunerController().getSampleRate()); + setSampleRate(tuner.getTunerController().getFrequencyController().getSampleRate()); } /** diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java index 40b69708..67fbaf04 100644 --- a/src/source/tuner/configuration/TunerConfigurationModel.java +++ b/src/source/tuner/configuration/TunerConfigurationModel.java @@ -377,7 +377,7 @@ public class TunerConfigurationModel extends AbstractTableModel { TunerConfiguration assigned = getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); - assigned.setFrequency( tuner.getTunerController().getFrequency() ); + assigned.setFrequency( tuner.getTunerController().getFrequencyController().getFrequency() ); broadcast( new TunerConfigurationEvent( assigned, Event.CHANGE ) ); } diff --git a/src/source/tuner/fcd/proV1/FCD1TunerController.java b/src/source/tuner/fcd/proV1/FCD1TunerController.java index 587905b7..6b576835 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerController.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerController.java @@ -63,7 +63,7 @@ public class FCD1TunerController extends FCDTunerController { super.init(); - mFrequencyController.setSampleRate(SAMPLE_RATE); + getFrequencyController().setSampleRate(SAMPLE_RATE); try { @@ -120,7 +120,7 @@ public class FCD1TunerController extends FCDTunerController try { - setFrequencyCorrection(config.getFrequencyCorrection()); + getFrequencyController().setFrequencyCorrection(config.getFrequencyCorrection()); setLNAGain(config.getLNAGain()); setLNAEnhance(config.getLNAEnhance()); setMixerGain(config.getMixerGain()); @@ -142,7 +142,7 @@ public class FCD1TunerController extends FCDTunerController try { - setFrequency(config.getFrequency()); + getFrequencyController().setFrequency(config.getFrequency()); } catch(SourceException se) { diff --git a/src/source/tuner/fcd/proV1/FCD1TunerEditor.java b/src/source/tuner/fcd/proV1/FCD1TunerEditor.java index 5c8f834b..40a330b0 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerEditor.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerEditor.java @@ -149,7 +149,7 @@ public class FCD1TunerEditor extends TunerConfigurationEditor try { - mController.setFrequencyCorrection( value ); + mController.getFrequencyController().setFrequencyCorrection( value ); save(); } catch ( SourceException e1 ) diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java index d1a99ff2..5e2412c4 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java @@ -52,7 +52,7 @@ public class FCD2TunerController extends FCDTunerController { super.init(); - mFrequencyController.setSampleRate(SAMPLE_RATE); + getFrequencyController().setSampleRate(SAMPLE_RATE); try { @@ -112,7 +112,7 @@ public class FCD2TunerController extends FCDTunerController { FCD2TunerConfiguration fcd2 = (FCD2TunerConfiguration)config; - setFrequencyCorrection(fcd2.getFrequencyCorrection()); + getFrequencyController().setFrequencyCorrection(fcd2.getFrequencyCorrection()); long frequency = MINIMUM_TUNABLE_FREQUENCY; @@ -122,13 +122,13 @@ public class FCD2TunerController extends FCDTunerController frequency = fcd2.getFrequency(); } - setFrequency(frequency); + getFrequencyController().setFrequency(frequency); setLNAGain(fcd2.getGainLNA()); setMixerGain(fcd2.getGainMixer()); try { - setFrequency(fcd2.getFrequency()); + getFrequencyController().setFrequency(fcd2.getFrequency()); } catch(SourceException se) { diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java b/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java index 1e0ab570..e5c5c8be 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java @@ -141,7 +141,7 @@ public class FCD2TunerEditor extends TunerConfigurationEditor try { - mController.setFrequencyCorrection( value ); + mController.getFrequencyController().setFrequencyCorrection( value ); save(); } catch ( SourceException e1 ) diff --git a/src/source/tuner/frequency/FrequencyLimitException.java b/src/source/tuner/frequency/FrequencyLimitException.java deleted file mode 100644 index be72a74e..00000000 --- a/src/source/tuner/frequency/FrequencyLimitException.java +++ /dev/null @@ -1,36 +0,0 @@ -package source.tuner.frequency; - -public class FrequencyLimitException extends Exception -{ - private static final long serialVersionUID = 1L; - - private long mFrequencyLimit; - private long mRequestedFrequency; - - /** - * Indicates that the requested frequency is outside of the tunable bounds - * of the tuner. - * @param message - * @param frequency - */ - public FrequencyLimitException( long requestedFrequency, - long frequencyLimit ) - { - super( "Requested frequency [" + requestedFrequency + - "] exceeds the limit [" + frequencyLimit + "]. Tuned " - + "frequency was set to the limit value" ); - - mRequestedFrequency = requestedFrequency; - mFrequencyLimit = frequencyLimit; - } - - public long getRequestedFrequency() - { - return mRequestedFrequency; - } - - public long getFrequencyLimit() - { - return mFrequencyLimit; - } -} diff --git a/src/source/tuner/hackrf/HackRFTunerController.java b/src/source/tuner/hackrf/HackRFTunerController.java index 41b1b729..2af8ed00 100644 --- a/src/source/tuner/hackrf/HackRFTunerController.java +++ b/src/source/tuner/hackrf/HackRFTunerController.java @@ -138,7 +138,7 @@ public class HackRFTunerController extends USBTunerController setMode(Mode.RECEIVE); - setFrequency(DEFAULT_FREQUENCY); + getFrequencyController().setFrequency(DEFAULT_FREQUENCY); } catch(Exception e) { @@ -306,7 +306,7 @@ public class HackRFTunerController extends USBTunerController */ public long getTunedFrequency() throws SourceException { - return mFrequencyController.getTunedFrequency(); + return getFrequencyController().getTunedFrequency(); } @Override @@ -353,11 +353,11 @@ public class HackRFTunerController extends USBTunerController try { setSampleRate(hackRFConfig.getSampleRate()); - setFrequencyCorrection(hackRFConfig.getFrequencyCorrection()); + getFrequencyController().setFrequencyCorrection(hackRFConfig.getFrequencyCorrection()); setAmplifierEnabled(hackRFConfig.getAmplifierEnabled()); setLNAGain(hackRFConfig.getLNAGain()); setVGAGain(hackRFConfig.getVGAGain()); - setFrequency(getFrequency()); + getFrequencyController().setFrequency(getFrequencyController().getFrequency()); } catch(UsbException e) { @@ -367,7 +367,7 @@ public class HackRFTunerController extends USBTunerController try { - setFrequency(hackRFConfig.getFrequency()); + getFrequencyController().setFrequency(hackRFConfig.getFrequency()); } catch(SourceException se) { @@ -511,7 +511,7 @@ public class HackRFTunerController extends USBTunerController { setSampleRateManual(rate.getRate(), 1); - mFrequencyController.setSampleRate(rate.getRate()); + getFrequencyController().setSampleRate(rate.getRate()); setBasebandFilter(rate.getFilter()); diff --git a/src/source/tuner/hackrf/HackRFTunerEditor.java b/src/source/tuner/hackrf/HackRFTunerEditor.java index 8962be28..2691a345 100644 --- a/src/source/tuner/hackrf/HackRFTunerEditor.java +++ b/src/source/tuner/hackrf/HackRFTunerEditor.java @@ -179,7 +179,7 @@ public class HackRFTunerEditor extends TunerConfigurationEditor try { - mController.setFrequencyCorrection( value ); + mController.getFrequencyController().setFrequencyCorrection( value ); save(); } catch ( SourceException e1 ) diff --git a/src/source/tuner/manager/AbstractSourceManager.java b/src/source/tuner/manager/AbstractSourceManager.java deleted file mode 100644 index 9c373d2d..00000000 --- a/src/source/tuner/manager/AbstractSourceManager.java +++ /dev/null @@ -1,111 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package source.tuner.manager; - -import sample.Broadcaster; -import sample.Listener; -import sample.complex.ComplexBuffer; -import sample.complex.IComplexBufferProvider; -import source.SourceEvent; -import source.tuner.channel.TunerChannel; -import source.tuner.channel.TunerChannelSource; - -import java.util.List; - -/** - * Interface to define the functionality of a source manager for handling tuner channel management, complex buffer - * listeners, and source event listeners. - */ -public abstract class AbstractSourceManager implements IComplexBufferProvider, Listener -{ - protected Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); - - /** - * Indicates if this source manager can tune (ie provide a source for) the specified tuner channel - */ - public abstract boolean canTune(TunerChannel tunerChannel); - - /** - * List of tuner channels being sourced by this source manager - */ - public abstract List getChannels(); - - /** - * Obtains a source for the tuner channel or returns null if the channel cannot be sourced by this tuner. - * Note: use the canTune() method to check if a tuner channel can be sourced/tuned prior to accessing this method. - * - * @param tunerChannel for requested source - * @return tuner channel source or null - */ - public abstract TunerChannelSource getSource(TunerChannel tunerChannel); - - - /** - * Releases the source and all related resources - */ - public abstract void releaseSource(TunerChannelSource source); - - /** - * Adds the listener to receive source events from this source manager - * - * @param sourceEventListener to add - */ - public abstract void addSourceEventListener(Listener sourceEventListener); - - /** - * Removes the listener from receiving source events from this source manager - * @param sourceEventListener to remove - */ - public abstract void removeSourceEventListener(Listener sourceEventListener); - - /** - * Adds the listener to receive complex buffer samples - */ - @Override - public void addComplexBufferListener(Listener listener) - { - mComplexBufferBroadcaster.addListener(listener); - } - - /** - * Removes the listener from receiving complex buffer samples - */ - @Override - public void removeComplexBufferListener(Listener listener) - { - mComplexBufferBroadcaster.removeListener(listener); - } - - /** - * Indicates if there are any complex buffer listeners registered with this source manager - */ - @Override - public boolean hasComplexBufferListeners() - { - return mComplexBufferBroadcaster.hasListeners(); - } - - /** - * Broadcasts the buffer to any registered listeners - */ - protected void broadcast(ComplexBuffer complexBuffer) - { - mComplexBufferBroadcaster.broadcast(complexBuffer); - } -} diff --git a/src/source/tuner/manager/TunerSourceManager.java b/src/source/tuner/manager/TunerSourceManager.java deleted file mode 100644 index fb6856bd..00000000 --- a/src/source/tuner/manager/TunerSourceManager.java +++ /dev/null @@ -1,83 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * - ******************************************************************************/ -package source.tuner.manager; - -import dsp.filter.channelizer.PolyphaseChannelManager; -import sample.Listener; -import source.SourceEvent; -import source.tuner.channel.TunerChannel; -import source.tuner.channel.TunerChannelSource; -import source.tuner.TunerController; - -import java.util.List; - -public class TunerSourceManager extends AbstractSourceManager -{ - private PolyphaseChannelManager mPolyphaseChannelManager; - private TunerController mTunerController; - - public TunerSourceManager(TunerController tunerController) - { - mTunerController = tunerController; - } - - //TODO: broadcast a tuner event each time we add a new channel to the polyphase manager - - @Override - public boolean canTune(TunerChannel tunerChannel) - { - return false; - } - - @Override - public List getChannels() - { - return null; - } - - @Override - public TunerChannelSource getSource(TunerChannel tunerChannel) - { - return null; - } - - @Override - public void releaseSource(TunerChannelSource source) - { - - } - - @Override - public void addSourceEventListener(Listener sourceEventListener) - { - - } - - @Override - public void removeSourceEventListener(Listener sourceEventListener) - { - - } - - @Override - public void receive(SourceEvent sourceEvent) - { - - } -} diff --git a/src/source/tuner/rtl/e4k/E4KTunerController.java b/src/source/tuner/rtl/e4k/E4KTunerController.java index 5c53621f..437247c8 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerController.java +++ b/src/source/tuner/rtl/e4k/E4KTunerController.java @@ -129,7 +129,7 @@ public class E4KTunerController extends RTL2832TunerController setSampleRate(sampleRate); double correction = e4kConfig.getFrequencyCorrection(); - setFrequencyCorrection(correction); + getFrequencyController().setFrequencyCorrection(correction); E4KGain masterGain = e4kConfig.getMasterGain(); setGain(masterGain, true); @@ -148,7 +148,7 @@ public class E4KTunerController extends RTL2832TunerController try { - setFrequency(e4kConfig.getFrequency()); + getFrequencyController().setFrequency(e4kConfig.getFrequency()); } catch(SourceException se) { @@ -354,7 +354,7 @@ public class E4KTunerController extends RTL2832TunerController * to rounding errors, 52 mhz becomes 51.999993, so we need to adjust * x to get 52.000003 mhz ... otherwise the PLL won't lock on the freq */ - if(actualFrequency < getMinFrequency()) + if(actualFrequency < getFrequencyController().getMinimumFrequency()) { x++; actualFrequency = calculateActualFrequency(pll, z, x); diff --git a/src/source/tuner/rtl/e4k/E4KTunerEditor.java b/src/source/tuner/rtl/e4k/E4KTunerEditor.java index 5989fdf2..064da0ad 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerEditor.java +++ b/src/source/tuner/rtl/e4k/E4KTunerEditor.java @@ -184,7 +184,7 @@ public class E4KTunerEditor extends TunerConfigurationEditor try { - mController.setFrequencyCorrection( value ); + mController.getFrequencyController().setFrequencyCorrection( value ); save(); } catch ( SourceException e1 ) diff --git a/src/source/tuner/rtl/r820t/R820TTunerController.java b/src/source/tuner/rtl/r820t/R820TTunerController.java index 762cb080..4fc9f782 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerController.java +++ b/src/source/tuner/rtl/r820t/R820TTunerController.java @@ -104,7 +104,7 @@ public class R820TTunerController extends RTL2832TunerController setSampleRate(sampleRate); double correction = config.getFrequencyCorrection(); - setFrequencyCorrection(correction); + getFrequencyController().setFrequencyCorrection(correction); R820TGain masterGain = config.getMasterGain(); setGain(masterGain, true); @@ -123,7 +123,7 @@ public class R820TTunerController extends RTL2832TunerController try { - setFrequency(config.getFrequency()); + getFrequencyController().setFrequency(config.getFrequency()); } catch(SourceException se) { diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/source/tuner/rtl/r820t/R820TTunerEditor.java index c9b75a2e..bf3cbf16 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerEditor.java +++ b/src/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -191,7 +191,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor try { - mController.setFrequencyCorrection( value ); + mController.getFrequencyController().setFrequencyCorrection( value ); } catch ( SourceException e1 ) { diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index 496708b9..46d79731 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -430,9 +430,9 @@ public class SpectralDisplayPanel extends JPanel implements Listener