mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
WIP - Adds FS/4 up and down converter classes.
This commit is contained in:
@@ -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<ComplexBuffer>()
|
||||
{
|
||||
mSource.setListener(new Listener<ComplexBuffer>()
|
||||
@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<ComplexBuffer>, 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<ComplexBuffer>()
|
||||
{
|
||||
@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);
|
||||
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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<Integer> 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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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<ComplexBuffer>
|
||||
{
|
||||
private Listener<ComplexBuffer> 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<ComplexBuffer> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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<ComplexBuffer>, 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;
|
||||
|
||||
Reference in New Issue
Block a user