wip-channelizer

This commit is contained in:
Denny
2017-05-13 10:16:18 -04:00
parent 7c6b7494b8
commit f461ed9cdb
10 changed files with 734 additions and 136 deletions

View File

@@ -92,9 +92,11 @@ public abstract class AbstractComplexPolyphaseChannelizer implements Listener<Co
*/
protected void dispatch(float[] channels)
{
ComplexBuffer complexChannelsBuffer = new ComplexBuffer(channels);
for(PolyphaseChannelSource channel : mChannels)
{
channel.processChannels(channels);
channel.processChannels(complexChannelsBuffer);
}
}

View File

@@ -33,9 +33,12 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class Channelizer implements ISourceEventProcessor, Listener<SourceEvent>
{
private final static Logger mLog = LoggerFactory.getLogger(Channelizer.class);
private static final int CHANNEL_BANDWIDTH = 12500;
private static final double CHANNEL_OVERSAMPLING = 2.0;
private Tuner mTuner;
private List<PolyphaseChannelSource> mChannelSources = new CopyOnWriteArrayList<>();
private ChannelizerConfiguration mChannelizerConfiguration;
/**
* Provides access to Digital Drop Channel (DDC) sources from the tuner. Incorporates a polyphase channelizer
@@ -46,12 +49,47 @@ public class Channelizer implements ISourceEventProcessor, Listener<SourceEvent>
public Channelizer(Tuner tuner)
{
mTuner = tuner;
int sampleRate = mTuner.getTunerController().getSampleRate();
if(sampleRate % 25000 != 0)
{
throw new IllegalArgumentException("Tuner sample rate [" + sampleRate + "] must be a multiple of 25 kHz");
}
int channelCount = sampleRate / CHANNEL_BANDWIDTH;
long frequency = mTuner.getTunerController().getFrequency();
mChannelizerConfiguration = new ChannelizerConfiguration(frequency, channelCount,
CHANNEL_BANDWIDTH, CHANNEL_OVERSAMPLING);
mTuner.addSourceEventListener(this);
}
/**
* 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)
{
//TODO: return a polyphase channel here
try
{
List<Integer> polyphaseIndexes = mChannelizerConfiguration.getPolyphaseChannelIndexes(tunerChannel);
}
catch(IllegalArgumentException iae)
{
mLog.info("Can't provide DDC for " + tunerChannel.toString() + " due to channelizer frequency [" +
" due to channelizer frequency [" + mChannelizerConfiguration.getCenterFrequency() +
"] and sample rate [" + (mChannelizerConfiguration.getChannelCount() *
mChannelizerConfiguration.getChannelBandwidth()) + "]");
}
return null;
}
@@ -121,6 +159,16 @@ public class Channelizer implements ISourceEventProcessor, Listener<SourceEvent>
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
}
break;
case NOTIFICATION_FREQUENCY_CHANGE:
mChannelizerConfiguration.setCenterFrequency(sourceEvent.getValue().longValue());
updateChannelResultsProcessors();
break;
case NOTIFICATION_SAMPLE_RATE_CHANGE:
int sampleRate = sourceEvent.getValue().intValue();
int channelCount = sampleRate / mChannelizerConfiguration.getChannelBandwidth();
mChannelizerConfiguration.setChannelCount(channelCount);
updateChannelResultsProcessors();
break;
}
@@ -130,6 +178,11 @@ public class Channelizer implements ISourceEventProcessor, Listener<SourceEvent>
@Override
public void receive(SourceEvent sourceEvent)
{
process(sourceEvent);
}
private void updateChannelResultsProcessors()
{
//Update the processors anytime that the center frequency or sample rate changes
}
}

View File

@@ -0,0 +1,310 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.filter.channelizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import source.tuner.TunerChannel;
import java.util.ArrayList;
import java.util.List;
public class ChannelizerConfiguration
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelizerConfiguration.class);
private long mCenterFrequency;
private int mChannelCount;
private int mChannelBandwidth;
private double mOversampling = 1.0;
/**
* Configuration details for a polyphase channelizer that can be used to identify which polyphase
* channelizer output channel(s) should be processed to produce an output channel of a specified
* center frequency and channel sample rate/bandwidth.
*
* @param centerFrequency
* @param channelCount
* @param channelBandwidth
* @param oversampling set to 1.0 for no oversample, or greater than 1.0 for oversampling
*/
public ChannelizerConfiguration(long centerFrequency, int channelCount, int channelBandwidth, double oversampling)
{
mCenterFrequency = centerFrequency;
mChannelCount = channelCount;
mChannelBandwidth = channelBandwidth;
mOversampling = oversampling;
}
/**
* Center frequency for the channelizer
*/
public long getCenterFrequency()
{
return mCenterFrequency;
}
/**
* Sets or changes the center frequency for the channelizer
*/
public void setCenterFrequency(long frequency)
{
mCenterFrequency = frequency;
}
/**
* Number of channels being channelized by the polyphase channelizer
*/
public int getChannelCount()
{
return mChannelCount;
}
/**
* Sets the number of channels being channelized by the polyphase channelizer
*/
public void setChannelCount(int channelCount)
{
mChannelCount = channelCount;
}
/**
* Channel bandwidth. Note: this value may be smaller than the channel sample rate when oversampling
* is being used.
*/
public int getChannelBandwidth()
{
return mChannelBandwidth;
}
/**
* Half of the channel bandwidth.
*/
public int getHalfChannelBandwidth()
{
return mChannelBandwidth / 2;
}
/**
* Sets the channel bandwidth in hertz.
*/
public void setChannelBandwidth(int channelBandwidth)
{
mChannelBandwidth = channelBandwidth;
}
/**
* 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 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();
}
/**
* Channelizer input sample rate
* @return input sample rate in hertz
*/
public int getSampleRate()
{
return getChannelBandwidth() * getChannelCount();
}
/**
* Channelizer input minimum frequency
* @return minimum frequency in hertz
*/
public long getMinimumFrequency()
{
return getCenterFrequency() - (getSampleRate() / 2);
}
/**
* Channelizer input maximum frequency
* @return maximum frequency in hertz
*/
public long getMaximumFrequency()
{
return getCenterFrequency() + (getSampleRate() / 2);
}
/**
* Identifies the channelizer output channel(s) 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 channels 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<Integer> getPolyphaseChannelIndexes(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() + "]");
}
List<Integer> indices = new ArrayList<>();
long startFrequencyOffset = (tunerChannel.getMinFrequency() - getMinimumFrequency());
int startIndex = (int)(startFrequencyOffset / getChannelBandwidth());
long endFrequencyOffset = (tunerChannel.getMaxFrequency() - getMinimumFrequency());
int endIndex = (int)(endFrequencyOffset / getChannelBandwidth());
int currentIndex = startIndex;
while(currentIndex <= endIndex)
{
long currentIndexMin = getMinimumFrequencyForChannelIndex(currentIndex);
long currentIndexMax = getMaximumFrequencyForChannelIndex(currentIndex);
//Case 1: tuner channel completely aligns with indexed channel
if(currentIndexMin <= tunerChannel.getMinFrequency() && tunerChannel.getMaxFrequency() <= currentIndexMax)
{
indices.add(currentIndex);
}
//Case 2: tuner channel min frequency aligns and max frequency exceeds
else if(currentIndexMin <= tunerChannel.getMinFrequency() &&
tunerChannel.getMinFrequency() < currentIndexMax &&
tunerChannel.getMaxFrequency() > currentIndexMax)
{
indices.add(currentIndex);
}
//Case 3: tuner channel min frequency exceeds and max frequency aligns
else if(tunerChannel.getMinFrequency() < currentIndexMin &&
tunerChannel.getMaxFrequency() > currentIndexMin &&
tunerChannel.getMaxFrequency() <= currentIndexMax)
{
indices.add(currentIndex);
}
//Case 4: tuner channel encapsulates the indexed channel
else if(tunerChannel.getMinFrequency() < currentIndexMin && tunerChannel.getMaxFrequency() > currentIndexMax)
{
indices.add(currentIndex);
}
currentIndex++;
}
return indices;
}
/**
* 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 long getCenterFrequencyForChannelIndex(int index)
{
if(index < 0 || index >= getChannelCount())
{
throw new IllegalArgumentException("Illegal channel index");
}
return getMinimumFrequency() + getHalfChannelBandwidth() + (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.
* @return frequency of the minimum band edge
*/
public long getMinimumFrequencyForChannelIndex(int index)
{
if(index < 0 || index > getChannelCount())
{
throw new IllegalArgumentException("Can't determine minimum frequency - illegal channel index [" +
index + "]");
}
return getMinimumFrequency() + (index * getChannelBandwidth());
}
/**
* 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.
* @return frequency of the maximum band edge
*/
public long getMaximumFrequencyForChannelIndex(int index)
{
if(index < 0 || index > getChannelCount())
{
throw new IllegalArgumentException("Can't determine maximum frequency - illegal channel index [" +
index + "]");
}
//Maximum frequency is the same as the minimum frequency of the next higher indexed channel
return getMinimumFrequency() + ((index + 1) * getChannelBandwidth());
}
public static void main(String[] args)
{
mLog.debug("Starting ....");
int channelCount = 10;
ChannelizerConfiguration channelizerConfiguration =
new ChannelizerConfiguration(100062500, channelCount, 12500, 2.0);
for(int x = 0; x < channelCount; x++)
{
mLog.debug("Channel " + x + ": " + channelizerConfiguration.getMinimumFrequencyForChannelIndex(x) + " - " +
channelizerConfiguration.getMaximumFrequencyForChannelIndex(x) +
" Center: " + channelizerConfiguration.getCenterFrequencyForChannelIndex(x));
}
TunerChannel tunerChannel = new TunerChannel(100006250, 12500);
List<Integer> indices = channelizerConfiguration.getPolyphaseChannelIndexes(tunerChannel);
mLog.debug("Indices: " + indices);
mLog.debug("Finished!");
}
}

View File

@@ -224,7 +224,7 @@ public class ChannelizerViewer extends JFrame
mPolyphaseChannelizer = new ComplexPolyphaseChannelizer(taps, mChannelCount, CHANNEL_BANDWIDTH);
mChannelDistributor = new ChannelDistributor(bufferSize, mChannelCount);
mPolyphaseChannelizer.setChannelDistributor(mChannelDistributor);
// mPolyphaseChannelizer.setChannelDistributor(mChannelDistributor);
init();
}

View File

@@ -20,16 +20,15 @@ package dsp.filter.channelizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.OverflowableTransferQueue;
import sample.complex.ComplexBuffer;
import sample.real.IOverflowListener;
import source.ISourceEventProcessor;
import source.SourceEvent;
import source.SourceException;
import source.tuner.ComplexChannelSource;
import source.tuner.TunerChannel;
import java.util.concurrent.ScheduledExecutorService;
public class PolyphaseChannelSource extends ComplexChannelSource
{
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class);
@@ -40,6 +39,7 @@ public class PolyphaseChannelSource extends ComplexChannelSource
private int mChannelSampleRate;
private int mChannelCount;
private int mOutputFrequencyCorrection;
private ChannelBufferProcessor mProcessor = new ChannelBufferProcessor();
/**
* Polyphase channelizer channel implementation. Adapts the channel array output samples from the polyphase
@@ -86,13 +86,11 @@ public class PolyphaseChannelSource extends ComplexChannelSource
/**
* Process the array samples for each of the output channels from the polyphase channelizer.
* @param channels
* @param channelsBuffer
*/
public void processChannels(float[] channels)
public void processChannels(ComplexBuffer channelsBuffer)
{
//TODO we have to dispatch full buffers on a separate thread so that we don't bog down the channelizer
//TODO thread that is delivering the channel samples to us.
mProcessor.process(channelsBuffer);
}
/**
@@ -198,4 +196,46 @@ public class PolyphaseChannelSource extends ComplexChannelSource
{
}
public class ChannelBufferProcessor implements Runnable
{
private OverflowableTransferQueue<ComplexBuffer> mComplexChannelBuffers;
public ChannelBufferProcessor()
{
mComplexChannelBuffers = new OverflowableTransferQueue<>(500, 10);
mComplexChannelBuffers.setOverflowListener(new IOverflowListener()
{
@Override
public void sourceOverflow(boolean overflow)
{
broadcastOverflowState(overflow);
}
});
}
/**
* Enqueues the polyphase analysis filter's channel output for post channelizer mixing, filtering
* and resampling operations and buffers output for dispatch to downstream processing.
*
* @param channelsBuffer to enqueue for processing
*/
public void process(ComplexBuffer channelsBuffer)
{
mComplexChannelBuffers.offer(channelsBuffer);
}
@Override
public void run()
{
try
{
}
catch(Throwable throwable)
{
mLog.error("Error while processing polyphase channel samples", throwable);
}
}
}
}

View File

@@ -0,0 +1,31 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.filter.channelizer.output;
public class ChannelOutputProcessorFactory
{
/**
* Factory for obtaining polyphase channelizer results processors to perform any post-processing
* that may be required to perform frequency translation and/or resampling of channel results.
*/
public static IPolyphaseChannelOutputProcessor getProcessor(int sampleRate, long frequency)
{
return null;
}
}

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.filter.channelizer.output;
import sample.complex.IComplexSampleListener;
public interface IPolyphaseChannelOutputProcessor
{
/**
* Process the channel output channel results from the polyphase channelizer filter
*
* @param channelResults to process
* @param listener to receive the processed channel results
*/
void process(float[] channelResults, IComplexSampleListener listener);
/**
* Specifies the positive or negative frequency correction that should be applied to samples extracted from
* the polyphase channelizer output results.
* @param frequency
*/
void setFrequencyCorrection(long frequency);
}

View File

@@ -0,0 +1,105 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.filter.channelizer.output;
import dsp.mixer.Oscillator;
import sample.complex.Complex;
import sample.complex.IComplexSampleListener;
import java.util.List;
public class SingleChannelOutputProcessor implements IPolyphaseChannelOutputProcessor
{
private int mChannelOffset;
private Oscillator mFrequencyCorrectionMixer;
/**
* 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 SingleChannelOutputProcessor(int sampleRate, List<Integer> channelIndexes)
{
mFrequencyCorrectionMixer = new Oscillator(0, sampleRate);
setPolyphaseChannelIndices(channelIndexes);
}
/**
* Updates this processor to extract a 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<Integer> 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;
}
/**
* Extracts the channel from the channel results array, applies frequency translation, and delivers the
* extracted frequency-translated channel sample 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(float[] channelResults, IComplexSampleListener listener)
{
if(channelResults.length >= (mChannelOffset + 1))
{
//Only perform frequency translation if the frequency correction mixer has a non-zero frequency value
if(mFrequencyCorrectionMixer.isEnabled())
{
mFrequencyCorrectionMixer.rotate();
float i = Complex.multiplyInphase(channelResults[mChannelOffset], channelResults[mChannelOffset + 1],
mFrequencyCorrectionMixer.inphase(), mFrequencyCorrectionMixer.quadrature());
float q = Complex.multiplyQuadrature(channelResults[mChannelOffset], channelResults[mChannelOffset + 1],
mFrequencyCorrectionMixer.inphase(), mFrequencyCorrectionMixer.quadrature());
listener.receive(i, q);
}
else
{
listener.receive(channelResults[mChannelOffset], channelResults[mChannelOffset + 1]);
}
}
}
/**
* Specifies a frequency correction/translation value to be applied to channel samples output from the
* polyphase channelizer.
* @param frequency
*/
@Override
public void setFrequencyCorrection(long frequency)
{
mFrequencyCorrectionMixer.setFrequency(frequency);
}
}

View File

@@ -1,135 +1,152 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.mixer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.complex.Complex;
import sample.complex.ComplexBuffer;
public class Oscillator
{
private final static Logger mLog = LoggerFactory.getLogger( Oscillator.class );
private final static Logger mLog = LoggerFactory.getLogger(Oscillator.class);
private double mFrequency;
private double mSampleRate;
private boolean mEnabled = true;
private double mFrequency;
private double mSampleRate;
private Complex mAnglePerSample;
private Complex mCurrentAngle = new Complex( 0.0f, -1.0f );
private Complex mAnglePerSample;
private Complex mCurrentAngle = new Complex(0.0f, -1.0f);
/**
* Oscillator produces complex or float samples corresponding to a sine wave
* oscillating at the specified frequency and sample rate
*
* @param frequency - positive or negative frequency in hertz
* @param sampleRate - in hertz
*/
public Oscillator( long frequency, int sampleRate )
{
mSampleRate = (double)sampleRate;
mFrequency = (double)frequency;
update();
}
/**
* Updates the internal values after a frequency or sample rate change
*/
private void update()
{
float anglePerSample =
(float)( 2.0d * Math.PI * mFrequency / mSampleRate );
/**
* Oscillator produces complex or float samples corresponding to a sine wave
* oscillating at the specified frequency and sample rate
*
* @param frequency - positive or negative frequency in hertz
* @param sampleRate - in hertz
*/
public Oscillator(long frequency, int sampleRate)
{
mEnabled = frequency != 0;
mAnglePerSample = Complex.fromAngle( anglePerSample );
}
mSampleRate = (double)sampleRate;
mFrequency = (double)frequency;
/**
* Sets or changes the frequency of this oscillator
*/
public void setFrequency( long frequency )
{
mFrequency = (double)frequency;
update();
}
update();
}
/**
* Sets or changes the sample rate of this oscillator
*/
public void setSampleRate( int sampleRate )
{
mSampleRate = (double)sampleRate;
update();
}
/**
* Indicates if this oscillator has a non-zero frequency setting. When used as a frequency translating
* mixer, setting a frequency of zero indicates that no frequency correction is needed, so this flag can
* indicate when to apply the oscillator to frequency correct samples.
*
* @return true if the current frequency value is non-zero.
*/
public boolean isEnabled()
{
return mEnabled;
}
/**
* Steps the current angle by the angle per sample amount
*/
public void rotate()
{
mCurrentAngle.multiply( mAnglePerSample );
mCurrentAngle.fastNormalize();
}
/**
* Updates the internal values after a frequency or sample rate change
*/
private void update()
{
float anglePerSample =
(float)(2.0d * Math.PI * mFrequency / mSampleRate);
public float inphase()
{
return mCurrentAngle.inphase();
}
public float quadrature()
{
return mCurrentAngle.quadrature();
}
/**
* Get next complex sample
*/
public Complex getComplex()
{
return mCurrentAngle.copy();
}
mAnglePerSample = Complex.fromAngle(anglePerSample);
}
/**
* Get the next float sample
*/
public float getFloat()
{
return mCurrentAngle.real();
}
/**
* Sets or changes the frequency of this oscillator
*/
public void setFrequency(long frequency)
{
mEnabled = frequency != 0;
mFrequency = (double)frequency;
update();
}
/**
* Generates the requested number of complex samples and returns them in a complex buffer
* @param sampleCount to generate
* @return complex buffer containing the requested complex samples
*/
public ComplexBuffer generateComplexBuffer(int sampleCount)
{
float[] buffer = new float[sampleCount * 2];
/**
* Sets or changes the sample rate of this oscillator
*/
public void setSampleRate(int sampleRate)
{
mSampleRate = (double)sampleRate;
update();
}
for(int x = 0; x < sampleCount; x++)
{
int index = 2 * x;
buffer[index] = inphase();
buffer[index + 1] = quadrature();
/**
* Steps the current angle by the angle per sample amount
*/
public void rotate()
{
mCurrentAngle.multiply(mAnglePerSample);
mCurrentAngle.fastNormalize();
}
rotate();
}
public float inphase()
{
return mCurrentAngle.inphase();
}
return new ComplexBuffer(buffer);
}
public float quadrature()
{
return mCurrentAngle.quadrature();
}
/**
* Get next complex sample
*/
public Complex getComplex()
{
return mCurrentAngle.copy();
}
/**
* Get the next float sample
*/
public float getFloat()
{
return mCurrentAngle.real();
}
/**
* Generates the requested number of complex samples and returns them in a complex buffer
*
* @param sampleCount to generate
* @return complex buffer containing the requested complex samples
*/
public ComplexBuffer generateComplexBuffer(int sampleCount)
{
float[] buffer = new float[sampleCount * 2];
for(int x = 0; x < sampleCount; x++)
{
int index = 2 * x;
buffer[index] = inphase();
buffer[index + 1] = quadrature();
rotate();
}
return new ComplexBuffer(buffer);
}
}

View File

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