mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
254 lines
8.0 KiB
Java
254 lines
8.0 KiB
Java
/*******************************************************************************
|
|
* 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/>
|
|
******************************************************************************/
|
|
package dsp.fsk;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import dsp.filter.Filters;
|
|
import dsp.filter.FloatHalfBandFilter;
|
|
import dsp.filter.FloatHalfBandNoDecimateFilter;
|
|
import dsp.filter.LTRPulseShapingFilter;
|
|
import dsp.filter.SquaringFilter;
|
|
import dsp.filter.Window.WindowType;
|
|
import dsp.filter.cic.RealPrimeCICDecimate;
|
|
import dsp.filter.dc.IIRSinglePoleDCRemovalFilter;
|
|
import dsp.symbol.Slicer;
|
|
import dsp.symbol.Slicer.Output;
|
|
import instrument.Instrumentable;
|
|
import instrument.tap.Tap;
|
|
import instrument.tap.TapGroup;
|
|
import instrument.tap.stream.BinaryTap;
|
|
import instrument.tap.stream.FloatBufferTap;
|
|
import instrument.tap.stream.FloatTap;
|
|
import instrument.tap.stream.SymbolEventTap;
|
|
import sample.Listener;
|
|
import sample.real.RealBuffer;
|
|
|
|
public class LTRFSKDecoder implements Listener<RealBuffer>, Instrumentable
|
|
{
|
|
private RealPrimeCICDecimate mCICDecimator;
|
|
|
|
private FloatHalfBandFilter mHBFilter1;
|
|
private FloatHalfBandFilter mHBFilter2;
|
|
private FloatHalfBandFilter mHBFilter3;
|
|
private FloatHalfBandFilter mHBFilter4;
|
|
private FloatHalfBandFilter mHBFilter5;
|
|
private FloatHalfBandNoDecimateFilter mHBFilter6;
|
|
private IIRSinglePoleDCRemovalFilter mDCFilter;
|
|
private SquaringFilter mSquaringFilter;
|
|
private LTRPulseShapingFilter mPulseShaper;
|
|
private Slicer mSlicer;
|
|
|
|
private List<TapGroup> mAvailableTaps;
|
|
private final String TAP_F1_F2 = "LTR FSK Demod Filter1 >< Filter2";
|
|
private final String TAP_F2_F3 = "LTR FSK Demod Filter2 >< Filter3";
|
|
private final String TAP_F3_F4 = "LTR FSK Demod Filter3 >< Filter4";
|
|
private final String TAP_F4_F5 = "LTR FSK Demod Filter4 >< Filter5";
|
|
private final String TAP_F5_F6 = "LTR FSK Demod Filter5 >< Filter6";
|
|
private final String TAP_F6_DT = "LTR FSK Demod Filter6 >< DC Filter";
|
|
private final String TAP_CIC = "LTR FSK Demod CIC Decimator";
|
|
private final String TAP_DT_SF1 = "LTR FSK Demod DC Filter >< Squaring Filter";
|
|
private final String TAP_SF1_PS = "LTR FSK Demod Squaring Filter >< Pulse Shaper";
|
|
private final String TAP_PS_SLICER = "LTR FSK Demod Pulse Shaper >< Slicer";
|
|
private final String TAP_SYMBOL_EVENT = "LTR FSK Demod Symbol Event";
|
|
|
|
/**
|
|
* Implements a Logic Trunked Radio sub-audible 300 baud FSK signaling
|
|
* decoder. Expects a 48000 sample rate input.
|
|
*/
|
|
public LTRFSKDecoder()
|
|
{
|
|
mCICDecimator = new RealPrimeCICDecimate( 32, 1, 375, 60, WindowType.HAMMING, 2 );
|
|
|
|
mHBFilter1 = new FloatHalfBandFilter(
|
|
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
|
|
|
|
mHBFilter2 = new FloatHalfBandFilter(
|
|
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
|
|
mHBFilter1.setListener( mHBFilter2 );
|
|
|
|
mHBFilter3 = new FloatHalfBandFilter(
|
|
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
|
|
mHBFilter2.setListener( mHBFilter3 );
|
|
|
|
mHBFilter4 = new FloatHalfBandFilter(
|
|
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
|
|
mHBFilter3.setListener( mHBFilter4 );
|
|
|
|
mHBFilter5 = new FloatHalfBandFilter(
|
|
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
|
|
mHBFilter4.setListener( mHBFilter5 );
|
|
|
|
mHBFilter6 = new FloatHalfBandNoDecimateFilter(
|
|
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f );
|
|
mHBFilter5.setListener( mHBFilter6 );
|
|
|
|
mDCFilter = new IIRSinglePoleDCRemovalFilter( 0.9946f );
|
|
mHBFilter6.setListener( mDCFilter );
|
|
|
|
mSquaringFilter = new SquaringFilter();
|
|
mDCFilter.setListener( mSquaringFilter );
|
|
|
|
mPulseShaper = new LTRPulseShapingFilter();
|
|
mSquaringFilter.setListener( mPulseShaper );
|
|
|
|
mSlicer = new Slicer( Output.NORMAL, 5 );
|
|
mPulseShaper.setListener( mSlicer );
|
|
}
|
|
|
|
@Override
|
|
public void receive( RealBuffer buffer )
|
|
{
|
|
for( float sample: buffer.getSamples() )
|
|
{
|
|
mHBFilter1.receive( sample );
|
|
}
|
|
|
|
mCICDecimator.receive(buffer);
|
|
}
|
|
|
|
public void addListener( Listener<Boolean> listener )
|
|
{
|
|
mSlicer.setListener( listener );
|
|
}
|
|
|
|
public void removeListener( Listener<Boolean> listener )
|
|
{
|
|
mSlicer.removeListener( listener );
|
|
}
|
|
|
|
@Override
|
|
public List<TapGroup> getTapGroups()
|
|
{
|
|
if( mAvailableTaps == null )
|
|
{
|
|
mAvailableTaps = new ArrayList<TapGroup>();
|
|
|
|
TapGroup group = new TapGroup( "LTR FSK Decoder" );
|
|
|
|
group.add( new FloatTap( TAP_F1_F2, 0, 1.0f ) );
|
|
group.add( new FloatTap( TAP_F2_F3, 8, 0.5f ) );
|
|
group.add( new FloatTap( TAP_F3_F4, 12, 0.25f ) );
|
|
group.add( new FloatTap( TAP_F4_F5, 14, 0.125f ) );
|
|
group.add( new FloatTap( TAP_F5_F6, 15, 0.0625f ) );
|
|
group.add( new FloatTap( TAP_F6_DT, 31, 0.0625f ) );
|
|
group.add( new FloatBufferTap( TAP_CIC, 31, 0.0625f ) );
|
|
group.add( new FloatTap( TAP_DT_SF1, 31, 0.0625f ) );
|
|
group.add( new BinaryTap( TAP_SF1_PS, 33, 0.0625f ) );
|
|
group.add( new BinaryTap( TAP_PS_SLICER, 33, 0.0625f ) );
|
|
group.add( new SymbolEventTap( TAP_SYMBOL_EVENT, 7, 0.0125f ) );
|
|
|
|
mAvailableTaps.add( group );
|
|
}
|
|
|
|
return mAvailableTaps;
|
|
}
|
|
|
|
@Override
|
|
public void registerTap( Tap tap )
|
|
{
|
|
switch( tap.getName() )
|
|
{
|
|
case TAP_CIC:
|
|
mCICDecimator.setListener( (FloatBufferTap)tap );
|
|
break;
|
|
case TAP_F1_F2:
|
|
mHBFilter1.setListener( (FloatTap)tap );
|
|
((FloatTap)tap).setListener( mHBFilter2 );
|
|
break;
|
|
case TAP_F2_F3:
|
|
mHBFilter2.setListener( (FloatTap)tap );
|
|
((FloatTap)tap).setListener( mHBFilter3 );
|
|
break;
|
|
case TAP_F3_F4:
|
|
mHBFilter3.setListener( (FloatTap)tap );
|
|
((FloatTap)tap).setListener( mHBFilter4 );
|
|
break;
|
|
case TAP_F4_F5:
|
|
mHBFilter4.setListener( (FloatTap)tap );
|
|
((FloatTap)tap).setListener( mHBFilter5 );
|
|
break;
|
|
case TAP_F5_F6:
|
|
mHBFilter5.setListener( (FloatTap)tap );
|
|
((FloatTap)tap).setListener( mHBFilter6 );
|
|
break;
|
|
case TAP_F6_DT:
|
|
mHBFilter6.setListener( (FloatTap)tap );
|
|
((FloatTap)tap).setListener( mDCFilter );
|
|
break;
|
|
case TAP_DT_SF1:
|
|
mDCFilter.setListener( (FloatTap)tap );
|
|
((FloatTap)tap).setListener( mSquaringFilter );
|
|
break;
|
|
case TAP_SF1_PS:
|
|
mSquaringFilter.setListener( (BinaryTap)tap );
|
|
((BinaryTap)tap).setListener( mPulseShaper );
|
|
break;
|
|
case TAP_PS_SLICER:
|
|
mPulseShaper.setListener( (BinaryTap)tap );
|
|
((BinaryTap)tap).setListener( mSlicer );
|
|
break;
|
|
case TAP_SYMBOL_EVENT:
|
|
mSlicer.addTap( (SymbolEventTap)tap );
|
|
break;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void unregisterTap( Tap tap )
|
|
{
|
|
switch( tap.getName() )
|
|
{
|
|
case TAP_CIC:
|
|
mCICDecimator.removeListener();
|
|
break;
|
|
case TAP_F1_F2:
|
|
mHBFilter1.setListener( mHBFilter2 );
|
|
break;
|
|
case TAP_F2_F3:
|
|
mHBFilter2.setListener( mHBFilter3 );
|
|
break;
|
|
case TAP_F3_F4:
|
|
mHBFilter3.setListener( mHBFilter4 );
|
|
break;
|
|
case TAP_F4_F5:
|
|
mHBFilter4.setListener( mHBFilter5 );
|
|
break;
|
|
case TAP_F5_F6:
|
|
mHBFilter5.setListener( mHBFilter6 );
|
|
break;
|
|
case TAP_F6_DT:
|
|
mHBFilter6.setListener( mDCFilter );
|
|
break;
|
|
case TAP_DT_SF1:
|
|
mDCFilter.setListener( mSquaringFilter );
|
|
break;
|
|
case TAP_SF1_PS:
|
|
mSquaringFilter.setListener( mPulseShaper );
|
|
break;
|
|
case TAP_PS_SLICER:
|
|
mPulseShaper.setListener( mSlicer );
|
|
break;
|
|
case TAP_SYMBOL_EVENT:
|
|
mSlicer.removeTap();
|
|
break;
|
|
}
|
|
}
|
|
}
|