Compare commits

...

19 Commits

Author SHA1 Message Date
Denny
d95c78c0f1 Updates version to 0.4.0 alpha 5 2018-09-23 05:51:28 -04:00
Denny Sheirer
aee5ece8d2 Merge pull request #430 from DSheirer/429-polyphase-channelizer-25khz-channel-rate
429 polyphase channelizer 25khz channel rate
2018-09-23 05:50:18 -04:00
Denny
ba358a7d48 Adjusted costas loop gain values. Updated channel filter values to mitigate adjacent channel interference. 2018-09-23 05:34:41 -04:00
Denny
09b209496b Resolves issue with NPE in 3/4 rate decoder. 2018-09-22 09:19:15 -04:00
Denny Sheirer
cc209683bd Merge pull request #428 from DSheirer/377-npe-in-trellis-decoder
377 NPE in P25 3/4 Rate Trellis Decoder
2018-09-22 02:53:16 -04:00
Denny
2771c28dba Resolves issue with NPE in 3/4 rate decoder. 2018-09-22 02:52:36 -04:00
Denny
d752917073 Resolves #427. Moves the module(s) reset operation to the stop() method
so that the modules are reset after they are shut down, in order to
prepare for processing chain reuse.

Previously, the modules were reset upon startup, but after any traffic
channel metadata had already been transferred to the channel state,
causing the metadata to be lost and allowing initial audio packets to
stream without any metadata and related streaming/monitoring rules.
2018-09-20 04:01:45 -04:00
Denny
28bc02e172 Updates channel state to ensure teardown processing is completed during
a call end and/or during a commanded stop.
2018-09-19 04:25:37 -04:00
Denny
0f6044d48b Updates mutable metadata to reset the buffer overflow flag between uses. 2018-09-18 19:15:06 -04:00
Denny Sheirer
58ecb83e2c Merge pull request #425 from DSheirer/417-channel-overflow-state-processing
417 Channel Overflow State Processing - Part 2
2018-09-16 10:23:15 -04:00
Denny
9e1de922e4 Resolves issue with overflow state processing. Displays the overflow
state in the Message field of the channel metadata table column instead
of displaying it in the channel state column.
2018-09-16 10:22:27 -04:00
Denny Sheirer
84d0b827d8 Merge pull request #422 from DSheirer/421-nbfm-25-khz-channel-bandwidth
421 Add 25 kHz Channel Bandwidth Support to NBFM Decoder
2018-09-16 07:22:38 -04:00
Denny
a17132be0d Updates the NBFM decoder to allow user to select between 12.5 and 25.0
kHz channel bandwidth so that 25 kHz channels can be demodulated correctly.
2018-09-16 07:21:14 -04:00
Denny Sheirer
dae68515a5 Merge pull request #420 from DSheirer/419-heterdyne-channelizer-center-frequency-tuning
419 Heterodyne Channelizer Center Frequency Calculations
2018-09-16 06:04:09 -04:00
Denny
0aaec8ff54 Updates the heterodyne channelizer's center frequency calculator to
only update the center frequency when needed and to place the set of
decoding channels adjacent to the right side of the tuner's center
frequency when the complete set of channels fit into the usable half
bandwidth of the tuner.
2018-09-16 06:02:46 -04:00
Denny Sheirer
001883edc2 Merge pull request #418 from DSheirer/417-channel-overflow-zombie-state
417 Traffic Channels in Buffer Overflow state do not tear down correctly
2018-09-16 05:23:05 -04:00
Denny
335fb51456 Updates buffer overflow handling to occur coincident to other channel
states rather than being a channel state itself.  This was preventing
traffic channels that overflow from being torn down correctly.
2018-09-16 05:21:46 -04:00
Denny Sheirer
e94b6ae172 Merge pull request #416 from DSheirer/415-overflow-state
415 Buffer Overflow State Listener Registration
2018-09-14 06:46:34 -04:00
Denny
785125da4f Correctly implements the buffer overflow listener registration so that
decoding channels can be notified when the internal buffer goes into
and out of overflow state.
2018-09-14 06:45:37 -04:00
44 changed files with 762 additions and 516 deletions

View File

@@ -15,7 +15,7 @@ repositories {
mavenCentral()
}
version = '0.4.0-alpha.4'
version = '0.4.0-alpha.5'
sourceCompatibility = 1.8
sourceSets {

View File

@@ -20,6 +20,7 @@ package io.github.dsheirer.channel.metadata;
public enum Attribute
{
BUFFER_OVERFLOW,
CHANNEL_CONFIGURATION_NAME,
CHANNEL_CONFIGURATION_SITE,
CHANNEL_CONFIGURATION_SYSTEM,

View File

@@ -116,6 +116,16 @@ public class AttributeChangeRequest<T>
return null;
}
public Boolean getBooleanValue()
{
if(mValue instanceof Boolean)
{
return (Boolean)mValue;
}
return false;
}
/**
* Returns the value as a DecoderType or null if the value is not a DecoderType instance.
*/

View File

@@ -1,19 +1,16 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
* 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 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.
* 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/>
* 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.channel.metadata;
@@ -21,9 +18,11 @@ package io.github.dsheirer.channel.metadata;
import io.github.dsheirer.channel.state.State;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.EventQueue;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
@@ -32,6 +31,8 @@ import java.util.Map;
public class ChannelMetadataModel extends AbstractTableModel implements Listener<MutableMetadataChangeEvent>
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelMetadataModel.class);
private final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.00000" );
public static final int COLUMN_STATE = 0;
@@ -45,7 +46,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener
public static final int COLUMN_CONFIGURATION_NAME = 8;
public static final int COLUMN_MESSAGE = 9;
private static final String[] COLUMNS = {"State", "Decoder", "Channel", "Frequency", "Primary From", "Primary To",
private static final String[] COLUMNS = {"Status", "Decoder", "Channel", "Frequency", "Primary From", "Primary To",
"Secondary From", "Secondary To", "Channel Name", "Message"};
private List<MutableMetadata> mChannelMetadata = new ArrayList();
@@ -184,7 +185,11 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener
return null;
case COLUMN_MESSAGE:
if(metadata.hasMessageType() | metadata.hasMessage())
if(metadata.isBufferOverflow())
{
return "**OVERFLOW**";
}
else if(metadata.hasMessageType() || metadata.hasMessage())
{
StringBuilder sb = new StringBuilder();
if(metadata.hasMessageType())
@@ -248,6 +253,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener
case CHANNEL_STATE:
fireTableCellUpdated(rowIndex, COLUMN_STATE);
break;
case BUFFER_OVERFLOW:
case MESSAGE:
case MESSAGE_TYPE:
fireTableCellUpdated(rowIndex, COLUMN_MESSAGE);

View File

@@ -120,8 +120,6 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
mForegroundColors.put(State.FADE, Color.DARK_GRAY);
mBackgroundColors.put(State.IDLE, Color.WHITE);
mForegroundColors.put(State.IDLE, Color.DARK_GRAY);
mBackgroundColors.put(State.OVERFLOW, Color.RED);
mForegroundColors.put(State.OVERFLOW, Color.YELLOW);
mBackgroundColors.put(State.RESET, Color.PINK);
mForegroundColors.put(State.RESET, Color.YELLOW);
mBackgroundColors.put(State.TEARDOWN, Color.DARK_GRAY);

View File

@@ -41,6 +41,7 @@ public class Metadata
protected boolean mUpdated;
protected DecoderType mPrimaryDecoderType;
protected boolean mSelected;
protected boolean mBufferOverflow;
protected State mState = State.IDLE;
protected String mChannelConfigurationSystem;
protected String mChannelConfigurationSite;
@@ -173,6 +174,14 @@ public class Metadata
return mSelected;
}
/**
* Indicates if the channel for this metadata is in a buffer overflow state.
*/
public boolean isBufferOverflow()
{
return mBufferOverflow;
}
/**
* List of de-duplicated broadcast channels aggregated from the primary and secondary TO/FROM aliases.
*/

View File

@@ -80,6 +80,10 @@ public class MutableMetadata extends Metadata implements Listener<AttributeChang
{
switch(request.getAttribute())
{
case BUFFER_OVERFLOW:
mBufferOverflow = request.getBooleanValue();
broadcast(Attribute.BUFFER_OVERFLOW);
break;
case CHANNEL_CONFIGURATION_SYSTEM:
mChannelConfigurationSystem = request.getStringValue();
broadcast(Attribute.CHANNEL_CONFIGURATION_SYSTEM);
@@ -164,6 +168,7 @@ public class MutableMetadata extends Metadata implements Listener<AttributeChang
public void resetTemporalAttributes()
{
mDoNotRecord = null;
mBufferOverflow = false;
if(hasMessage())
{

View File

@@ -135,21 +135,8 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
@Override
public void stop()
{
mMutableMetadata.resetAllAttributes();
mTrafficChannelEndListener = null;
if(mTrafficChannelCallEvent != null)
{
mTrafficChannelCallEvent.end();
broadcast(mTrafficChannelCallEvent);
}
mTrafficChannelCallEvent = null;
processTeardownState();
mSquelchLocked = false;
setState(State.TEARDOWN);
}
public void dispose()
@@ -346,8 +333,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
break;
}
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE,
mSourceOverflow ? State.OVERFLOW : mState));
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
}
}
@@ -370,8 +356,19 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
{
mSourceOverflow = overflow;
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE,
mSourceOverflow ? State.OVERFLOW : mState));
mMutableMetadata.receive(new AttributeChangeRequest<Boolean>(Attribute.BUFFER_OVERFLOW, mSourceOverflow));
}
/**
* Indicates if this channel's sample buffer is in overflow state, meaning that the inbound sample
* stream is not being processed fast enough and samples are being thrown away until the processing can
* catch up.
*
* @return true if the channel is in overflow state.
*/
public boolean isOverflow()
{
return mSourceOverflow;
}
/**
@@ -410,9 +407,13 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
if(mTrafficChannelEndListener != null)
if(mTrafficChannelEndListener != null && mTrafficChannelCallEvent != null)
{
mTrafficChannelCallEvent.end();
broadcast(mTrafficChannelCallEvent);
mTrafficChannelEndListener.callEnd(mTrafficChannelCallEvent.getChannel());
mTrafficChannelEndListener = null;
mTrafficChannelCallEvent = null;
}
}

View File

@@ -34,7 +34,7 @@ public enum State
@Override
public boolean canChangeTo(State state)
{
return state == CALL |
return state == CALL ||
state == CONTROL ||
state == DATA ||
state == ENCRYPTED ||
@@ -108,8 +108,7 @@ public enum State
public boolean canChangeTo(State state)
{
return state != FADE &&
state != RESET &&
state != OVERFLOW;
state != RESET;
}
},
/**
@@ -121,25 +120,10 @@ public enum State
public boolean canChangeTo(State state)
{
return state != TEARDOWN &&
state != RESET &&
state != OVERFLOW;
}
},
/**
* Indicates that the channel's sample buffer has overflowed and new buffer samples are no longer being
* delivered to the channel. This state persists until the processing catches up and reduces the number
* of samples in the input buffer below a threshold value. Transition to and from this state is exclusively
* managed by the Channel State and no other channel states can be processed from the channel decoders
* until the channel state is taken out of overflow.
*/
OVERFLOW("OVERFLOW")
{
@Override
public boolean canChangeTo(State state)
{
return false;
state != RESET;
}
},
/**
* Indicates that the channel has been reset and is ready to use for a new decoding session.
*/
@@ -191,4 +175,10 @@ public enum State
{
return mDisplayValue;
}
@Override
public String toString()
{
return mDisplayValue;
}
}

View File

@@ -186,7 +186,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
if(frequency > 0)
{
Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(frequency,
mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
mDecodeConfiguration.getChannelSpecification().getBandwidth()));
if(channel != null)
{

View File

@@ -489,14 +489,14 @@ public class Channel extends Configuration implements Listener<SourceEvent>
SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration;
mTunerChannel = new TunerChannel(config.getFrequency(),
mDecodeConfiguration.getDecoderType().getChannelBandwidth());
mDecodeConfiguration.getChannelSpecification().getBandwidth());
}
else if(mSourceConfiguration.getSourceType() == SourceType.RECORDING)
{
SourceConfigRecording config = (SourceConfigRecording)mSourceConfiguration;
mTunerChannel = new TunerChannel(config.getFrequency(),
mDecodeConfiguration.getDecoderType().getChannelBandwidth());
mDecodeConfiguration.getChannelSpecification().getBandwidth());
}
}

View File

@@ -212,8 +212,7 @@ public class ChannelProcessingManager implements ChannelEventListener
try
{
source = mSourceManager.getSource(channel.getSourceConfiguration(),
channel.getDecodeConfiguration().getDecoderType().getChannelBandwidth(),
channel.getDecodeConfiguration().getDecoderType().getChannelSpecification());
channel.getDecodeConfiguration().getChannelSpecification());
}
catch(SourceException se)
{

View File

@@ -63,7 +63,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
{
private final static Logger mLog = LoggerFactory.getLogger(ComplexPolyphaseChannelizerM2.class);
private static final int DEFAULT_MINIMUM_CHANNEL_BANDWIDTH = 12500;
private static final int DEFAULT_MINIMUM_CHANNEL_BANDWIDTH = 25000;
//Sized at 152 buffers a second where max = 5 seconds and reset = 2 seconds worth of buffers
private IFFTProcessor mIFFTProcessor = new IFFTProcessor((5 * 152), (2 * 152));

View File

@@ -29,6 +29,7 @@ import io.github.dsheirer.source.Source;
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.ChannelSpecification;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import org.slf4j.Logger;
@@ -62,7 +63,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class PolyphaseChannelManager implements ISourceEventProcessor
{
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelManager.class);
private static final double MINIMUM_CHANNEL_BANDWIDTH = 12500.0;
private static final double MINIMUM_CHANNEL_BANDWIDTH = 25000.0;
private static final double CHANNEL_OVERSAMPLING = 2.0;
private static final int POLYPHASE_CHANNELIZER_TAPS_PER_CHANNEL = 9;
private static final int POLYPHASE_SYNTHESIZER_TAPS_PER_CHANNEL = 9;
@@ -141,7 +142,7 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
* @param tunerChannel specifying center frequency and bandwidth.
* @return source or null.
*/
public TunerChannelSource getChannel(TunerChannel tunerChannel)
public TunerChannelSource getChannel(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
{
PolyphaseChannelSource channelSource = null;
@@ -155,10 +156,17 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
{
long centerFrequency = mChannelCalculator.getCenterFrequencyForIndexes(polyphaseIndexes);
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener,
mChannelCalculator.getChannelSampleRate(), centerFrequency);
try
{
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener,
mChannelCalculator.getChannelSampleRate(), centerFrequency, channelSpecification);
mChannelSources.add(channelSource);
mChannelSources.add(channelSource);
}
catch(FilterDesignException fde)
{
mLog.debug("Couldn't design final output low pass filter for polyphase channel source");
}
}
}
catch(IllegalArgumentException iae)

View File

@@ -16,22 +16,35 @@
package io.github.dsheirer.dsp.filter.channelizer;
import io.github.dsheirer.dsp.filter.channelizer.output.IPolyphaseChannelOutputProcessor;
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2;
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableChannelResultsBuffer;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
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.HashMap;
import java.util.Map;
public class PolyphaseChannelSource extends TunerChannelSource
{
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class);
private ReusableComplexBufferAssembler mReusableComplexBufferAssembler;
private IPolyphaseChannelOutputProcessor mPolyphaseChannelOutputProcessor;
private IPolyphaseChannelOutputProcessor mReplacementPolyphaseChannelOutputProcessor;
private long mReplacementFrequency;
private ComplexFIRFilter2 mLowPassFilter;
private static Map<Integer,float[]> sLowPassFilters = new HashMap();
private double mChannelSampleRate;
private long mIndexCenterFrequency;
private long mChannelFrequencyCorrection;
@@ -46,15 +59,22 @@ public class PolyphaseChannelSource extends TunerChannelSource
* @param producerSourceEventListener to receive source event requests (e.g. start/stop sample stream)
* @param channelSampleRate for the downstream sample output
* @param centerFrequency of the incoming polyphase channel(s)
* @throws FilterDesignException if a channel low pass filter can't be designed to the channel specification
*/
public PolyphaseChannelSource(TunerChannel tunerChannel, IPolyphaseChannelOutputProcessor outputProcessor,
Listener<SourceEvent> producerSourceEventListener, double channelSampleRate,
long centerFrequency)
long centerFrequency, ChannelSpecification channelSpecification)
throws FilterDesignException
{
super(producerSourceEventListener, tunerChannel);
mPolyphaseChannelOutputProcessor = outputProcessor;
mChannelSampleRate = channelSampleRate;
mReusableComplexBufferAssembler = new ReusableComplexBufferAssembler(2500, mChannelSampleRate);
float[] filterCoefficients = getLowPassFilter(channelSampleRate, channelSpecification.getPassFrequency(),
channelSpecification.getStopFrequency());
mLowPassFilter = new ComplexFIRFilter2(filterCoefficients, 1.0f);
setFrequency(centerFrequency);
}
@@ -93,14 +113,33 @@ public class PolyphaseChannelSource extends TunerChannelSource
*/
public void setPolyphaseChannelOutputProcessor(IPolyphaseChannelOutputProcessor outputProcessor, long frequency)
{
//Lock on the channel output processor to block the channel results processor thread from servicing the
//channel output processor's buffer while we change out the processors.
synchronized(mPolyphaseChannelOutputProcessor)
//If this is the first time, simply assign the output processor
if(mPolyphaseChannelOutputProcessor == null)
{
mPolyphaseChannelOutputProcessor = outputProcessor;
}
//Otherwise, we have to swap out the processor on the sample processing thread
else
{
mReplacementPolyphaseChannelOutputProcessor = outputProcessor;
mReplacementFrequency = frequency;
}
}
/**
* Updates the output processor to use the new output processor provided by the external
* polyphase channel manager. This method should only be executed on the sample processing
* thread, within the processChannelResults() method.
*/
private void swapOutputProcessor()
{
if(mReplacementPolyphaseChannelOutputProcessor != null)
{
IPolyphaseChannelOutputProcessor existingProcessor = mPolyphaseChannelOutputProcessor;
//Swap out the processor so that incoming samples can accumulate in the new channel output processor
mPolyphaseChannelOutputProcessor = outputProcessor;
mPolyphaseChannelOutputProcessor = mReplacementPolyphaseChannelOutputProcessor;
mReplacementPolyphaseChannelOutputProcessor = null;
//Remove the buffer overflow listener and clear any previous buffer overflow state
existingProcessor.setSourceOverflowListener(null);
@@ -109,18 +148,9 @@ public class PolyphaseChannelSource extends TunerChannelSource
//Register to receive buffer overflow notifications so we can push them up through the source chain
mPolyphaseChannelOutputProcessor.setSourceOverflowListener(this);
//Fully process the residual channel results buffer of the previous channel output processor
if(existingProcessor != null)
{
existingProcessor.processChannelResults(mReusableComplexBufferAssembler);
}
//Finally, setup the frequency offset for the output processor and reset the frequency correction value
//to allow consumers to adjust and calculate a new frequency correction value, if needed.
mIndexCenterFrequency = frequency;
mChannelFrequencyCorrection = 0;
//Finally, setup the frequency offset for the output processor.
mIndexCenterFrequency = mReplacementFrequency;
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
broadcastConsumerSourceEvent(SourceEvent.frequencyCorrectionChange(mChannelFrequencyCorrection));
}
}
@@ -133,12 +163,13 @@ public class PolyphaseChannelSource extends TunerChannelSource
*/
public void receiveChannelResults(ReusableChannelResultsBuffer channelResultsBuffer)
{
mReusableComplexBufferAssembler.updateTimestamp(channelResultsBuffer.getTimestamp());
if(mPolyphaseChannelOutputProcessor != null)
if(mReplacementPolyphaseChannelOutputProcessor != null)
{
mPolyphaseChannelOutputProcessor.receiveChannelResults(channelResultsBuffer);
swapOutputProcessor();
}
mReusableComplexBufferAssembler.updateTimestamp(channelResultsBuffer.getTimestamp());
mPolyphaseChannelOutputProcessor.receiveChannelResults(channelResultsBuffer);
}
/**
@@ -173,7 +204,6 @@ public class PolyphaseChannelSource extends TunerChannelSource
if(mPolyphaseChannelOutputProcessor != null)
{
mPolyphaseChannelOutputProcessor.dispose();
mPolyphaseChannelOutputProcessor = null;
}
}
@@ -213,11 +243,7 @@ public class PolyphaseChannelSource extends TunerChannelSource
@Override
protected void processSamples()
{
//Lock on the output processor so that it can't be changed in the middle of processing
synchronized(mPolyphaseChannelOutputProcessor)
{
mPolyphaseChannelOutputProcessor.processChannelResults(mReusableComplexBufferAssembler);
}
mPolyphaseChannelOutputProcessor.processChannelResults(mReusableComplexBufferAssembler);
}
/**
@@ -236,10 +262,7 @@ public class PolyphaseChannelSource extends TunerChannelSource
*/
private void updateFrequencyOffset()
{
synchronized(mPolyphaseChannelOutputProcessor)
{
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
}
mPolyphaseChannelOutputProcessor.setFrequencyOffset(getFrequencyOffset());
}
@Override
@@ -248,4 +271,42 @@ public class PolyphaseChannelSource extends TunerChannelSource
return "POLYPHASE [" + mPolyphaseChannelOutputProcessor.getInputChannelCount() + "] " +
getTunerChannel().getFrequency();
}
/**
* Creates a low-pass filter to use as the final cleanup filter for the decimated output stream
*
* @param sampleRate for the final output channel rate
* @param passFrequency for half of the desired channel rate
* @param stopFrequency for the attenuated band
* @return a newly designed filter or a previously designed (cached) filter
* @throws FilterDesignException
*/
private float[] getLowPassFilter(double sampleRate, double passFrequency, double stopFrequency) throws FilterDesignException
{
//Use existing filter if we've already designed one
if(sLowPassFilters.containsKey((int)sampleRate))
{
return sLowPassFilters.get((int)sampleRate);
}
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
.sampleRate(sampleRate)
.gridDensity(16)
.passBandCutoff(passFrequency)
.passBandAmplitude(1.0)
.passBandRipple(0.01)
.stopBandStart(stopFrequency)
.stopBandAmplitude(0.0)
.stopBandRipple(0.01)
.build();
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
//This will throw an exception if the filter cannot be designed
float[] taps = designer.getImpulseResponse();
sLowPassFilters.put((int)sampleRate, taps);
return taps;
}
}

View File

@@ -16,29 +16,18 @@
package io.github.dsheirer.dsp.filter.channelizer.output;
import io.github.dsheirer.dsp.filter.channelizer.TwoChannelSynthesizerM2;
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2;
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
import io.github.dsheirer.dsp.mixer.FS4DownConverter;
import io.github.dsheirer.sample.buffer.ReusableChannelResultsBuffer;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TwoChannelOutputProcessor extends ChannelOutputProcessor
{
private final static Logger mLog = LoggerFactory.getLogger(TwoChannelOutputProcessor.class);
private static Map<Integer,float[]> sLowPassFilterKernelMap = new HashMap<>();
// private final static Logger mLog = LoggerFactory.getLogger(TwoChannelOutputProcessor.class);
private TwoChannelSynthesizerM2 mSynthesizer;
private FS4DownConverter mFS4DownConverter = new FS4DownConverter();
private ComplexFIRFilter2 mLowPassFilter;
private int mChannelOffset1;
private int mChannelOffset2;
@@ -58,28 +47,11 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
super(2, sampleRate, gain);
setPolyphaseChannelIndices(channelIndexes);
setSynthesisFilter(filter);
float[] lowPassFilter = sLowPassFilterKernelMap.get((int)sampleRate);
if(lowPassFilter == null)
{
try
{
lowPassFilter = getLowPassFilter(sampleRate);
sLowPassFilterKernelMap.put((int)sampleRate, lowPassFilter);
}
catch(FilterDesignException fde)
{
mLog.error("Couldn't design a low-pass filter for 2-channel synthesizer", fde);
throw new IllegalStateException("Cannot design low-pass filter for 2 channel synthesizer", fde);
}
}
mLowPassFilter = new ComplexFIRFilter2(lowPassFilter, 1.0f);
}
/**
* Sets the frequency offset to apply to the incoming samples to mix the desired signal to baseband.
*
* @param frequencyOffset in hertz
*/
@Override
@@ -126,7 +98,7 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
public void process(List<ReusableChannelResultsBuffer> channelResultsBuffers,
ReusableComplexBufferAssembler reusableComplexBufferAssembler)
{
for(ReusableChannelResultsBuffer buffer: channelResultsBuffers)
for(ReusableChannelResultsBuffer buffer : channelResultsBuffers)
{
ReusableComplexBuffer channel1 = buffer.getChannel(mChannelOffset1);
ReusableComplexBuffer channel2 = buffer.getChannel(mChannelOffset2);
@@ -140,48 +112,11 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
//Apply offset and frequency correction to center the signal of interest within the synthesized channel
getFrequencyCorrectionMixer().mixComplex(synthesized.getSamples());
ReusableComplexBuffer lowPassFiltered = mLowPassFilter.filter(synthesized);
synthesized.applyGain(getGain());
lowPassFiltered.applyGain(getGain());
reusableComplexBufferAssembler.receive(lowPassFiltered);
reusableComplexBufferAssembler.receive(synthesized);
buffer.decrementUserCount();
}
}
/**
* Creates a low-pass filter kernel for the specified sample rate.
* @param sampleRate for the channel
* @return filter
* @throws FilterDesignException if the filter cannot be designed
*/
private static float[] getLowPassFilter(double sampleRate) throws FilterDesignException
{
double cutoff = sampleRate / 4.0;
int passBandCutoff = (int)(cutoff * 0.92);
int stopBandStart = (int)(cutoff * 1.08);
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
.sampleRate(sampleRate)
.gridDensity(16)
.oddLength(true)
.passBandCutoff((int)passBandCutoff)
.passBandAmplitude(1.0)
.passBandRipple(0.01)
.stopBandStart((int)stopBandStart)
.stopBandAmplitude(0.0)
.stopBandRipple(0.005) //Approximately 94 dB attenuation
.build();
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
if(designer.isValid())
{
return designer.getImpulseResponse();
}
throw new FilterDesignException("Couldn't design low-pass filter for sample rate: " + sampleRate +
" Pass End:" + passBandCutoff + " Stop Begin:" + stopBandStart);
}
}

View File

@@ -62,12 +62,12 @@ public class FilterViewer extends Application
// .build();
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
.sampleRate(50000.0)
.sampleRate(25000.0)
.gridDensity(16)
.passBandCutoff(6000)
.passBandCutoff(11800)
.passBandAmplitude(1.0)
.passBandRipple(0.01)
.stopBandStart(6700)
.stopBandStart(12400)
.stopBandAmplitude(0.0)
.stopBandRipple(0.01)
.build();

View File

@@ -23,8 +23,8 @@ public enum PLLGain
//NOTE: static gain level of 200 produced the best results for releases prior to 0.3.4b2
LEVEL_1(150.0, 0, 1),
LEVEL_2(175.0, 2, 4),
LEVEL_3(200.0, 5, 6),
LEVEL_2(170.0, 2, 4),
LEVEL_3(190.0, 5, 6),
LEVEL_4(200.0, 7, 8),
LEVEL_5(200.0, 9, 10);

View File

@@ -23,6 +23,7 @@ import io.github.dsheirer.source.ISourceEventProcessor;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.Tuner;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel;
@@ -376,10 +377,11 @@ public class ChannelizerViewer2 extends JFrame
mComplexDecibelConverter.addListener(mSpectrumPanel);
TunerChannel tunerChannel = new TunerChannel(frequency, bandwidth);
mSource = mTestTuner.getChannelSourceManager().getSource(tunerChannel, null);
mSource = mTestTuner.getChannelSourceManager().getSource(tunerChannel, new ChannelSpecification(50000, 12500, 6000, 7000));
if(mSource != null)
{
mLog.debug("Channel: " + mSource.getTunerChannel() + " Rate:" + mSource.getSampleRate());
mSource.setListener(new Listener<ReusableComplexBuffer>()
{
@Override

View File

@@ -565,12 +565,6 @@ public class ProcessingChain implements IChannelEventListener
{
if(mSource != null)
{
//Reset each of the modules
for(Module module : mModules)
{
module.reset();
}
//Broadcast the source sample rate so that each of the modules can self-configure
mSourceEventBroadcaster.broadcast(SourceEvent.sampleRateChange(mSource.getSampleRate(), "Processing Chain Startup"));
@@ -652,6 +646,12 @@ public class ProcessingChain implements IChannelEventListener
{
module.stop();
}
//Reset each of the modules
for(Module module : mModules)
{
module.reset();
}
}
}

View File

@@ -15,8 +15,6 @@
******************************************************************************/
package io.github.dsheirer.module.decode;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
import java.util.ArrayList;
import java.util.EnumSet;
@@ -26,36 +24,27 @@ import java.util.EnumSet;
public enum DecoderType
{
//Primary Decoders
AM("AM", "AM", 10000, new ChannelSpecification(25000.0, 3000.0, 5000.0)),
LTR_STANDARD("LTR-Standard", "LTR", 12500, new ChannelSpecification(25000.0, 6000.0, 7000.0)),
LTR_NET("LTR-Net", "LTR-Net", 12500, new ChannelSpecification(25000.0, 6000.0, 7000.0)),
MPT1327("MPT1327", "MPT1327", 12500, new ChannelSpecification(25000.0, 6000.0, 7000.0)),
NBFM("NBFM", "NBFM", 12500, new ChannelSpecification(25000.0, 6000.0, 7000.0)),
PASSPORT("Passport", "Passport", 12500, new ChannelSpecification(25000.0, 6000.0, 7000.0)),
P25_PHASE1("P25 Phase I", "P25-1", 12500, new ChannelSpecification(50000.0, 6000.0, 7000.0)),
AM("AM", "AM"),
LTR_STANDARD("LTR-Standard", "LTR"),
LTR_NET("LTR-Net", "LTR-Net"),
MPT1327("MPT1327", "MPT1327"),
NBFM("NBFM", "NBFM"),
PASSPORT("Passport", "Passport"),
P25_PHASE1("P25 Phase I", "P25-1"),
//Auxiliary Decoders
FLEETSYNC2("Fleetsync II", "Fleetsync2", 12500),
LJ_1200("LJ1200 173.075", "LJ1200", 12500),
MDC1200("MDC1200", "MDC1200", 12500),
TAIT_1200("Tait 1200", "Tait 1200", 12500);
FLEETSYNC2("Fleetsync II", "Fleetsync2"),
LJ_1200("LJ1200 173.075", "LJ1200"),
MDC1200("MDC1200", "MDC1200"),
TAIT_1200("Tait 1200", "Tait 1200");
private String mDisplayString;
private String mShortDisplayString;
private int mChannelBandwidth;
private ChannelSpecification mChannelSpecification;
DecoderType(String displayString, String shortDisplayString, int bandwidth, ChannelSpecification channelSpecification)
DecoderType(String displayString, String shortDisplayString)
{
mDisplayString = displayString;
mShortDisplayString = shortDisplayString;
mChannelBandwidth = bandwidth;
mChannelSpecification = channelSpecification;
}
DecoderType(String displayString, String shortDisplayString, int bandwidth)
{
this(displayString, shortDisplayString, bandwidth, null);
}
/**
@@ -97,20 +86,6 @@ public enum DecoderType
return mShortDisplayString;
}
public int getChannelBandwidth()
{
return mChannelBandwidth;
}
/**
* Channel specification for a channel for this decoder type.
* @return specification or null
*/
public ChannelSpecification getChannelSpecification()
{
return mChannelSpecification;
}
@Override
public String toString()
{

View File

@@ -17,9 +17,11 @@
******************************************************************************/
package io.github.dsheirer.module.decode.am;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
public class DecodeConfigAM extends DecodeConfiguration
{
@@ -27,6 +29,17 @@ public class DecodeConfigAM extends DecodeConfiguration
{
}
/**
* Source channel specification for this decoder
*/
@JsonIgnore
@Override
public ChannelSpecification getChannelSpecification()
{
return new ChannelSpecification(25000.0,
3000, 3000.0, 5000.0);
}
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
public DecoderType getDecoderType()
{

View File

@@ -29,6 +29,7 @@ import io.github.dsheirer.module.decode.mpt1327.DecodeConfigMPT1327;
import io.github.dsheirer.module.decode.nbfm.DecodeConfigNBFM;
import io.github.dsheirer.module.decode.p25.DecodeConfigP25Phase1;
import io.github.dsheirer.module.decode.passport.DecodeConfigPassport;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@@ -58,4 +59,7 @@ public abstract class DecodeConfiguration extends Configuration
@JsonIgnore
public abstract DecoderType getDecoderType();
@JsonIgnore
public abstract ChannelSpecification getChannelSpecification();
}

View File

@@ -30,10 +30,17 @@ import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
@@ -86,64 +93,6 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
});
}
// @Override
// public void channelChanged(ChannelEvent event)
// {
// boolean changed = false;
//
// if(event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE &&
// event.getChannel().isSelected())
// {
// if(mDisplayedChannel == null || (mDisplayedChannel != null && mDisplayedChannel != event.getChannel()))
// {
// mDisplayedChannel = event.getChannel();
//
// ProcessingChain chain = mChannelProcessingManager.getProcessingChain(mDisplayedChannel);
//
// if(chain != null)
// {
// mDisplayedModel = chain.getMessageActivityModel();
// }
// else
// {
// mDisplayedModel = EMPTY_MODEL;
// }
//
// mMessageTable.setModel(mDisplayedModel);
//
// if(mDisplayedChannel != null)
// {
// mManagementPanel.enableButtons();
// }
// else
// {
// mManagementPanel.disableButtons();
// }
//
// changed = true;
// }
// }
// else if(event.getEvent() == Event.NOTIFICATION_PROCESSING_STOP || event.getEvent() == Event.REQUEST_DISABLE)
// {
// if(mDisplayedChannel != null && mDisplayedChannel == event.getChannel())
// {
// mDisplayedChannel = null;
//
// mMessageTable.setModel(EMPTY_MODEL);
//
// mManagementPanel.disableButtons();
//
// changed = true;
// }
// }
//
// if(changed)
// {
// revalidate();
// repaint();
// }
// }
public class MessageManagementPanel extends JPanel
{
private static final long serialVersionUID = 1L;

View File

@@ -17,10 +17,12 @@
******************************************************************************/
package io.github.dsheirer.module.decode.ltrnet;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.message.MessageDirection;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
public class DecodeConfigLTRNet extends DecodeConfiguration
{
@@ -46,4 +48,15 @@ public class DecodeConfigLTRNet extends DecodeConfiguration
{
mMessageDirection = direction;
}
/**
* Source channel specification for this decoder
*/
@JsonIgnore
@Override
public ChannelSpecification getChannelSpecification()
{
return new ChannelSpecification(25000.0,
12500, 6000.0, 7000.0);
}
}

View File

@@ -17,10 +17,12 @@
******************************************************************************/
package io.github.dsheirer.module.decode.ltrstandard;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.message.MessageDirection;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
public class DecodeConfigLTRStandard extends DecodeConfiguration
{
@@ -47,4 +49,15 @@ public class DecodeConfigLTRStandard extends DecodeConfiguration
{
mMessageDirection = direction;
}
/**
* Source channel specification for this decoder
*/
@JsonIgnore
@Override
public ChannelSpecification getChannelSpecification()
{
return new ChannelSpecification(25000.0,
12500, 6000.0, 7000.0);
}
}

View File

@@ -17,9 +17,11 @@
******************************************************************************/
package io.github.dsheirer.module.decode.mpt1327;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
public class DecodeConfigMPT1327 extends DecodeConfiguration
{
@@ -102,4 +104,15 @@ public class DecodeConfigMPT1327 extends DecodeConfiguration
{
mTrafficChannelPoolSize = size;
}
/**
* Source channel specification for this decoder
*/
@JsonIgnore
@Override
public ChannelSpecification getChannelSpecification()
{
return new ChannelSpecification(25000.0,
12500, 6000.0, 7000.0);
}
}

View File

@@ -17,12 +17,16 @@
******************************************************************************/
package io.github.dsheirer.module.decode.nbfm;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
public class DecodeConfigNBFM extends DecodeConfiguration
{
private Bandwidth mBandwidth = Bandwidth.BW_12_5;
public DecodeConfigNBFM()
{
}
@@ -33,4 +37,53 @@ public class DecodeConfigNBFM extends DecodeConfiguration
return DecoderType.NBFM;
}
/**
* Channel bandwidth
*/
@JacksonXmlProperty(isAttribute = true, localName = "bandwidth")
public Bandwidth getBandwidth()
{
return mBandwidth;
}
@JsonIgnore
@Override
public ChannelSpecification getChannelSpecification()
{
if(mBandwidth == Bandwidth.BW_12_5)
{
return new ChannelSpecification(25000.0, 12500, 6000.0, 7000.0);
}
else
{
return new ChannelSpecification(50000.0, 25000, 12500.0, 13500.0);
}
}
/**
* Sets the channel bandwidth
*/
public void setBandwidth(Bandwidth bandwidth)
{
mBandwidth = bandwidth;
}
public enum Bandwidth
{
BW_12_5("12.5 kHz"),
BW_25_0("25.0 kHz");
private String mLabel;
Bandwidth(String label)
{
mLabel = label;
}
@Override
public String toString()
{
return mLabel;
}
};
}

View File

@@ -1,19 +1,17 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 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/>
* 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.module.decode.nbfm;
@@ -24,70 +22,94 @@ import io.github.dsheirer.gui.editor.ValidatingEditor;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import net.miginfocom.swing.MigLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class NBFMDecoderEditor extends ValidatingEditor<Channel>
{
private static final long serialVersionUID = 1L;
public NBFMDecoderEditor()
{
init();
}
private void init()
{
setLayout( new MigLayout( "insets 0 0 0 0,wrap 2", "[right][grow,fill]", "" ) );
add( new JLabel( "Narrow Band FM Decoder" ) );
}
@Override
public void validate( Editor<Channel> editor ) throws EditorValidationException
{
//No validation
}
private JComboBox<DecodeConfigNBFM.Bandwidth> mComboBandwidth;
@Override
public void save()
{
if( hasItem() && isModified() )
{
DecodeConfigNBFM nbfm = new DecodeConfigNBFM();
getItem().setDecodeConfiguration( nbfm );
}
setModified( false );
}
public NBFMDecoderEditor()
{
init();
}
private void setControlsEnabled( boolean enabled )
{
}
private void init()
{
setLayout(new MigLayout("insets 0 0 0 0,wrap 2", "[right][grow,fill]", ""));
@Override
public void setItem( Channel item )
{
super.setItem( item );
if( hasItem() )
{
setControlsEnabled( true );
DecodeConfiguration config = getItem().getDecodeConfiguration();
if( config instanceof DecodeConfigNBFM )
{
DecodeConfigNBFM nbfm = (DecodeConfigNBFM)config;
setModified( false );
}
else
{
setModified( true );
}
}
else
{
setControlsEnabled( false );
setModified( false );
}
}
mComboBandwidth = new JComboBox<>();
mComboBandwidth.setModel(new DefaultComboBoxModel<DecodeConfigNBFM.Bandwidth>(
DecodeConfigNBFM.Bandwidth.values()));
mComboBandwidth.setEnabled(false);
mComboBandwidth.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
setModified(true);
}
});
add(new JLabel("Bandwidth:"));
add(mComboBandwidth);
}
@Override
public void validate(Editor<Channel> editor) throws EditorValidationException
{
//No validation
}
@Override
public void save()
{
if(hasItem() && isModified())
{
DecodeConfigNBFM nbfm = new DecodeConfigNBFM();
nbfm.setBandwidth((DecodeConfigNBFM.Bandwidth)mComboBandwidth.getSelectedItem());
getItem().setDecodeConfiguration(nbfm);
}
setModified(false);
}
private void setControlsEnabled(boolean enabled)
{
mComboBandwidth.setEnabled(enabled);
}
@Override
public void setItem(Channel item)
{
super.setItem(item);
if(hasItem())
{
setControlsEnabled(true);
DecodeConfiguration config = getItem().getDecodeConfiguration();
if(config instanceof DecodeConfigNBFM)
{
DecodeConfigNBFM nbfm = (DecodeConfigNBFM)config;
mComboBandwidth.setSelectedItem(nbfm.getBandwidth());
setModified(false);
}
else
{
setModified(true);
}
}
else
{
setControlsEnabled(false);
setModified(false);
}
}
}

View File

@@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
public class DecodeConfigP25Phase1 extends DecodeConfiguration
{
@@ -104,4 +105,14 @@ public class DecodeConfigP25Phase1 extends DecodeConfiguration
{
mTrafficChannelPoolSize = size;
}
/**
* Source channel specification for this decoder
*/
@JsonIgnore
@Override
public ChannelSpecification getChannelSpecification()
{
return new ChannelSpecification(50000.0, 12500, 5750.0, 6500.0);
}
}

View File

@@ -350,9 +350,13 @@ public class Trellis_3_4_Rate
public void add( Con con )
{
Node current = getLastNode();
Tribit input = INPUT_FROM_CONSTELLATION_MAP
.get( con )[ current.getState().getValue() ];
Tribit input = null;
if(current != null)
{
input = INPUT_FROM_CONSTELLATION_MAP.get( con )[ current.getState().getValue() ];
}
/* A non-null input tribit from the lookup table is valid, otherwise
* create 8 alternate branches from the original path to explore

View File

@@ -1,7 +1,6 @@
/*
* *********************************************************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
/*******************************************************************************
* 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
@@ -12,13 +11,15 @@
*
* 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.module.decode.passport;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
public class DecodeConfigPassport extends DecodeConfiguration
{
@@ -31,4 +32,14 @@ public class DecodeConfigPassport extends DecodeConfiguration
{
return DecoderType.PASSPORT;
}
/**
* Source channel specification for this decoder
*/
@JsonIgnore
@Override
public ChannelSpecification getChannelSpecification()
{
return new ChannelSpecification(25000.0, 12500, 6000.0, 7000.0);
}
}

View File

@@ -74,8 +74,7 @@ public class SourceManager
return mTunerModel;
}
public Source getSource(SourceConfiguration config, int bandwidth, ChannelSpecification channelSpecification)
throws SourceException
public Source getSource(SourceConfiguration config, ChannelSpecification channelSpecification) throws SourceException
{
Source retVal = null;
@@ -85,10 +84,10 @@ public class SourceManager
retVal = mMixerManager.getSource(config);
break;
case TUNER:
retVal = mTunerModel.getSource((SourceConfigTuner) config, bandwidth, channelSpecification);
retVal = mTunerModel.getSource((SourceConfigTuner) config, channelSpecification);
break;
case RECORDING:
retVal = mRecordingSourceManager.getSource(config, bandwidth);
retVal = mRecordingSourceManager.getSource(config, channelSpecification);
case NONE:
default:
break;

View File

@@ -1,19 +1,17 @@
/*******************************************************************************
* 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/>
* 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.recording;
@@ -22,6 +20,7 @@ import io.github.dsheirer.source.Source;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.config.SourceConfigRecording;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import org.slf4j.Logger;
@@ -33,64 +32,64 @@ import java.util.List;
public class RecordingSourceManager
{
private final static Logger mLog = LoggerFactory.getLogger( RecordingSourceManager.class );
private final static Logger mLog = LoggerFactory.getLogger(RecordingSourceManager.class);
private ArrayList<Recording> mRecordings = new ArrayList<Recording>();
private ArrayList<Recording> mRecordings = new ArrayList<Recording>();
private SettingsManager mSettingsManager;
private SettingsManager mSettingsManager;
public RecordingSourceManager( SettingsManager settingsManager )
{
mSettingsManager = settingsManager;
loadRecordings();
}
public RecordingSourceManager(SettingsManager settingsManager)
{
mSettingsManager = settingsManager;
loadRecordings();
}
/**
* Iterates current recordings to get a tuner channel source for the frequency
* specified in the channel config's source config object
*/
public Source getSource( SourceConfiguration config, int bandwidth )
throws SourceException
public Source getSource(SourceConfiguration config, ChannelSpecification channelSpecification)
throws SourceException
{
TunerChannelSource retVal = null;
TunerChannelSource retVal = null;
if( config instanceof SourceConfigRecording )
{
SourceConfigRecording configRecording = (SourceConfigRecording)config;
TunerChannel tunerChannel = configRecording.getTunerChannel();
tunerChannel.setBandwidth( bandwidth );
if(config instanceof SourceConfigRecording)
{
SourceConfigRecording configRecording = (SourceConfigRecording)config;
Recording recording = getRecordingFromAlias(
configRecording.getRecordingAlias() );
if( recording != null )
{
retVal = recording.getChannel( tunerChannel );
}
else
{
throw new SourceException( "Recording with alias name [" +
configRecording.getRecordingAlias() + "] is not currently available" );
}
}
return retVal;
TunerChannel tunerChannel = configRecording.getTunerChannel();
tunerChannel.setBandwidth(channelSpecification.getBandwidth());
Recording recording = getRecordingFromAlias(
configRecording.getRecordingAlias());
if(recording != null)
{
retVal = recording.getChannel(tunerChannel);
}
else
{
throw new SourceException("Recording with alias name [" +
configRecording.getRecordingAlias() + "] is not currently available");
}
}
return retVal;
}
public Recording getRecordingFromAlias( String alias )
public Recording getRecordingFromAlias(String alias)
{
for( Recording recording: mRecordings )
{
if( recording.getRecordingConfiguration()
.getAlias().contentEquals( alias ) )
{
return recording;
}
}
return null;
for(Recording recording : mRecordings)
{
if(recording.getRecordingConfiguration()
.getAlias().contentEquals(alias))
{
return recording;
}
}
return null;
}
/**
@@ -98,47 +97,47 @@ public class RecordingSourceManager
*/
public List<Recording> getRecordings()
{
Collections.sort( mRecordings );
return mRecordings;
}
public void addRecording( Recording recording )
{
mRecordings.add( recording );
}
public Recording addRecording( RecordingConfiguration config )
{
Recording recording = new Recording( mSettingsManager, config );
Collections.sort(mRecordings);
mRecordings.add( recording );
return recording;
return mRecordings;
}
public void removeRecording( Recording recording )
public void addRecording(Recording recording)
{
/* Remove the config from the settings manager */
mSettingsManager.removeRecordingConfiguration(
recording.getRecordingConfiguration() );
mRecordings.remove( recording );
mRecordings.add(recording);
}
public Recording addRecording(RecordingConfiguration config)
{
Recording recording = new Recording(mSettingsManager, config);
mRecordings.add(recording);
return recording;
}
public void removeRecording(Recording recording)
{
/* Remove the config from the settings manager */
mSettingsManager.removeRecordingConfiguration(
recording.getRecordingConfiguration());
mRecordings.remove(recording);
}
private void loadRecordings()
{
List<RecordingConfiguration> recordingConfigurations =
mSettingsManager.getRecordingConfigurations();
List<RecordingConfiguration> recordingConfigurations =
mSettingsManager.getRecordingConfigurations();
mLog.info( "RecordingSourceManager - discovered [" +
recordingConfigurations.size() + "] recording configurations" );
for( RecordingConfiguration config: recordingConfigurations )
{
Recording recording = new Recording( mSettingsManager, config );
mRecordings.add( recording );
}
}
mLog.info("RecordingSourceManager - discovered [" +
recordingConfigurations.size() + "] recording configurations");
for(RecordingConfiguration config : recordingConfigurations)
{
Recording recording = new Recording(mSettingsManager, config);
mRecordings.add(recording);
}
}
}

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.HeterodyneChannelSourceManager;
import io.github.dsheirer.source.tuner.manager.PolyphaseChannelSourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,8 +51,8 @@ public abstract class Tuner implements ISourceEventProcessor
//Register to receive frequency and sample rate change notifications
mTunerController.addListener(this::process);
// mChannelSourceManager = new PolyphaseChannelSourceManager(mTunerController);
mChannelSourceManager = new HeterodyneChannelSourceManager(mTunerController);
mChannelSourceManager = new PolyphaseChannelSourceManager(mTunerController);
// mChannelSourceManager = new HeterodyneChannelSourceManager(mTunerController);
//Register to receive channel count change notifications
mChannelSourceManager.addSourceEventListener(this::process);

View File

@@ -26,12 +26,15 @@ import io.github.dsheirer.source.ISourceEventProcessor;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceEventListenerToProcessorAdapter;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
import io.github.dsheirer.source.tuner.frequency.FrequencyController;
import io.github.dsheirer.source.tuner.frequency.FrequencyController.Tunable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.SortedSet;
public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener,
IReusableComplexBufferProvider, Listener<ReusableComplexBuffer>
{
@@ -361,4 +364,85 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
{
broadcast(complexBuffer);
}
/**
* Indicates if the current center frequency and bandwidth is correct to source the tuner channel
*
* @param tunerChannel to test
* @return true if the current center frequency and bandwidth is correct for the channel
*/
public boolean isTunedFor(TunerChannel tunerChannel)
{
try
{
if(tunerChannel.getMinFrequency() < getMinTunedFrequency())
{
return false;
}
if(tunerChannel.getMaxFrequency() > getMaxTunedFrequency())
{
return false;
}
if(hasMiddleUnusableBandwidth())
{
long minAvoid = getFrequency() - getMiddleUnusableHalfBandwidth();
long maxAvoid = getFrequency() + getMiddleUnusableHalfBandwidth();
if(tunerChannel.overlaps(minAvoid, maxAvoid))
{
return false;
}
}
}
catch(SourceException se)
{
return false;
}
return true;
}
/**
* Indicates if the current center frequency and bandwidth is correct to source the tuner channel set
*
* @param tunerChannels to test
* @return true if the current center frequency and bandwidth is correct for the channel set
*/
public boolean isTunedFor(SortedSet<TunerChannel> tunerChannels)
{
try
{
if(tunerChannels.first().getMinFrequency() < getMinTunedFrequency())
{
return false;
}
if(tunerChannels.last().getMaxFrequency() > getMaxTunedFrequency())
{
return false;
}
if(hasMiddleUnusableBandwidth())
{
long minAvoid = getFrequency() - getMiddleUnusableHalfBandwidth();
long maxAvoid = getFrequency() + getMiddleUnusableHalfBandwidth();
for(TunerChannel tunerChannel: tunerChannels)
{
if(tunerChannel.overlaps(minAvoid, maxAvoid))
{
return false;
}
}
}
}
catch(SourceException se)
{
return false;
}
return true;
}
}

View File

@@ -340,13 +340,13 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
*
* Returns null if no tuner can source the channel
*/
public Source getSource(SourceConfigTuner config, int bandwidth, ChannelSpecification channelSpecification)
public Source getSource(SourceConfigTuner config, ChannelSpecification channelSpecification)
{
TunerChannelSource retVal = null;
TunerChannel tunerChannel = config.getTunerChannel();
tunerChannel.setBandwidth(bandwidth);
tunerChannel.setBandwidth(channelSpecification.getBandwidth());
Iterator<Tuner> it = mTuners.iterator();

View File

@@ -19,6 +19,7 @@ import io.github.dsheirer.dsp.filter.cic.ComplexPrimeCICDecimate;
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.IOverflowListener;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.OverflowableReusableBufferTransferQueue;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
@@ -74,6 +75,7 @@ public class CICTunerChannelSource extends TunerChannelSource implements Listene
channelSpecification.getStopFrequency());
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();
@@ -82,6 +84,18 @@ public class CICTunerChannelSource extends TunerChannelSource implements Listene
mFrequencyCorrectionMixer = new LowPhaseNoiseOscillator(frequencyOffset, sampleRate);
}
/**
* Registers an overflow listener to be notified in the internal buffer is in an
* overflow state.
*
* @param listener to receiver updates to the source overflow state
*/
@Override
public void setOverflowListener(IOverflowListener listener)
{
mBuffer.setOverflowListener(listener);
}
@Override
public void dispose()
{

View File

@@ -21,18 +21,21 @@ package io.github.dsheirer.source.tuner.channel;
public class ChannelSpecification
{
private double mMinimumSampleRate;
private int mBandwidth;
private double mPassFrequency;
private double mStopFrequency;
/**
* Constructs a channel specification instance.
* @param minimumSampleRate requested for the channel
* @param bandwidth
* @param passFrequency for the pass band of the channel
* @param stopFrequency for the stop band of teh channel
*/
public ChannelSpecification(double minimumSampleRate, double passFrequency, double stopFrequency)
public ChannelSpecification(double minimumSampleRate, int bandwidth, double passFrequency, double stopFrequency)
{
mMinimumSampleRate = minimumSampleRate;
mBandwidth = bandwidth;
mPassFrequency = passFrequency;
mStopFrequency = stopFrequency;
}
@@ -45,6 +48,14 @@ public class ChannelSpecification
return mMinimumSampleRate;
}
/**
* Bandwidth of the channel in Hertz
*/
public int getBandwidth()
{
return mBandwidth;
}
/**
* Pass band frequency for filtering the channel
*/

View File

@@ -286,8 +286,26 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
{
if(!mStopped)
{
getHeartbeatManager().broadcast();
processSamples();
try
{
getHeartbeatManager().broadcast();
}
catch(Throwable t)
{
mLog.error("Error while sending heartbeat", t);
}
}
if(!mStopped)
{
try
{
processSamples();
}
catch(Throwable t)
{
mLog.error("Error while processing samples", t);
}
}
if(mStopped)
@@ -300,7 +318,15 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
mScheduledFuture = null;
performDisposal();
try
{
getHeartbeatManager().broadcast();
performDisposal();
}
catch(Throwable t)
{
mLog.error("Error during final shutdown processing of samples", t);
}
}
}
}

View File

@@ -54,48 +54,57 @@ public class CenterFrequencyCalculator
//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)
if(maxChannelFrequency - minChannelFrequency <= tunerController.getUsableHalfBandwidth())
{
boolean processingRequired = true;
while(isValidCandidateFrequency && processingRequired)
candidateFrequency = minChannelFrequency - tunerController.getMiddleUnusableHalfBandwidth();
isValidCandidateFrequency = true;
}
else
{
//Iterate the channels and make sure that none of them overlap the center DC spike buffer, if one exists
if(tunerController.getMiddleUnusableHalfBandwidth() > 0)
{
processingRequired = false;
boolean processingRequired = true;
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)
while(isValidCandidateFrequency && processingRequired)
{
if(channel.overlaps(minAvoid, maxAvoid))
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)
{
//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)
if(channel.overlaps(minAvoid, maxAvoid))
{
candidateFrequency += adjustment;
processingRequired = true;
}
//Otherwise, punt and indicate that we can't find a center frequency or add the channel
else
{
isValidCandidateFrequency = false;
}
//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;
//break out of the for/each loop, so that we can start over again with all of the channels
break;
//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)

View File

@@ -108,23 +108,26 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
*/
private void updateTunerFrequency()
{
long centerFrequency = CenterFrequencyCalculator.getCenterFrequency(mTunerController, getTunerChannels());
if(centerFrequency == CenterFrequencyCalculator.INVALID_FREQUENCY)
if(!mTunerController.isTunedFor(getTunerChannels()))
{
mLog.error("Couldn't calculate center frequency for tuner and tuner channels");
return;
}
long centerFrequency = CenterFrequencyCalculator.getCenterFrequency(mTunerController, getTunerChannels());
if(centerFrequency != mTunerController.getFrequency())
{
try
if(centerFrequency == CenterFrequencyCalculator.INVALID_FREQUENCY)
{
mTunerController.setFrequency(centerFrequency);
mLog.error("Couldn't calculate center frequency for tuner and tuner channels");
return;
}
catch(SourceException se)
if(centerFrequency != mTunerController.getFrequency())
{
mLog.error("Couldn't update tuner center frequency to " + centerFrequency, se);
try
{
mTunerController.setFrequency(centerFrequency);
}
catch(SourceException se)
{
mLog.error("Couldn't update tuner center frequency to " + centerFrequency, se);
}
}
}
}

View File

@@ -393,11 +393,6 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
@Override
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
{
if(channelSpecification != null)
{
throw new IllegalArgumentException("Update polyphase source manager to support channel specifications");
}
if(isTunable(tunerChannel))
{
//Get a new set of currently tuned channels
@@ -422,7 +417,7 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
}
//If we're successful to here, allocate the channel
return mPolyphaseChannelManager.getChannel(tunerChannel);
return mPolyphaseChannelManager.getChannel(tunerChannel, channelSpecification);
}
catch(SourceException se)
{

View File

@@ -32,7 +32,7 @@ public class TestTunerController extends TunerController
public static final long MINIMUM_FREQUENCY = 1l;
public static final long MAXIMUM_FREQUENCY = 1000000000l;
public static final int SAMPLE_RATE = 50000;
public static final int SAMPLE_RATE = 10000000;
public static final int DC_NOISE_BANDWIDTH = 0;
public static final double USABLE_BANDWIDTH_PERCENTAGE = 1.00;