diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java index 008e7dc2..fba3a9da 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java @@ -40,12 +40,16 @@ import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; +import javax.swing.JToggleButton; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.Dimension; import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class ChannelizerViewer extends JFrame @@ -61,6 +65,7 @@ public class ChannelizerViewer extends JFrame private JLabel mToneFrequencyLabel; private PrimarySpectrumPanel mPrimarySpectrumPanel; private ChannelArrayPanel mChannelPanel; + private DiscreteIndexChannelArrayPanel mDiscreteIndexChannelPanel; private int mChannelCount; private int mChannelsPerRow; private long mBaseFrequency = 100000000; //100 MHz @@ -98,10 +103,11 @@ public class ChannelizerViewer extends JFrame if(mPrimaryPanel == null) { mPrimaryPanel = new JPanel(); - mPrimaryPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[][][grow,fill]")); + mPrimaryPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[][][grow,fill][grow,fill]")); mPrimaryPanel.add(getSpectrumPanel(), "wrap"); mPrimaryPanel.add(getControlPanel(), "wrap"); - mPrimaryPanel.add(getChannelArrayPanel()); + mPrimaryPanel.add(getChannelArrayPanel(), "wrap"); + mPrimaryPanel.add(getDiscreteIndexChannelPanel()); } return mPrimaryPanel; @@ -177,6 +183,16 @@ public class ChannelizerViewer extends JFrame return mChannelPanel; } + private DiscreteIndexChannelArrayPanel getDiscreteIndexChannelPanel() + { + if(mDiscreteIndexChannelPanel == null) + { + mDiscreteIndexChannelPanel = new DiscreteIndexChannelArrayPanel(); + } + + return mDiscreteIndexChannelPanel; + } + public class ChannelArrayPanel extends JPanel { public ChannelArrayPanel() @@ -222,6 +238,41 @@ public class ChannelizerViewer extends JFrame } } + public class DiscreteIndexChannelArrayPanel extends JPanel + { + public DiscreteIndexChannelArrayPanel() + { + 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")); + + for(int x = 0; x < mChannelCount; x++) + { + TunerChannelSource source = mTestTuner.getChannelSourceManager().getSpecialSource(x); + DiscreteChannelPanel channelPanel = new DiscreteChannelPanel(mSettingsManager, source, x); + channelPanel.setDFTSize(mChannelPanelDFTSize); + + if(x % mChannelsPerRow == mChannelsPerRow - 1) + { + add(channelPanel, "grow,push,wrap 2px"); + } + else + { + add(channelPanel, "grow,push"); + } + } + } + } + /** * Returns a list of tuner channels that will fit within the tuner's bandwidth, minus a half channel each at the * lower and upper ends of the spectrum. @@ -291,18 +342,30 @@ public class ChannelizerViewer extends JFrame private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.COMPLEX); private ComplexDecibelConverter mComplexDecibelConverter = new ComplexDecibelConverter(); private SpectrumPanel mSpectrumPanel; + private JToggleButton mLoggingButton; + private boolean mLoggingEnabled; public ChannelPanel(SettingsManager settingsManager, double sampleRate, long frequency, int bandwidth, boolean enableLogging) { - setLayout(new MigLayout("insets 0 0 0 0", "[center,grow,fill]", "[grow,fill][]")); + setLayout(new MigLayout("insets 0 0 0 0", "[center,grow,fill][]", "[grow,fill][]")); mSpectrumPanel = new SpectrumPanel(settingsManager); mSpectrumPanel.setSampleSize(28); - add(mSpectrumPanel, "wrap"); + add(mSpectrumPanel, "span"); add(new JLabel("Center:" + frequency)); + mLoggingButton = new JToggleButton("Logging"); + mLoggingButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + mLoggingEnabled = mLoggingButton.isSelected(); + } + }); + add(mLoggingButton); + mDFTProcessor.addConverter(mComplexDecibelConverter); mDFTProcessor.process(SourceEvent.sampleRateChange(sampleRate)); - mComplexDecibelConverter.addListener(mSpectrumPanel); TunerChannel tunerChannel = new TunerChannel(frequency, bandwidth); @@ -310,21 +373,19 @@ public class ChannelizerViewer extends JFrame if(mSource != null) { - if(enableLogging) + mSource.setListener(new Listener() { - mSource.setListener(new Listener() + @Override + public void receive(ComplexBuffer complexBuffer) { - @Override - public void receive(ComplexBuffer complexBuffer) + if(mLoggingEnabled) { - mDFTProcessor.receive(complexBuffer); + mLog.debug("Samples:" + Arrays.toString(complexBuffer.getSamples())); } - }); - } - else - { - mSource.setListener(mDFTProcessor); - } + + mDFTProcessor.receive(complexBuffer); + } + }); mSource.start(); } @@ -358,13 +419,95 @@ public class ChannelizerViewer extends JFrame } } + public class DiscreteChannelPanel extends JPanel implements Listener, ISourceEventProcessor + { + private TunerChannelSource mSource; + private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.COMPLEX); + private ComplexDecibelConverter mComplexDecibelConverter = new ComplexDecibelConverter(); + private SpectrumPanel mSpectrumPanel; + private JToggleButton mLoggingButton; + private boolean mLoggingEnabled; + + public DiscreteChannelPanel(SettingsManager settingsManager, TunerChannelSource source, int index) + { + mSource = source; + setLayout(new MigLayout("insets 0 0 0 0", "[center,grow,fill][]", "[grow,fill][]")); + mSpectrumPanel = new SpectrumPanel(settingsManager); + mSpectrumPanel.setSampleSize(28); + add(mSpectrumPanel, "span"); + add(new JLabel("Index:" + index)); + + mLoggingButton = new JToggleButton("Logging"); + mLoggingButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + mLoggingEnabled = mLoggingButton.isSelected(); + } + }); + add(mLoggingButton); + + mDFTProcessor.addConverter(mComplexDecibelConverter); + mDFTProcessor.process(SourceEvent.sampleRateChange(25000.0)); + mComplexDecibelConverter.addListener(mSpectrumPanel); + + if(mSource != null) + { + mSource.setListener(new Listener() + { + @Override + public void receive(ComplexBuffer complexBuffer) + { + if(mLoggingEnabled) + { + mLog.debug("Samples:" + Arrays.toString(complexBuffer.getSamples())); + } + + mDFTProcessor.receive(complexBuffer); + } + }); + + mSource.start(); + } + else + { + mLog.error("Couldn't get a source from the tuner for index: " + index); + } + } + + 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) { boolean useGUI = true; if(useGUI) { - int channelsPerRow = 4; + int channelsPerRow = 8; final ChannelizerViewer frame = new ChannelizerViewer(channelsPerRow); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java index 6e62c8c6..662bbdbc 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java @@ -197,7 +197,8 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann int half = getChannelCount() / 2; - int outputOffset = getChannelCount() - half - 1; +// int outputOffset = getChannelCount() - half - 1; + int outputOffset = getChannelCount() - 1; int outputIndex; int sampleIndex; int filterIndex; @@ -214,7 +215,8 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann //The second half of the sample buffer was loaded using the previous column pointer - adjust for that now sampleIndexMap = mSampleIndexMap[(mColumnPointer == (mSamples.length - 1) ? 0 : mColumnPointer + 1)]; - outputOffset = getChannelCount() + half - 1; +// outputOffset = getChannelCount() + half - 1; + outputOffset = getChannelCount() - 1; for(int channel = half; channel < getChannelCount(); channel++) { diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java index 57de3a4e..cae8b91a 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java @@ -34,6 +34,7 @@ import io.github.dsheirer.source.tuner.channel.TunerChannelSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -158,6 +159,32 @@ public class PolyphaseChannelManager implements ISourceEventProcessor return channelSource; } + public TunerChannelSource getSpecialChannel(int index) + { + PolyphaseChannelSource channelSource = null; + + try + { + List polyphaseIndexes = new ArrayList<>(); + polyphaseIndexes.add(index); + + IPolyphaseChannelOutputProcessor outputProcessor = getOutputProcessor(polyphaseIndexes); + + TunerChannel tunerChannel = new TunerChannel(100000000, 12500); + + if(outputProcessor != null) + { + channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener, + mChannelCalculator.getChannelSampleRate()); + } + } + catch(IllegalArgumentException iae) + { + } + + 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. @@ -456,7 +483,7 @@ public class PolyphaseChannelManager implements ISourceEventProcessor if(taps == null) { - taps = FilterFactory.getSincM2Synthesizer(mChannelCalculator.getChannelBandwidth(), channels, + taps = FilterFactory.getSincM2Synthesizer(mChannelCalculator.getChannelSampleRate(), channels, POLYPHASE_FILTER_TAPS_PER_CHANNEL, Window.WindowType.BLACKMAN_HARRIS_7, true); mOutputProcessorFilters.put(channels, taps); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/SynthesizerViewer.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/SynthesizerViewer.java index 9352c5eb..33e728bd 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/SynthesizerViewer.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/SynthesizerViewer.java @@ -23,6 +23,7 @@ import io.github.dsheirer.dsp.mixer.LowPhaseNoiseOscillator; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.SampleType; import io.github.dsheirer.sample.complex.ComplexBuffer; +import io.github.dsheirer.sample.real.IOverflowListener; import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; @@ -155,6 +156,15 @@ public class SynthesizerViewer extends JFrame mDFTProcessor.process(SourceEvent.sampleRateChange(CHANNEL_SAMPLE_RATE)); mDFTProcessor.setFrameRate(CHANNEL_FFT_FRAME_RATE); mComplexDecibelConverter.addListener(mSpectrumPanel); + + mDFTProcessor.setOverflowListener(new IOverflowListener() + { + @Override + public void sourceOverflow(boolean overflow) + { + mLog.debug("Buffer " + (overflow ? "overflow" : "reset")); + } + }); } public void setDFTSize(DFTSize dftSize) @@ -174,6 +184,7 @@ public class SynthesizerViewer extends JFrame private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.COMPLEX); private ComplexDecibelConverter mComplexDecibelConverter = new ComplexDecibelConverter(); private SpectrumPanel mSpectrumPanel; + private boolean mLoggingEnabled = false; public ChannelPanel(SettingsManager settingsManager, ChannelControlPanel channelControlPanel) { diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java index 4afb805d..f280efbb 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java @@ -68,7 +68,9 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP @Override public void setFrequencyOffset(long frequencyOffset) { - mFrequencyCorrectionMixer.setFrequency(frequencyOffset); + long offset = frequencyOffset; + mLog.debug("Setting frequency offset to: " + offset); +// mFrequencyCorrectionMixer.setFrequency(offset); mFrequencyCorrectionEnabled = (frequencyOffset != 0); } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java index cc58584b..1ca55983 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java @@ -19,6 +19,7 @@ import io.github.dsheirer.dsp.filter.Filters; import io.github.dsheirer.dsp.filter.channelizer.PolyphaseChannelResultsBuffer; import io.github.dsheirer.dsp.filter.channelizer.TwoChannelSynthesizerM2; import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter; +import io.github.dsheirer.dsp.mixer.FS4DownConverter; import io.github.dsheirer.sample.complex.TimestampedBufferAssembler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +31,7 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor private ComplexFIRFilter mLowPassFilter = new ComplexFIRFilter(Filters.HALF_BAND_FILTER_47T.getCoefficients(), 1.0f); private TwoChannelSynthesizerM2 mSynthesizer; + private FS4DownConverter mFS4DownConverter = new FS4DownConverter(); private final static Logger mLog = LoggerFactory.getLogger(TwoChannelOutputProcessor.class); @@ -59,8 +61,7 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor @Override public void setFrequencyOffset(long frequencyOffset) { -// super.setFrequencyOffset(frequencyOffset); - mLog.debug("**Ignoring** Setting frequency offset [" + frequencyOffset + "] for channel offsets [" + mChannelOffset1 + "," + mChannelOffset2 + "]"); + super.setFrequencyOffset(frequencyOffset); } @@ -109,8 +110,11 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor //Join the two channels using the synthesizer float[] synthesized = mSynthesizer.process(channel1, channel2); + //The synthesized channels are centered at +FS/4 ... downconvert to center the spectrum + mFS4DownConverter.mixComplex(synthesized); + //Apply frequency correction to center the signal of interest within the synthesized channel - getFrequencyCorrectionMixer().mixComplex(synthesized); +// getFrequencyCorrectionMixer().mixComplex(synthesized); //Apply low-pass filtering to attenuate neighboring channel(s) // synthesized = mLowPassFilter.filter(synthesized); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/dc/BufferDCFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/dc/BufferDCFilter.java new file mode 100644 index 00000000..bf229d4a --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/dc/BufferDCFilter.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * sdr-trunk + * Copyright (C) 2014-2018 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 io.github.dsheirer.dsp.filter.dc; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BufferDCFilter +{ + private final static Logger mLog = LoggerFactory.getLogger(BufferDCFilter.class); + + /** + * Calculates the DC offset present in the samples and subtracts the offset from all samples. + */ + public static void filterReal(float[] samples) + { + double accumulator = 0.0; + + for(int x = 0; x < samples.length; x++) + { + accumulator += samples[x]; + } + + float adjustment = (float)(accumulator / (double)samples.length); + + for(int x = 0; x < samples.length; x++) + { + samples[x] = samples[x] - adjustment; + } + } + + /** + * Calculates the DC offset present in the samples and subtracts the offset from all samples. + */ + public static void filterComplex(float[] samples) + { + double iAccumulator = 0.0; + double qAccumulator = 0.0; + + for(int x = 0; x < samples.length; x += 2) + { + iAccumulator += samples[x]; + qAccumulator += samples[x + 1]; + } + + double half = (double)samples.length / 2.0; + + float iAdjustment = (float)(iAccumulator / half); + float qAdjustment = (float)(qAccumulator / half); + + for(int x = 0; x < samples.length; x += 2) + { + samples[x] = samples[x] - iAdjustment; + samples[x + 1] = samples[x + 1] - qAdjustment; + } + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/dc/DCRemovalFilter_CB.java b/src/main/java/io/github/dsheirer/dsp/filter/dc/DCRemovalFilter_CB.java deleted file mode 100644 index 28deb686..00000000 --- a/src/main/java/io/github/dsheirer/dsp/filter/dc/DCRemovalFilter_CB.java +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package io.github.dsheirer.dsp.filter.dc; - -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.complex.ComplexBuffer; - -public class DCRemovalFilter_CB implements Listener -{ - private Listener mListener; - private DCRemovalFilter mIFilter; - private DCRemovalFilter mQFilter; - - public DCRemovalFilter_CB( float ratio ) - { - mIFilter = new DCRemovalFilter( ratio ); - mQFilter = new DCRemovalFilter( ratio ); - } - - public void dispose() - { - mListener = null; - } - - public float[] filter( float[] samples ) - { - for( int x = 0; x < samples.length; x += 2 ) - { - samples[ x ] = mIFilter.filter( samples[ x ] ); - samples[ x + 1 ] = mQFilter.filter( samples[ x + 1 ] ); - } - - return samples; - } - - public ComplexBuffer filter( ComplexBuffer buffer ) - { - filter( buffer.getSamples() ); - - return buffer; - } - - @Override - public void receive( ComplexBuffer buffer ) - { - if( mListener != null ) - { - mListener.receive( filter( buffer ) ); - } - } - - public void setListener( Listener listener ) - { - mListener = listener; - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/dc/DCRemovalFilter_R_R.java b/src/main/java/io/github/dsheirer/dsp/filter/dc/DCRemovalFilter_R_R.java deleted file mode 100644 index ee5c8c53..00000000 --- a/src/main/java/io/github/dsheirer/dsp/filter/dc/DCRemovalFilter_R_R.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package io.github.dsheirer.dsp.filter.dc; - -import io.github.dsheirer.sample.real.RealSampleListener; - -public class DCRemovalFilter_R_R extends DCRemovalFilter implements RealSampleListener -{ - RealSampleListener mListener; - - public DCRemovalFilter_R_R( float ratio ) - { - super( ratio ); - } - - public void dispose() - { - mListener = null; - } - - @Override - public void receive( float sample ) - { - if( mListener != null ) - { - mListener.receive( filter( sample ) ); - } - } - - public void setListener( RealSampleListener listener ) - { - mListener = listener; - } - - public void removeListener() - { - mListener = null; - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/mixer/AbstractOscillator.java b/src/main/java/io/github/dsheirer/dsp/mixer/AbstractOscillator.java index 6969ad78..b0432b2e 100644 --- a/src/main/java/io/github/dsheirer/dsp/mixer/AbstractOscillator.java +++ b/src/main/java/io/github/dsheirer/dsp/mixer/AbstractOscillator.java @@ -79,6 +79,10 @@ public abstract class AbstractOscillator implements IOscillator return mEnabled; } + /** + * Commands sub-class implementations to update internal structures to account for changes in frequency or + * sample rate. The boolean reset indicator optionally commands a reset of the current I/Q values. + */ protected abstract void update(); /** diff --git a/src/main/java/io/github/dsheirer/dsp/mixer/FS4DownConverter.java b/src/main/java/io/github/dsheirer/dsp/mixer/FS4DownConverter.java new file mode 100644 index 00000000..5c70593e --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/mixer/FS4DownConverter.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * sdr-trunk + * Copyright (C) 2014-2018 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 io.github.dsheirer.dsp.mixer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FS4DownConverter +{ + private final static Logger mLog = LoggerFactory.getLogger(FS4DownConverter.class); + + private int mPointer = 0; + + /** + * Performs complex down-sampling by one-fourth (-FS/4) of the sample rate using simple value assignments. + * + * See: Digital Signal Processing 3e, Lyons, p.674-675 + */ + public FS4DownConverter() + { + } + + public float[] mixComplex(float[] samples) + { + float real; + + for(int x = 0; x < samples.length; x += 2) + { + switch(mPointer) + { + case 0: + //no-op + break; + case 1: + real = samples[x]; + samples[x] = samples[x + 1]; + samples[x + 1] = -real; + break; + case 2: + samples[x] = -samples[x]; + samples[x + 1] = -samples[x + 1]; + break; + case 3: + real = samples[x]; + samples[x] = -samples[x + 1]; + samples[x + 1] = real; + break; + } + + mPointer++; + + if(mPointer >= 4) + { + mPointer = 0; + } + } + + return samples; + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/mixer/FS4UpConverter.java b/src/main/java/io/github/dsheirer/dsp/mixer/FS4UpConverter.java new file mode 100644 index 00000000..c57abaaa --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/mixer/FS4UpConverter.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * sdr-trunk + * Copyright (C) 2014-2018 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 io.github.dsheirer.dsp.mixer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FS4UpConverter +{ + private final static Logger mLog = LoggerFactory.getLogger(FS4UpConverter.class); + + private int mPointer = 0; + + /** + * Performs complex up-sampling by one-fourth (+FS/4) of the sample rate using simple value assignments. + * + * See: Digital Signal Processing 3e, Lyons, p.674-675 + */ + public FS4UpConverter() + { + } + + public float[] mixComplex(float[] samples) + { + float real; + + for(int x = 0; x < samples.length; x += 2) + { + switch(mPointer) + { + case 0: + //no-op + break; + case 1: + real = samples[x]; + samples[x] = -samples[x + 1]; + samples[x + 1] = real; + break; + case 2: + samples[x] = -samples[x]; + samples[x + 1] = -samples[x + 1]; + break; + case 3: + real = samples[x]; + samples[x] = samples[x + 1]; + samples[x + 1] = -real; + break; + } + + mPointer++; + + if(mPointer >= 4) + { + mPointer = 0; + } + } + + return samples; + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/mixer/LowPhaseNoiseOscillator.java b/src/main/java/io/github/dsheirer/dsp/mixer/LowPhaseNoiseOscillator.java index 86d4b786..76171c27 100644 --- a/src/main/java/io/github/dsheirer/dsp/mixer/LowPhaseNoiseOscillator.java +++ b/src/main/java/io/github/dsheirer/dsp/mixer/LowPhaseNoiseOscillator.java @@ -31,6 +31,7 @@ public class LowPhaseNoiseOscillator extends AbstractOscillator private double mCosineAngle; private double mSineAngle; private double mGain = 1.0; + private boolean mReset; /** * Ultra-low phase noise complex oscillator as described in Digital Signal Processing 3e, Lyons, p.786 @@ -45,6 +46,19 @@ public class LowPhaseNoiseOscillator extends AbstractOscillator update(); } + /** + * Sets the frequency of the tone generated by this oscillator. + */ + @Override + public void setFrequency(double frequency) + { + //This oscillator does not recover when the current frequency is equal to exactly half the sample rate, so we + //have to set a flag to reset the I/Q structures when changing the frequency from exactly half the sample rate. + mReset = getFrequency() < 0.01 || Math.abs(getFrequency()) - (getSampleRate() / 2) < 1.0; + + super.setFrequency(frequency); + } + @Override public float quadrature() { @@ -64,8 +78,17 @@ public class LowPhaseNoiseOscillator extends AbstractOscillator { double anglePerSample = TWO_PI * getFrequency() / getSampleRate(); - mCosineAngle = (float)Math.cos(anglePerSample); - mSineAngle = (float)Math.sin(anglePerSample); + mCosineAngle = Math.cos(anglePerSample); + mSineAngle = Math.sin(anglePerSample); + + if(mReset) + { + mInphase = 1.0; + mQuadrature = 0.0; + mPreviousInphase = 1.0; + mPreviousQuadrature = 0.0; + mReset = false; + } } /** diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java index 3a3550df..9280ac4b 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java @@ -54,6 +54,8 @@ public abstract class ChannelSourceManager implements ISourceEventProcessor */ public abstract TunerChannelSource getSource(TunerChannel tunerChannel); + public abstract TunerChannelSource getSpecialSource(int index); + /** * Adds a listener to receive source events */ diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/TunerChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/TunerChannelSourceManager.java index e6159a0b..510aba97 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/manager/TunerChannelSourceManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/TunerChannelSourceManager.java @@ -49,6 +49,13 @@ public class TunerChannelSourceManager extends ChannelSourceManager mTunerController.addListener(mPolyphaseChannelManager); } + @Override + public TunerChannelSource getSpecialSource(int index) + { + //TODO: remove this debug method + return mPolyphaseChannelManager.getSpecialChannel(index); + } + /** * Indicates if the channel min/max frequencies are within the tunable frequency range of the tuner controller * diff --git a/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java b/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java index 01b3ee1f..fa0cb3f3 100644 --- a/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java +++ b/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java @@ -21,6 +21,7 @@ import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.SampleType; import io.github.dsheirer.sample.complex.ComplexBuffer; +import io.github.dsheirer.sample.real.IOverflowListener; import io.github.dsheirer.source.ISourceEventProcessor; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.spectrum.converter.DFTResultsConverter; @@ -83,6 +84,15 @@ public class DFTProcessor implements Listener, ISourceEventProces mWindow = null; } + /** + * Sets the listener to receive buffer overflow/reset indications + * @param listener + */ + public void setOverflowListener(IOverflowListener listener) + { + mOverflowableBufferStream.setOverflowListener(listener); + } + public WindowType getWindowType() { return mWindowType;