/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 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.ComplexSampleTap;
import instrument.tap.stream.ComplexTap;
import instrument.tap.stream.DibitTap;
import instrument.tap.stream.QPSKTap;
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.complex.ComplexBuffer;
import sample.complex.ComplexBufferToStreamConverter;
import sample.complex.IComplexBufferListener;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB;
import dsp.gain.ComplexFeedForwardGainControl;
import dsp.psk.LSMDemodulator;
import dsp.psk.QPSKPolarSlicer;
import source.tuner.frequency.FrequencyChangeEvent;
public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
{
private final static Logger mLog = LoggerFactory.getLogger( P25_LSMDecoder.class );
/* Instrumentation Taps */
private static final String INSTRUMENT_BASEBAND_FILTER_OUTPUT = "Tap Point: Baseband Filter Output";
private static final String INSTRUMENT_AGC_OUTPUT = "Tap Point: AGC Output";
private static final String INSTRUMENT_LSM_DEMODULATOR_OUTPUT = "Tap Point: LSM Demodulator Output";
private static final String INSTRUMENT_QPSK_SLICER_OUTPUT = "Tap Point: QPSK Slicer Output";
private List<TapGroup> mAvailableTaps;
private ComplexFIRFilter_CB_CB mBasebandFilter;
private ComplexBufferToStreamConverter mStreamConverter = new ComplexBufferToStreamConverter();
private ComplexFeedForwardGainControl mAGC =
new ComplexFeedForwardGainControl( 32 );
private LSMDemodulator mLSMDemodulator = new LSMDemodulator();
private QPSKPolarSlicer mQPSKSlicer = new QPSKPolarSlicer();
private P25MessageFramer mMessageFramer;
public P25_LSMDecoder( AliasList aliasList )
{
super( aliasList );
mBasebandFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass(
48000, 7250, 8000, 60, WindowType.HANNING, true ), 1.0f );
mBasebandFilter.setListener( mStreamConverter );
mStreamConverter.setListener( mAGC );
mAGC.setListener( mLSMDemodulator );
mLSMDemodulator.setSymbolListener( mQPSKSlicer );
mMessageFramer = new P25MessageFramer( aliasList, mLSMDemodulator );
mQPSKSlicer.addListener( mMessageFramer );
mMessageFramer.setListener( getMessageProcessor() );
}
public void dispose()
{
super.dispose();
mBasebandFilter.dispose();
mBasebandFilter = null;
mStreamConverter.dispose();
mStreamConverter = null;
mAGC.dispose();
mAGC = null;
mLSMDemodulator.dispose();
mLSMDemodulator = null;
mQPSKSlicer.dispose();
mQPSKSlicer = 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)
{
//Ignored
}
};
}
@Override
public Listener<ComplexBuffer> getComplexBufferListener()
{
return mBasebandFilter;
}
public Modulation getModulation()
{
return Modulation.CQPSK;
}
/**
* Provides a list of instrumentation taps for monitoring internal processing
*/
@Override
public List<TapGroup> getTapGroups()
{
if( mAvailableTaps == null )
{
mAvailableTaps = new ArrayList<>();
TapGroup group = new TapGroup( "P25 LSM Decoder" );
group.add( new ComplexTap( INSTRUMENT_BASEBAND_FILTER_OUTPUT, 0, 1.0f ) );
group.add( new ComplexTap( INSTRUMENT_AGC_OUTPUT, 0, 1.0f ) );
group.add( new QPSKTap( INSTRUMENT_LSM_DEMODULATOR_OUTPUT, 0, 1.0f ) );
group.add( new DibitTap( INSTRUMENT_QPSK_SLICER_OUTPUT, 0, 0.1f ) );
mAvailableTaps.add( group );
if( mLSMDemodulator != null )
{
mAvailableTaps.addAll( mLSMDemodulator.getTapGroups() );
}
}
return mAvailableTaps;
}
/**
* Adds the instrumentation tap
*/
@Override
public void registerTap( Tap tap )
{
if( mLSMDemodulator != null )
{
mLSMDemodulator.registerTap( tap );
}
switch( tap.getName() )
{
case INSTRUMENT_BASEBAND_FILTER_OUTPUT:
ComplexSampleTap baseband = (ComplexSampleTap)tap;
mStreamConverter.setListener( baseband );
baseband.setListener( mAGC );
break;
case INSTRUMENT_AGC_OUTPUT:
ComplexSampleTap agcSymbol = (ComplexSampleTap)tap;
mAGC.setListener( agcSymbol );
agcSymbol.setListener( mLSMDemodulator );
break;
case INSTRUMENT_LSM_DEMODULATOR_OUTPUT:
QPSKTap qpsk = (QPSKTap)tap;
mLSMDemodulator.setSymbolListener( qpsk );
qpsk.setListener( mQPSKSlicer );
break;
case INSTRUMENT_QPSK_SLICER_OUTPUT:
mQPSKSlicer.addListener( (DibitTap)tap );
break;
default:
throw new IllegalArgumentException( "Unrecognized tap: " +
tap.getName() );
}
}
/**
* Removes the instrumentation tap
*/
@Override
public void unregisterTap( Tap tap )
{
if( mLSMDemodulator != null )
{
mLSMDemodulator.unregisterTap( tap );
}
switch( tap.getName() )
{
case INSTRUMENT_BASEBAND_FILTER_OUTPUT:
mStreamConverter.setListener( mAGC );
break;
case INSTRUMENT_AGC_OUTPUT:
mAGC.setListener( mLSMDemodulator );
break;
case INSTRUMENT_LSM_DEMODULATOR_OUTPUT:
mLSMDemodulator.setSymbolListener( mQPSKSlicer );
break;
case INSTRUMENT_QPSK_SLICER_OUTPUT:
mQPSKSlicer.removeListener( (DibitTap)tap );
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
}
}