/******************************************************************************* * sdrtrunk * Copyright (C) 2014-2017 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/> * ******************************************************************************/ package module.decode.p25; import instrument.tap.Tap; import instrument.tap.TapGroup; import instrument.tap.stream.DibitTap; import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import sample.Listener; import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.IFrequencyChangeListener; import source.tuner.frequency.IFrequencyChangeProvider; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; import dsp.filter.fir.real.RealFIRFilter_RB_RB; public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferListener { 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 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() { 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; } /** * Adds an instrumentation tap to monitor internal processing. */ @Override public void registerTap( Tap tap ) { 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; } } /** * Removes the instrumentation tap. */ @Override public void unregisterTap( Tap tap ) { 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() ); } } @Override public void reset() { // TODO Auto-generated method stub } @Override public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub } @Override public void stop() { // TODO Auto-generated method stub } @Override public void setFrequencyChangeListener( Listener<FrequencyChangeEvent> listener ) { if( mSymbolFilter != null ) { mSymbolFilter.setFrequencyChangeListener( listener ); } } @Override public void removeFrequencyChangeListener() { if( mSymbolFilter != null ) { mSymbolFilter.setFrequencyChangeListener( null ); } } @Override public Listener<FrequencyChangeEvent> getFrequencyChangeListener() { if( mSymbolFilter != null ) { return mSymbolFilter.getFrequencyChangeListener(); } return null; } }