Creates new HeterodyneChannelSourceManager that uses frequency translation

and CIC decimation for each channel provided by the tuner.
This commit is contained in:
Denny
2018-09-04 08:16:57 -04:00
parent f963d821a1
commit b8a46af836
26 changed files with 1568 additions and 137 deletions

View File

@@ -21,7 +21,7 @@ import io.github.dsheirer.source.ISourceEventProcessor;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.tuner.TunerEvent.Event;
import io.github.dsheirer.source.tuner.manager.ChannelSourceManager;
import io.github.dsheirer.source.tuner.manager.TunerChannelSourceManager;
import io.github.dsheirer.source.tuner.manager.HeterodyneChannelSourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,7 +51,9 @@ public abstract class Tuner implements ISourceEventProcessor
//Register to receive frequency and sample rate change notifications
mTunerController.addListener(this::process);
mChannelSourceManager = new TunerChannelSourceManager(mTunerController);
// mChannelSourceManager = new TunerChannelSourceManager(mTunerController);
mChannelSourceManager = new HeterodyneChannelSourceManager(mTunerController);
//Register to receive channel count change notifications
mChannelSourceManager.addSourceEventListener(this::process);

View File

@@ -289,15 +289,7 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
}
case CHANNEL_COUNT:
int channelCount = tuner.getChannelSourceManager().getTunerChannelCount();
if(channelCount > 0)
{
return channelCount + " (LOCKED)";
}
else
{
return "0";
}
return channelCount + " (" + (tuner.getTunerController().isLocked() ? "LOCKED)" : "UNLOCKED)");
case FREQUENCY_ERROR:
double ppm = tuner.getTunerController().getFrequencyCorrection();
return mFrequencyErrorPPMFormat.format(ppm);

View File

@@ -0,0 +1,239 @@
/*******************************************************************************
* 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.source.tuner.channel;
import io.github.dsheirer.dsp.filter.cic.ComplexPrimeCICDecimate2;
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
import io.github.dsheirer.dsp.mixer.IOscillator;
import io.github.dsheirer.dsp.mixer.LowPhaseNoiseOscillator;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.OverflowableReusableBufferTransferQueue;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.sample.buffer.ReusableComplexBufferQueue;
import io.github.dsheirer.sample.complex.Complex;
import io.github.dsheirer.source.SourceEvent;
import java.util.ArrayList;
import java.util.List;
/**
* Complex sample source that provides a frequency-translated and decimated sample buffer stream. Uses a
* CIC decimation filter that requires the decimation rate to be an integer multiple. Sample buffer processing
* occurs on a scheduled runnable thread.
*/
public class CICTunerChannelSource2 extends TunerChannelSource implements Listener<ReusableComplexBuffer>
{
// private final static Logger mLog = LoggerFactory.getLogger(CICTunerChannelSource2.class);
//Maximum number of filled buffers for the blocking queue
private static final int BUFFER_MAX_CAPACITY = 300;
//Threshold for resetting buffer overflow condition
private static final int BUFFER_OVERFLOW_RESET_THRESHOLD = 100;
//Pass band cutoff frequency for the complex channel - target = 12.5 kHz channel
private static final int CHANNEL_PASS_FREQUENCY = 6000;
//Stop band frequency for the complex channel
private static final int CHANNEL_STOP_FREQUENCY = 6700;
private OverflowableReusableBufferTransferQueue<ReusableComplexBuffer> mBuffer;
private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("CICTunerChannelSource");
private IOscillator mFrequencyCorrectionMixer;
private ComplexPrimeCICDecimate2 mDecimationFilter;
private Listener<ReusableComplexBuffer> mListener;
private List<ReusableComplexBuffer> mSampleBuffers = new ArrayList<>();
private double mChannelSampleRate;
private long mChannelFrequencyCorrection = 0;
private long mTunerFrequency;
/**
* Constructs a frequency translating and CIC decimating channel source.
*
* @param producerSourceEventListener to receive sample stream start/stop requests
* @param tunerChannel that details the desired channel frequency and bandwidth
* @param sampleRate of the incoming sample stream
* @param decimation to use in the CIC decimation filter.
* @throws FilterDesignException if a final cleanup filter cannot be designed using the remez filter
* designer and the filter parameters.
*/
public CICTunerChannelSource2(Listener<SourceEvent> producerSourceEventListener, TunerChannel tunerChannel,
double sampleRate, int decimation) throws FilterDesignException
{
super(producerSourceEventListener, tunerChannel);
mDecimationFilter = new ComplexPrimeCICDecimate2(sampleRate, decimation, CHANNEL_PASS_FREQUENCY, CHANNEL_STOP_FREQUENCY);
mBuffer = new OverflowableReusableBufferTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD);
//Setup the frequency mixer to the current source frequency
mChannelSampleRate = sampleRate / (double)decimation;
mTunerFrequency = tunerChannel.getFrequency();
long frequencyOffset = mTunerFrequency - getTunerChannel().getFrequency();
mFrequencyCorrectionMixer = new LowPhaseNoiseOscillator(frequencyOffset, sampleRate);
}
@Override
public void dispose()
{
//
}
/**
* Primary interface for receiving incoming complex sample buffers to be frequency translated and decimated.
*/
@Override
public void receive(ReusableComplexBuffer buffer)
{
mBuffer.offer(buffer);
}
/**
* Sets/updates the center frequency for the sample streaming being sent from the producer.
*
* @param frequency in hertz
*/
@Override
public void setFrequency(long frequency)
{
mTunerFrequency = frequency;
updateMixerFrequencyOffset();
}
/**
* Current frequency correction being applied to the channel sample stream
*
* @return correction in hertz
*/
public long getFrequencyCorrection()
{
return mChannelFrequencyCorrection;
}
/**
* Changes the frequency correction value and broadcasts the change to the registered downstream listener.
*
* @param correction current frequency correction value.
*/
public void setFrequencyCorrection(long correction)
{
mChannelFrequencyCorrection = correction;
updateMixerFrequencyOffset();
broadcastConsumerSourceEvent(SourceEvent.frequencyCorrectionChange(mChannelFrequencyCorrection));
}
/**
* Calculates the local mixer frequency offset from the tuned frequency,
* channel's requested frequency, and channel frequency correction.
*/
private void updateMixerFrequencyOffset()
{
long offset = mTunerFrequency - getTunerChannel().getFrequency() - mChannelFrequencyCorrection;
mFrequencyCorrectionMixer.setFrequency(offset);
}
/**
* Sets the sample rate of the incoming sample stream from the producer
*
* @param sampleRate in hertz
*/
@Override
protected void setSampleRate(double sampleRate)
{
//Not implemented. Sample rate changes are not permitted once sample stream starts
}
/**
* Sets the frequency correction for the outbound sample stream to the consumer
*
* @param correction in hertz
*/
@Override
protected void setChannelFrequencyCorrection(long correction)
{
mChannelFrequencyCorrection = correction;
broadcastConsumerSourceEvent(SourceEvent.channelFrequencyCorrectionChange(correction));
updateMixerFrequencyOffset();
}
/**
* Frequency correction value currently being applied to the outbound sample stream
*/
@Override
public long getChannelFrequencyCorrection()
{
return mChannelFrequencyCorrection;
}
/**
* Sets the listener to receive the complex buffer sample output from this channel
*
* @param complexBufferListener to receive complex buffers
*/
@Override
public void setListener(Listener<ReusableComplexBuffer> complexBufferListener)
{
mDecimationFilter.setListener(complexBufferListener);
}
@Override
public void removeListener(Listener<ReusableComplexBuffer> listener)
{
mDecimationFilter.removeListener();
}
@Override
public double getSampleRate()
{
return mChannelSampleRate;
}
/**
* Primary processing method that is invoked on a recurring basis to process any queued complex buffers.
*
* Mixes the target frequency to baseband and then passes the buffer to the CIC decimation filter
*/
protected void processSamples()
{
mBuffer.drainTo(mSampleBuffers);
for(ReusableComplexBuffer complexBuffer : mSampleBuffers)
{
float[] samples = complexBuffer.getSamples();
ReusableComplexBuffer translatedComplexBuffer = mReusableComplexBufferQueue.getBuffer(samples.length);
float[] translatedSamples = translatedComplexBuffer.getSamples();
/* Perform frequency translation */
for(int x = 0; x < samples.length; x += 2)
{
mFrequencyCorrectionMixer.rotate();
translatedSamples[x] = Complex.multiplyInphase(samples[x], samples[x + 1],
mFrequencyCorrectionMixer.inphase(), mFrequencyCorrectionMixer.quadrature());
translatedSamples[x + 1] = Complex.multiplyQuadrature(samples[x], samples[x + 1],
mFrequencyCorrectionMixer.inphase(), mFrequencyCorrectionMixer.quadrature());
}
mDecimationFilter.receive(translatedComplexBuffer);
complexBuffer.decrementUserCount();
}
mSampleBuffers.clear();
}
}

View File

@@ -66,7 +66,7 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
* Sets the center frequency for the sample streaming being sent from the producer.
* @param frequency in hertz
*/
protected abstract void setFrequency(long frequency);
public abstract void setFrequency(long frequency);
/**
* Sets the sample rate of the incoming sample stream from the producer
@@ -118,12 +118,7 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
{
//Broadcast current frequency and sample rate so consumer can configure correctly
broadcastConsumerSourceEvent(SourceEvent.frequencyChange(getFrequency()));
if(mProducerSourceEventListener != null)
{
mProducerSourceEventListener.receive(SourceEvent.startSampleStreamRequest(this));
}
broadcastProducerSourceEvent(SourceEvent.startSampleStreamRequest(this));
mScheduledIntervalProcessor.start();
}
@@ -133,12 +128,8 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
*/
public void stop()
{
if(mProducerSourceEventListener != null)
{
mProducerSourceEventListener.receive(SourceEvent.stopSampleStreamRequest(this));
mProducerSourceEventListener.receive(SourceEvent.sourceDisposeRequest(this));
}
broadcastProducerSourceEvent(SourceEvent.stopSampleStreamRequest(this));
broadcastProducerSourceEvent(SourceEvent.sourceDisposeRequest(this));
mScheduledIntervalProcessor.stop();
}
@@ -171,6 +162,7 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
{
switch(sourceEvent.getEvent())
{
case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
case NOTIFICATION_FREQUENCY_CHANGE:
case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_LOCKED:
case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED:

View File

@@ -0,0 +1,150 @@
/*******************************************************************************
* 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.source.tuner.manager;
import io.github.dsheirer.source.tuner.TunerController;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Calculates tuner center frequency for a set of tuner channels.
*/
public class CenterFrequencyCalculator
{
public static final long INVALID_FREQUENCY = -1;
/**
* Determines the optimal center frequency for a given tuner and set of tuner channels.
*
* @param tunerController that is providing tuner channels
* @param channels to calculate
* @return optimal center frequency or INVALID_FREQUENCY if a center frequency cannot be calculated.
*/
public static long getCenterFrequency(TunerController tunerController, SortedSet<TunerChannel> channels)
{
long candidateFrequency = INVALID_FREQUENCY;
boolean isValidCandidateFrequency = true;
//If there is only 1 channel set the center frequency so that the channel is positioned to the right of center
if(channels.size() == 1)
{
candidateFrequency = channels.first().getMinFrequency() - tunerController.getMiddleUnusableHalfBandwidth() + 1;
}
else
{
long minChannelFrequency = channels.first().getMinFrequency();
long maxChannelFrequency = channels.last().getMaxFrequency();
//Start by placing the highest frequency channel at the high end of the spectrum
candidateFrequency = maxChannelFrequency - (tunerController.getUsableBandwidth() / 2);
//Iterate the channels and make sure that none of them overlap the center DC spike buffer, if one exists
if(tunerController.getMiddleUnusableHalfBandwidth() > 0)
{
boolean processingRequired = true;
while(isValidCandidateFrequency && processingRequired)
{
processingRequired = false;
long minAvoid = candidateFrequency - tunerController.getMiddleUnusableHalfBandwidth();
long maxAvoid = candidateFrequency + tunerController.getMiddleUnusableHalfBandwidth();
//If any of the center channel(s) overlap the central DC spike avoid area, we'll iteratively
//increase the tuned frequency causing the set of channels to move left in the tuned bandwidth until
//we either find a good center tune frequency, or we walk the lowest frequency channel out of the
//minimum tuned range, in which case we'll throw an exception indicating we don't have a solution.
for(TunerChannel channel : channels)
{
if(channel.overlaps(minAvoid, maxAvoid))
{
//Calculate a tuned frequency adjustment that places this overlapping channel just to the
//left of the central DC spike avoid zone
long adjustment = channel.getMaxFrequency() - minAvoid + 1;
//If the candidate frequency doesn't push the lowest channel out of bounds, make adjustment
if(candidateFrequency + adjustment - (tunerController.getUsableBandwidth() / 2) <= minChannelFrequency)
{
candidateFrequency += adjustment;
processingRequired = true;
}
//Otherwise, punt and indicate that we can't find a center frequency or add the channel
else
{
isValidCandidateFrequency = false;
}
//break out of the for/each loop, so that we can start over again with all of the channels
break;
}
}
}
}
}
if(isValidCandidateFrequency)
{
return candidateFrequency;
}
return INVALID_FREQUENCY;
}
/**
* Indicates if channel along with all of the other currently sourced
* channels can fit within the tunable bandwidth.
*
*/
/**
*
* Indicates if channel along with all of the other currently sourced
* channels can fit within the tunable bandwidth.
* @param channel to test
* @param tunerController that will provide the channel
* @param channels is a current set of channels being sourced by the tuner controller
* @return
*/
public static boolean canTune(TunerChannel channel, TunerController tunerController, SortedSet<TunerChannel> channels)
{
//Make sure we're within the tunable frequency range of this tuner
if(tunerController.getMinFrequency() < channel.getMinFrequency() && tunerController.getMaxFrequency() > channel.getMaxFrequency())
{
//If this is the first lock, then we're good
if(channels.isEmpty())
{
return true;
}
else
{
SortedSet<TunerChannel> allChannels = new TreeSet<>(channels);
allChannels.add(channel);
//If the bandwidth of the channel set is less than or equal to the tuner's usable bandwidth, then
//check to see if we can find a valid center frequency
if(allChannels.last().getMaxFrequency() - allChannels.first().getMinFrequency() <= tunerController.getUsableBandwidth())
{
return getCenterFrequency(tunerController, allChannels) != INVALID_FREQUENCY;
}
}
}
return false;
}
}

View File

@@ -54,9 +54,6 @@ public abstract class ChannelSourceManager implements ISourceEventProcessor
*/
public abstract TunerChannelSource getSource(TunerChannel tunerChannel);
//TODO: remove this testing method when finished with polyphase channel manager
public abstract TunerChannelSource getTestingSource(int index);
/**
* Adds a listener to receive source events
*/

View File

@@ -0,0 +1,236 @@
/*******************************************************************************
* 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.source.tuner.manager;
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.TunerController;
import io.github.dsheirer.source.tuner.channel.CICTunerChannelSource2;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Channel provider for heterodyne and decimate method of channel provisioning.
*/
public class HeterodyneChannelSourceManager extends ChannelSourceManager
{
private final static Logger mLog = LoggerFactory.getLogger(HeterodyneChannelSourceManager.class);
private final static int OBJECTIVE_CHANNEL_SAMPLE_RATE = 30000;
private List<CICTunerChannelSource2> mChannelSources = new CopyOnWriteArrayList<>();
private SortedSet<TunerChannel> mTunerChannels = new TreeSet<>();
private TunerController mTunerController;
private ChannelSourceEventProcessor mChannelSourceEventProcessor = new ChannelSourceEventProcessor();
public HeterodyneChannelSourceManager(TunerController tunerController)
{
mTunerController = tunerController;
mTunerController.addListener(this);
}
@Override
public SortedSet<TunerChannel> getTunerChannels()
{
return mTunerChannels;
}
@Override
public int getTunerChannelCount()
{
return mTunerChannels.size();
}
@Override
public TunerChannelSource getSource(TunerChannel tunerChannel)
{
if(CenterFrequencyCalculator.canTune(tunerChannel, mTunerController, mTunerChannels))
{
int decimation = mTunerController.getBandwidth() / OBJECTIVE_CHANNEL_SAMPLE_RATE;
try
{
//Attempt to create the channel source first, in case we get a filter design exception
CICTunerChannelSource2 tunerChannelSource = new CICTunerChannelSource2(mChannelSourceEventProcessor,
tunerChannel, mTunerController.getSampleRate(), decimation);
//Add to the list of channel sources so that it will receive the tuner frequency change
mChannelSources.add(tunerChannelSource);
//Set the current tuner frequency
tunerChannelSource.setFrequency(mTunerController.getFrequency());
//Add to the channel list and update the tuner center frequency as needed
mTunerChannels.add(tunerChannel);
updateTunerFrequency();
//Lock the tuner controller frequency and sample rate
mTunerController.setLocked(true);
broadcast(SourceEvent.channelCountChange(getTunerChannelCount()));
return tunerChannelSource;
}
catch(FilterDesignException fde)
{
mLog.error("Error creating CIC tuner channel source - couldn't design cleanup filter", fde);
}
}
return null;
}
/**
* Calculates a new center frequency and updates the tuner center frequency
*/
private void updateTunerFrequency()
{
long centerFrequency = CenterFrequencyCalculator.getCenterFrequency(mTunerController, getTunerChannels());
if(centerFrequency == CenterFrequencyCalculator.INVALID_FREQUENCY)
{
mLog.error("Couldn't calculate center frequency for tuner and tuner channels");
return;
}
if(centerFrequency != mTunerController.getFrequency())
{
try
{
mTunerController.setFrequency(centerFrequency);
}
catch(SourceException se)
{
mLog.error("Couldn't update tuner center frequency to " + centerFrequency, se);
}
}
}
@Override
public void process(SourceEvent tunerSourceEvent) throws SourceException
{
switch(tunerSourceEvent.getEvent())
{
case NOTIFICATION_FREQUENCY_CHANGE:
//Tuner center frequency has changed - update channels
updateTunerFrequency(tunerSourceEvent.getValue().longValue());
break;
case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE:
//The tuner is self-correcting for PPM error - relay to channels
broadcastToChannels(tunerSourceEvent);
break;
case NOTIFICATION_SAMPLE_RATE_CHANGE:
case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_LOCKED:
case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED:
//no-op
break;
default:
mLog.info("Unrecognized Source Event received from tuner: " + tunerSourceEvent);
}
}
/**
* Broadcasts the source event to any channel
*/
private void broadcastToChannels(SourceEvent sourceEvent)
{
for(CICTunerChannelSource2 channelSource: mChannelSources)
{
try
{
channelSource.process(sourceEvent);
}
catch(Exception e)
{
mLog.error("Error broadcasting source event to channel: " + sourceEvent);
}
}
}
/**
* Updates all channel sources to use the new tuner center frequency
*
* @param tunerFrequency in hertz
*/
private void updateTunerFrequency(long tunerFrequency)
{
for(CICTunerChannelSource2 channelSource : mChannelSources)
{
channelSource.setFrequency(tunerFrequency);
}
}
/**
* Processes channel source events
*/
public class ChannelSourceEventProcessor implements Listener<SourceEvent>
{
@Override
public void receive(SourceEvent sourceEvent)
{
switch(sourceEvent.getEvent())
{
case REQUEST_START_SAMPLE_STREAM:
if(sourceEvent.getSource() instanceof CICTunerChannelSource2)
{
mTunerController.addBufferListener((CICTunerChannelSource2)sourceEvent.getSource());
}
break;
case REQUEST_STOP_SAMPLE_STREAM:
if(sourceEvent.getSource() instanceof CICTunerChannelSource2)
{
mTunerController.removeBufferListener((CICTunerChannelSource2)sourceEvent.getSource());
}
break;
case REQUEST_SOURCE_DISPOSE:
if(sourceEvent.getSource() instanceof CICTunerChannelSource2)
{
CICTunerChannelSource2 channelSource = (CICTunerChannelSource2)sourceEvent.getSource();
mChannelSources.remove(channelSource);
mTunerChannels.remove(channelSource.getTunerChannel());
channelSource.dispose();
//Unlock the tuner controller if there are no more channels
if(getTunerChannelCount() == 0)
{
mTunerController.setLocked(false);
}
broadcast(SourceEvent.channelCountChange(getTunerChannelCount()));
}
break;
case NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED:
//Rebroadcast so that the tuner source can process this event
broadcast(sourceEvent);
break;
case NOTIFICATION_CHANNEL_COUNT_CHANGE:
//Lock the tuner controller frequency & sample rate when we're processing channels
break;
default:
mLog.info("Unrecognized Source Event received from channel: " + sourceEvent);
break;
}
}
}
}

View File

@@ -49,13 +49,6 @@ public class TunerChannelSourceManager extends ChannelSourceManager
mTunerController.addListener(mPolyphaseChannelManager);
}
@Override
public TunerChannelSource getTestingSource(int index)
{
//TODO: remove this debug method
return mPolyphaseChannelManager.getTestChannel(index);
}
/**
* Indicates if the channel min/max frequencies are within the tunable frequency range of the tuner controller
*

View File

@@ -1241,7 +1241,7 @@ public abstract class RTL2832TunerController extends USBTunerController
RATE_1_200MHZ(0x0600, 1200000, "1.200 MHz"),
RATE_1_440MHZ(0x0500, 1440000, "1.440 MHz"),
RATE_1_600MHZ(0x0480, 1600000, "1.600 MHz"),
RATE_1_800MHZ(0x0480, 1800000, "1.800 MHz"),
RATE_1_800MHZ(0x0400, 1800000, "1.800 MHz"),
RATE_1_920MHZ(0x03C0, 1920000, "1.920 MHz"),
RATE_2_048MHZ(0x0384, 2048000, "2.048 MHz"),
RATE_2_304MHZ(0x0320, 2304000, "2.304 MHz"),

View File

@@ -144,5 +144,6 @@ public class TestTunerController extends TunerController
public void setSampleRate(int sampleRate) throws SourceException
{
mSampleGenerator.setSampleRate(sampleRate);
mFrequencyController.setSampleRate(sampleRate);
}
}