mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-11 16:17:24 -05:00
Compare commits
19 Commits
v0.4.0-alp
...
v0.4.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d95c78c0f1 | ||
|
|
aee5ece8d2 | ||
|
|
ba358a7d48 | ||
|
|
09b209496b | ||
|
|
cc209683bd | ||
|
|
2771c28dba | ||
|
|
d752917073 | ||
|
|
28bc02e172 | ||
|
|
0f6044d48b | ||
|
|
58ecb83e2c | ||
|
|
9e1de922e4 | ||
|
|
84d0b827d8 | ||
|
|
a17132be0d | ||
|
|
dae68515a5 | ||
|
|
0aaec8ff54 | ||
|
|
001883edc2 | ||
|
|
335fb51456 | ||
|
|
e94b6ae172 | ||
|
|
785125da4f |
@@ -15,7 +15,7 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
version = '0.4.0-alpha.4'
|
||||
version = '0.4.0-alpha.5'
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -20,6 +20,7 @@ package io.github.dsheirer.channel.metadata;
|
||||
|
||||
public enum Attribute
|
||||
{
|
||||
BUFFER_OVERFLOW,
|
||||
CHANNEL_CONFIGURATION_NAME,
|
||||
CHANNEL_CONFIGURATION_SITE,
|
||||
CHANNEL_CONFIGURATION_SYSTEM,
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user