mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Merge branch 'master' into 39-polyphase-channelizer
# Conflicts: # src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java # src/main/java/io/github/dsheirer/dsp/filter/FilterFactory.java # src/main/java/io/github/dsheirer/dsp/filter/design/FilterView.java # src/main/java/io/github/dsheirer/dsp/filter/design/FilterViewer.java # src/main/java/io/github/dsheirer/dsp/filter/fir/complex/ComplexFIRFilter.java # src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java # src/main/java/io/github/dsheirer/dsp/mixer/Oscillator.java # src/main/java/io/github/dsheirer/dsp/psk/LSMDemodulator.java # src/main/java/io/github/dsheirer/module/decode/Decoder.java # src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java # src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java # src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder.java # src/main/java/io/github/dsheirer/module/decode/p25/P25_LSMDecoder.java # src/main/java/io/github/dsheirer/source/tuner/TunerChannelSource.java # src/main/java/io/github/dsheirer/source/tuner/TunerModel.java # src/main/java/io/github/dsheirer/source/tuner/TunerSourceEditor.java # src/main/java/io/github/dsheirer/spectrum/ChannelSpectrumPanel.java
This commit is contained in:
@@ -1,17 +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/>
|
||||
******************************************************************************/
|
||||
@@ -22,38 +22,39 @@ import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
public abstract class Decoder extends Module implements IMessageProvider, Listener<Message>
|
||||
public abstract class Decoder extends Module implements IMessageProvider
|
||||
{
|
||||
/* This has to be a broadcaster in order for references to persist */
|
||||
protected Listener<Message> mMessageListener;
|
||||
/* This has to be a broadcaster in order for references to persist */
|
||||
private Listener<Message> mMessageDistributor = new MessageDistributor();
|
||||
protected Listener<Message> mMessageListener;
|
||||
|
||||
/**
|
||||
* Decoder - parent class for all decoders, demodulators and components.
|
||||
*/
|
||||
public Decoder()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Decoder - parent class for all decoders, demodulators and components.
|
||||
*/
|
||||
public Decoder()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
mMessageListener = null;
|
||||
}
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
mMessageListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the decoder type (ie protocol)
|
||||
*/
|
||||
public abstract DecoderType getDecoderType();
|
||||
/**
|
||||
* Identifies the decoder type (ie protocol)
|
||||
*/
|
||||
public abstract DecoderType getDecoderType();
|
||||
|
||||
|
||||
/**
|
||||
* Adds a listener for receiving decoded messages from this decoder
|
||||
*/
|
||||
@Override
|
||||
public void setMessageListener( Listener<Message> listener )
|
||||
public void setMessageListener(Listener<Message> listener)
|
||||
{
|
||||
mMessageListener = listener;
|
||||
}
|
||||
mMessageListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving decoded messages from all attached
|
||||
@@ -62,38 +63,29 @@ public abstract class Decoder extends Module implements IMessageProvider, Listen
|
||||
@Override
|
||||
public void removeMessageListener()
|
||||
{
|
||||
mMessageListener = null;
|
||||
mMessageListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the message to the registered message listener
|
||||
* Message listener for receiving the output from this decoder
|
||||
*/
|
||||
public void broadcast( Message message )
|
||||
protected Listener<Message> getMessageListener()
|
||||
{
|
||||
if( mMessageListener != null )
|
||||
{
|
||||
mMessageListener.receive( message );
|
||||
}
|
||||
return mMessageDistributor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
broadcast( message );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Distributes/forwards messages from sub-class decoder implementations to the registered message listener.
|
||||
*/
|
||||
public class MessageDistributor implements Listener<Message>
|
||||
{
|
||||
@Override
|
||||
public void receive(Message message)
|
||||
{
|
||||
if(mMessageListener != null)
|
||||
{
|
||||
mMessageListener.receive(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,10 +247,8 @@ public class DecoderFactory
|
||||
switch(modulation)
|
||||
{
|
||||
case C4FM:
|
||||
// modules.add(new FMDemodulatorModule(P25_C4FM_IQ_FILTER));
|
||||
modules.add(new DemodulatedAudioFilterModule(P25_C4FM_DEMOD_FILTER, 1.0f));
|
||||
modules.add(new P25_C4FMDecoder(aliasList, decodeConfig.getAFCMaximumCorrection()));
|
||||
modules.add(new P25DecoderState(aliasList, channelType, Modulation.C4FM,
|
||||
modules.add(new P25_C4FMDecoder(aliasList));
|
||||
modules.add(new P25DecoderState(aliasList, channelType, P25Decoder.Modulation.C4FM,
|
||||
p25Config.getIgnoreDataCalls()));
|
||||
break;
|
||||
case CQPSK:
|
||||
|
||||
@@ -89,7 +89,7 @@ public class Fleetsync2Decoder extends Decoder implements IFilteredRealBufferLis
|
||||
mMessageProcessor = new Fleetsync2MessageProcessor( aliasList );
|
||||
mMessageFramer.addMessageListener( mMessageProcessor );
|
||||
|
||||
mMessageProcessor.setMessageListener( this );
|
||||
mMessageProcessor.setMessageListener( getMessageListener() );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
|
||||
@@ -99,7 +99,7 @@ public class LJ1200Decoder extends Decoder implements IFilteredRealBufferListene
|
||||
mMessageProcessor = new LJ1200MessageProcessor( aliasList );
|
||||
mTowerMessageFramer.addMessageListener( mMessageProcessor );
|
||||
mTransponderMessageFramer.addMessageListener( mMessageProcessor );
|
||||
mMessageProcessor.setMessageListener( this );
|
||||
mMessageProcessor.setMessageListener( getMessageListener() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -73,7 +73,7 @@ public class LTRNetDecoder extends Decoder
|
||||
|
||||
mLTRMessageFramer.addMessageListener( mLTRMessageProcessor );
|
||||
|
||||
mLTRMessageProcessor.setMessageListener( this );
|
||||
mLTRMessageProcessor.setMessageListener( getMessageListener() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -62,7 +62,7 @@ public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBuffer
|
||||
|
||||
mLTRMessageProcessor = new LTRStandardMessageProcessor( direction, aliasList );
|
||||
mLTRMessageFramer.addMessageListener( mLTRMessageProcessor );
|
||||
mLTRMessageProcessor.setMessageListener( this );
|
||||
mLTRMessageProcessor.setMessageListener( getMessageListener() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -101,7 +101,7 @@ public class MDCDecoder extends Decoder implements IFilteredRealBufferListener,
|
||||
/* Message Processor */
|
||||
mMessageProcessor = new MDCMessageProcessor( aliasList );
|
||||
mMessageFramer.addMessageListener( mMessageProcessor );
|
||||
mMessageProcessor.addMessageListener( this );
|
||||
mMessageProcessor.addMessageListener(getMessageListener());
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
|
||||
@@ -147,7 +147,7 @@ public class MPT1327Decoder extends Decoder implements IFilteredRealBufferListen
|
||||
|
||||
/* Fully decoded and framed messages processor */
|
||||
mMessageProcessor = new MPT1327MessageProcessor(aliasList);
|
||||
mMessageProcessor.setMessageListener(this);
|
||||
mMessageProcessor.setMessageListener(getMessageListener());
|
||||
mControlMessageFramer.addMessageListener(mMessageProcessor);
|
||||
mTrafficMessageFramer.addMessageListener(mMessageProcessor);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
/*******************************************************************************
|
||||
* 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.p25;
|
||||
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.instrument.Instrumentable;
|
||||
import io.github.dsheirer.module.decode.Decoder;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
@@ -9,62 +23,69 @@ import io.github.dsheirer.source.ISourceEventProvider;
|
||||
|
||||
public abstract class P25Decoder extends Decoder implements ISourceEventListener, ISourceEventProvider, Instrumentable
|
||||
{
|
||||
private P25MessageProcessor mMessageProcessor;
|
||||
|
||||
public P25Decoder( AliasList aliasList )
|
||||
{
|
||||
mMessageProcessor = new P25MessageProcessor( aliasList );
|
||||
mMessageProcessor.setMessageListener( this );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
|
||||
mMessageProcessor.dispose();
|
||||
mMessageProcessor = null;
|
||||
}
|
||||
private P25MessageProcessor mMessageProcessor;
|
||||
private AliasList mAliasList;
|
||||
|
||||
@Override
|
||||
public DecoderType getDecoderType()
|
||||
public P25Decoder(AliasList aliasList)
|
||||
{
|
||||
return DecoderType.P25_PHASE1;
|
||||
mAliasList = aliasList;
|
||||
mMessageProcessor = new P25MessageProcessor(mAliasList);
|
||||
mMessageProcessor.setMessageListener(getMessageListener());
|
||||
}
|
||||
|
||||
protected P25MessageProcessor getMessageProcessor()
|
||||
{
|
||||
return mMessageProcessor;
|
||||
}
|
||||
|
||||
public abstract Modulation getModulation();
|
||||
|
||||
public enum Modulation
|
||||
{
|
||||
CQPSK( "Simulcast (LSM)", "LSM" ),
|
||||
C4FM( "Normal (C4FM)", "C4FM" );
|
||||
|
||||
private String mLabel;
|
||||
private String mShortLabel;
|
||||
|
||||
private Modulation( String label, String shortLabel )
|
||||
{
|
||||
mLabel = label;
|
||||
mShortLabel = shortLabel;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public String getShortLabel()
|
||||
{
|
||||
return mShortLabel;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return getLabel();
|
||||
}
|
||||
}
|
||||
protected AliasList getAliasList()
|
||||
{
|
||||
return mAliasList;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
|
||||
mMessageProcessor.dispose();
|
||||
mMessageProcessor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecoderType getDecoderType()
|
||||
{
|
||||
return DecoderType.P25_PHASE1;
|
||||
}
|
||||
|
||||
protected P25MessageProcessor getMessageProcessor()
|
||||
{
|
||||
return mMessageProcessor;
|
||||
}
|
||||
|
||||
public abstract Modulation getModulation();
|
||||
|
||||
public enum Modulation
|
||||
{
|
||||
CQPSK("Simulcast (LSM)", "LSM"),
|
||||
C4FM("Normal (C4FM)", "C4FM");
|
||||
|
||||
private String mLabel;
|
||||
private String mShortLabel;
|
||||
|
||||
private Modulation(String label, String shortLabel)
|
||||
{
|
||||
mLabel = label;
|
||||
mShortLabel = shortLabel;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public String getShortLabel()
|
||||
{
|
||||
return mShortLabel;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return getLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,222 +1,200 @@
|
||||
/*******************************************************************************
|
||||
* 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.module.decode.p25;
|
||||
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.instrument.tap.Tap;
|
||||
import io.github.dsheirer.instrument.tap.TapGroup;
|
||||
import io.github.dsheirer.instrument.tap.stream.DibitTap;
|
||||
import io.github.dsheirer.instrument.tap.stream.FloatTap;
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
|
||||
import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter;
|
||||
import io.github.dsheirer.dsp.psk.DQPSKDemodulator;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBufferInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.pll.CostasLoop;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.real.IFilteredRealBufferListener;
|
||||
import io.github.dsheirer.sample.real.RealBuffer;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.sample.complex.ComplexBuffer;
|
||||
import io.github.dsheirer.sample.complex.IComplexBufferListener;
|
||||
import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferListener
|
||||
public class P25_C4FMDecoder extends P25Decoder implements IComplexBufferListener, Listener<ComplexBuffer>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class );
|
||||
|
||||
/* Instrumentation Taps */
|
||||
private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output";
|
||||
private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output";
|
||||
private final static Logger mLog = LoggerFactory.getLogger(P25_C4FMDecoder.class);
|
||||
protected static final double SYMBOL_RATE = 4800;
|
||||
protected InterpolatingSampleBuffer mInterpolatingSampleBuffer;
|
||||
protected DQPSKDemodulator mQPSKDemodulator;
|
||||
protected CostasLoop mCostasLoop;
|
||||
protected P25MessageFramer mMessageFramer;
|
||||
protected double mSampleRate;
|
||||
private Map<Double,float[]> mBasebandFilters = new HashMap<>();
|
||||
private ComplexFIRFilter mBasebandFilter;
|
||||
|
||||
private List<TapGroup> mAvailableTaps;
|
||||
private C4FMSymbolFilter mSymbolFilter;
|
||||
private C4FMSlicer mC4FMSlicer;
|
||||
private P25MessageFramer mMessageFramer;
|
||||
|
||||
/**
|
||||
* P25 Phase 1 C4FM Decoder processes real buffers of un-filtered,
|
||||
* demodulated audio and produces decoded P25 Phase 1 Messages.
|
||||
*
|
||||
* Provides Frequency Control to steer external tuner source and incorporates
|
||||
* internal gain control.
|
||||
*
|
||||
* Note: use the P25AudioModule to convert the decoded messages into audio.
|
||||
*
|
||||
* @param aliasList - optional (can be null) list of alias values for network
|
||||
* infrastructure and subscriber identities that will be included in each
|
||||
* decoded message
|
||||
*/
|
||||
public P25_C4FMDecoder( AliasList aliasList, int frequencyCorrectionMaximum )
|
||||
{
|
||||
super( aliasList );
|
||||
|
||||
/* Shape gain and frequency offsets to optimize sample stream */
|
||||
mSymbolFilter = new C4FMSymbolFilter( frequencyCorrectionMaximum );
|
||||
|
||||
/* Convert samples to symbols */
|
||||
mC4FMSlicer = new C4FMSlicer();
|
||||
mSymbolFilter.setListener( mC4FMSlicer );
|
||||
|
||||
/* Sync pattern detection and message construction */
|
||||
mMessageFramer = new P25MessageFramer( aliasList );
|
||||
mC4FMSlicer.addListener( mMessageFramer );
|
||||
|
||||
/* Process and broadcast messages */
|
||||
mMessageFramer.setListener( getMessageProcessor() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
|
||||
mSymbolFilter.dispose();
|
||||
mSymbolFilter = null;
|
||||
|
||||
mMessageFramer.dispose();
|
||||
mMessageFramer = null;
|
||||
|
||||
mC4FMSlicer.dispose();
|
||||
mC4FMSlicer = null;
|
||||
}
|
||||
|
||||
public Modulation getModulation()
|
||||
{
|
||||
return Modulation.C4FM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<RealBuffer> getFilteredRealBufferListener()
|
||||
{
|
||||
return mSymbolFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instrumentation taps for monitoring internal processing
|
||||
*/
|
||||
@Override
|
||||
public List<TapGroup> getTapGroups()
|
||||
public P25_C4FMDecoder(AliasList aliasList)
|
||||
{
|
||||
if( mAvailableTaps == null )
|
||||
{
|
||||
mAvailableTaps = new ArrayList<>();
|
||||
|
||||
TapGroup group = new TapGroup( "P25 C4FM Decoder" );
|
||||
|
||||
group.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) );
|
||||
group.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) );
|
||||
|
||||
mAvailableTaps.add( group );
|
||||
|
||||
if( mSymbolFilter != null )
|
||||
{
|
||||
mAvailableTaps.addAll( mSymbolFilter.getTapGroups() );
|
||||
}
|
||||
}
|
||||
|
||||
return mAvailableTaps;
|
||||
super(aliasList);
|
||||
setSampleRate(48000.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an instrumentation tap to monitor internal processing.
|
||||
*/
|
||||
@Override
|
||||
public void registerTap( Tap tap )
|
||||
public void setSampleRate(double sampleRate)
|
||||
{
|
||||
if( mSymbolFilter != null )
|
||||
{
|
||||
mSymbolFilter.registerTap( tap );
|
||||
}
|
||||
|
||||
switch( tap.getName() )
|
||||
{
|
||||
case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT:
|
||||
FloatTap symbolTap = (FloatTap)tap;
|
||||
mSymbolFilter.setListener( symbolTap );
|
||||
symbolTap.setListener( mC4FMSlicer );
|
||||
break;
|
||||
case INSTRUMENT_C4FM_SLICER_OUTPUT:
|
||||
DibitTap slicerTap = (DibitTap)tap;
|
||||
|
||||
if( mC4FMSlicer != null )
|
||||
{
|
||||
mC4FMSlicer.addListener( slicerTap );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(sampleRate <= SYMBOL_RATE * 2)
|
||||
{
|
||||
throw new IllegalArgumentException("Sample rate must be at least twice the symbol rate [4800]");
|
||||
}
|
||||
|
||||
mSampleRate = sampleRate;
|
||||
mBasebandFilter = new ComplexFIRFilter(getBasebandFilter(), 1.0f);
|
||||
|
||||
mCostasLoop = new CostasLoop(mSampleRate, SYMBOL_RATE);
|
||||
|
||||
mInterpolatingSampleBuffer = new InterpolatingSampleBufferInstrumented((float)(sampleRate / SYMBOL_RATE));
|
||||
mQPSKDemodulator = new DQPSKDemodulator(mCostasLoop, mInterpolatingSampleBuffer);
|
||||
|
||||
//Message framer can trigger a symbol-inversion correction to the PLL when detected
|
||||
mMessageFramer = new P25MessageFramer(getAliasList(), mCostasLoop);
|
||||
mMessageFramer.setListener(getMessageProcessor());
|
||||
mQPSKDemodulator.setDibitListener(mMessageFramer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the instrumentation tap.
|
||||
*/
|
||||
@Override
|
||||
public void unregisterTap( Tap tap )
|
||||
@Override
|
||||
public void receive(ComplexBuffer complexBuffer)
|
||||
{
|
||||
if( mSymbolFilter != null )
|
||||
{
|
||||
mSymbolFilter.unregisterTap( tap );
|
||||
}
|
||||
|
||||
switch( tap.getName() )
|
||||
{
|
||||
case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT:
|
||||
mSymbolFilter.setListener( mC4FMSlicer );
|
||||
break;
|
||||
case INSTRUMENT_C4FM_SLICER_OUTPUT:
|
||||
DibitTap slicerTap = (DibitTap)tap;
|
||||
|
||||
if( mC4FMSlicer != null )
|
||||
{
|
||||
mC4FMSlicer.removeListener( slicerTap );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unrecognized tap: " +
|
||||
tap.getName() );
|
||||
}
|
||||
ComplexBuffer filtered = filter(complexBuffer);
|
||||
mQPSKDemodulator.receive(filtered);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceEventListener(Listener<SourceEvent> listener )
|
||||
{
|
||||
if( mSymbolFilter != null )
|
||||
{
|
||||
mSymbolFilter.setFrequencyChangeListener( listener );
|
||||
}
|
||||
}
|
||||
protected ComplexBuffer filter(ComplexBuffer complexBuffer)
|
||||
{
|
||||
return mBasebandFilter.filter(complexBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSourceEventListener()
|
||||
{
|
||||
if( mSymbolFilter != null )
|
||||
{
|
||||
mSymbolFilter.setFrequencyChangeListener( null );
|
||||
}
|
||||
}
|
||||
private double getSampleRate()
|
||||
{
|
||||
return mSampleRate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<SourceEvent> getSourceEventListener()
|
||||
{
|
||||
if( mSymbolFilter != null )
|
||||
{
|
||||
return mSymbolFilter.getSourceEventListener();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private float[] getBasebandFilter()
|
||||
{
|
||||
float[] filter = mBasebandFilters.get(getSampleRate());
|
||||
|
||||
if(filter == null)
|
||||
{
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate((int)getSampleRate())
|
||||
.passBandCutoff(5100)
|
||||
.passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandStart(6500)
|
||||
.stopBandRipple(0.01)
|
||||
.build();
|
||||
|
||||
try
|
||||
{
|
||||
filter = FilterFactory.getTaps(specification);
|
||||
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.error("Couldn't design low pass baseband filter for sample rate: " + getSampleRate());
|
||||
}
|
||||
if(filter != null)
|
||||
{
|
||||
mBasebandFilters.put(getSampleRate(), filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException("Couldn't design a C4FM symbol filter for sample rate: " + mSampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
|
||||
mBasebandFilter.dispose();
|
||||
mBasebandFilter = null;
|
||||
|
||||
mMessageFramer.dispose();
|
||||
mMessageFramer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFrequencyChangeListener()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
|
||||
{
|
||||
return new Listener<FrequencyChangeEvent>()
|
||||
{
|
||||
@Override
|
||||
public void receive(FrequencyChangeEvent frequencyChangeEvent)
|
||||
{
|
||||
switch(frequencyChangeEvent.getEvent())
|
||||
{
|
||||
case NOTIFICATION_FREQUENCY_CHANGE:
|
||||
case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE:
|
||||
case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
|
||||
case NOTIFICATION_SAMPLE_RATE_CHANGE:
|
||||
mCostasLoop.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ComplexBuffer> getComplexBufferListener()
|
||||
{
|
||||
return P25_C4FMDecoder.this;
|
||||
}
|
||||
|
||||
public Modulation getModulation()
|
||||
{
|
||||
return Modulation.C4FM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
mCostasLoop.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(ScheduledExecutorService executor)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*******************************************************************************
|
||||
* 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.p25;
|
||||
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.dsp.psk.DQPSKDemodulatorInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBufferInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.SymbolDecisionData;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
import io.github.dsheirer.sample.complex.ComplexBuffer;
|
||||
|
||||
public class P25_C4FMDecoderInstrumented extends P25_C4FMDecoder
|
||||
{
|
||||
private Listener<Double> mPLLPhaseErrorListener;
|
||||
private Listener<Double> mPLLFrequencyListener;
|
||||
private Listener<Double> mSamplesPerSymbolListener;
|
||||
private Listener<Complex> mSymbolListener;
|
||||
private Listener<ComplexBuffer> mFilteredSymbolListener;
|
||||
private Listener<SymbolDecisionData> mSymbolDecisionDataListener;
|
||||
|
||||
public P25_C4FMDecoderInstrumented(AliasList aliasList)
|
||||
{
|
||||
super(aliasList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the filter method so that we can capture the filtered samples for instrumentation
|
||||
*/
|
||||
protected ComplexBuffer filter(ComplexBuffer complexBuffer)
|
||||
{
|
||||
ComplexBuffer filtered = super.filter(complexBuffer);
|
||||
|
||||
if(mFilteredSymbolListener != null)
|
||||
{
|
||||
mFilteredSymbolListener.receive(filtered);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides this method so we can correctly configure for instrumented operations
|
||||
*/
|
||||
public void setSampleRate(double sampleRate)
|
||||
{
|
||||
super.setSampleRate(sampleRate);
|
||||
InterpolatingSampleBufferInstrumented instrumentedBuffer = new InterpolatingSampleBufferInstrumented((float)(sampleRate / SYMBOL_RATE));
|
||||
mInterpolatingSampleBuffer = instrumentedBuffer;
|
||||
|
||||
DQPSKDemodulatorInstrumented instrumented = new DQPSKDemodulatorInstrumented(mCostasLoop, instrumentedBuffer, mSampleRate);
|
||||
mQPSKDemodulator = instrumented;
|
||||
|
||||
instrumented.setSymbolListener(mSymbolListener);
|
||||
instrumented.setPLLErrorListener(mPLLPhaseErrorListener);
|
||||
instrumented.setPLLFrequencyListener(mPLLFrequencyListener);
|
||||
instrumented.setSymbolDecisionDataListener(mSymbolDecisionDataListener);
|
||||
instrumented.setSamplesPerSymbolListener(mSamplesPerSymbolListener);
|
||||
instrumented.setDibitListener(mMessageFramer);
|
||||
}
|
||||
|
||||
public void setSymbolListener(Listener<Complex> listener)
|
||||
{
|
||||
mSymbolListener = listener;
|
||||
((DQPSKDemodulatorInstrumented)mQPSKDemodulator).setSymbolListener(listener);
|
||||
}
|
||||
|
||||
public void setPLLPhaseErrorListener(Listener<Double> listener)
|
||||
{
|
||||
mPLLPhaseErrorListener = listener;
|
||||
((DQPSKDemodulatorInstrumented)mQPSKDemodulator).setPLLErrorListener(listener);
|
||||
}
|
||||
|
||||
public void setPLLFrequencyListener(Listener<Double> listener)
|
||||
{
|
||||
mPLLFrequencyListener = listener;
|
||||
((DQPSKDemodulatorInstrumented)mQPSKDemodulator).setPLLFrequencyListener(listener);
|
||||
}
|
||||
|
||||
public void setFilteredBufferListener(Listener<ComplexBuffer> listener)
|
||||
{
|
||||
mFilteredSymbolListener = listener;
|
||||
}
|
||||
|
||||
public void setSymbolDecisionDataListener(Listener<SymbolDecisionData> listener)
|
||||
{
|
||||
mSymbolDecisionDataListener = listener;
|
||||
((DQPSKDemodulatorInstrumented)mQPSKDemodulator).setSymbolDecisionDataListener(listener);
|
||||
}
|
||||
|
||||
public void setSamplesPerSymbolListener(Listener<Double> listener)
|
||||
{
|
||||
mSamplesPerSymbolListener = listener;
|
||||
((DQPSKDemodulatorInstrumented)mQPSKDemodulator).setSamplesPerSymbolListener(listener);
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,8 @@ import io.github.dsheirer.dsp.filter.Window.WindowType;
|
||||
import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter_CB_CB;
|
||||
import io.github.dsheirer.dsp.gain.ComplexFeedForwardGainControl;
|
||||
import io.github.dsheirer.dsp.psk.LSMDemodulator;
|
||||
import io.github.dsheirer.dsp.psk.QPSKPolarSlicer;
|
||||
import io.github.dsheirer.instrument.tap.Tap;
|
||||
import io.github.dsheirer.dsp.psk.QPSKSymbolDecoder;
|
||||
import io.github.dsheirer.instrument.tap.TapGroup;
|
||||
import io.github.dsheirer.instrument.tap.stream.ComplexSampleTap;
|
||||
import io.github.dsheirer.instrument.tap.stream.ComplexTap;
|
||||
import io.github.dsheirer.instrument.tap.stream.DibitTap;
|
||||
import io.github.dsheirer.instrument.tap.stream.QPSKTap;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.ComplexBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexBufferToStreamConverter;
|
||||
@@ -37,7 +32,6 @@ import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class P25_LSMDecoder extends P25Decoder implements IReusableComplexBufferListener
|
||||
|
||||
@@ -78,7 +78,7 @@ public class SyncBroadcast extends TSBKMessage
|
||||
|
||||
if( isMicroslotsLockedToMinuteRollover() )
|
||||
{
|
||||
sb.append( " MICROSLOT-MINUTE ROLLOVER:LOCKED" );
|
||||
sb.append( " MICROSLOT-MINUTE ROLLOVER:SLOW" );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -62,7 +62,7 @@ public class PassportDecoder extends Decoder
|
||||
|
||||
mPassportMessageProcessor = new PassportMessageProcessor( aliasList );
|
||||
mPassportMessageFramer.addMessageListener( mPassportMessageProcessor );
|
||||
mPassportMessageProcessor.setMessageListener( this );
|
||||
mPassportMessageProcessor.setMessageListener( getMessageListener() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -103,8 +103,8 @@ public class Tait1200Decoder extends Decoder implements IFilteredRealBufferListe
|
||||
mMessageFramerGPS.addMessageListener( mMessageAProcessor );
|
||||
mMessageFramerANI.addMessageListener( mMessageBProcessor );
|
||||
|
||||
mMessageAProcessor.setMessageListener( this );
|
||||
mMessageBProcessor.setMessageListener( this );
|
||||
mMessageAProcessor.setMessageListener(getMessageListener());
|
||||
mMessageBProcessor.setMessageListener(getMessageListener());
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
|
||||
Reference in New Issue
Block a user