Merge pull request #187 from DSheirer/186-p25-c4fm-decoder-no-worky

Resolves #186 - p25 C4FM decoder wasn't working (at all).
This commit is contained in:
Denny Sheirer
2017-02-11 12:17:58 +00:00
committed by GitHub
4 changed files with 95 additions and 108 deletions

View File

@@ -1,65 +1,64 @@
package dsp.filter.design; package dsp.filter.design;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dsp.filter.fir.FIRFilterSpecification; import dsp.filter.fir.FIRFilterSpecification;
import dsp.filter.fir.remez.RemezFIRFilterDesigner; import dsp.filter.fir.remez.RemezFIRFilterDesigner;
import javafx.application.Application; import javafx.application.Application;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FilterViewer extends Application public class FilterViewer extends Application
{ {
private final static Logger mLog = LoggerFactory.getLogger( FilterViewer.class ); private final static Logger mLog = LoggerFactory.getLogger(FilterViewer.class);
/** /**
* Developer tool to visualize filter designs * Developer tool to visualize filter designs
*/ */
public FilterViewer() public FilterViewer()
{ {
} }
@Override @Override
public void start( Stage primaryStage ) throws Exception public void start(Stage primaryStage) throws Exception
{ {
Scene scene = new Scene( new FilterView( getFilter() ) ); Scene scene = new Scene(new FilterView(getFilter()));
primaryStage.setTitle( "Filter Viewer" ); primaryStage.setTitle("Filter Viewer");
primaryStage.setScene( scene ); primaryStage.setScene(scene);
primaryStage.show(); primaryStage.show();
} }
public static void main( String[] args ) public static void main(String[] args)
{ {
launch( args ); launch(args);
} }
/** /**
* Provides the filter to visualize. Modify this method to visualize your filter design. * Provides the filter to visualize. Modify this method to visualize your filter design.
*/ */
private float[] getFilter() private float[] getFilter()
{ {
// FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder() FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
// .sampleRate( 48000 ) .sampleRate(48000)
// .gridDensity( 16 ) .gridDensity(16)
// .passBandCutoff( 3200 ) .passBandCutoff(2500)
// .passBandAmplitude( 1.0 ) .passBandAmplitude(1.0)
// .passBandRipple( 0.02 ) .passBandRipple(0.01)
// .stopBandStart( 4000 ) .stopBandStart(4000)
// .stopBandAmplitude( 0.0 ) .stopBandAmplitude(0.0)
// .stopBandRipple( 0.03 ) .stopBandRipple(0.008)
// .build(); .build();
FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder() // FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder()
.sampleRate( 24000 ) // .sampleRate(24000)
.stopBandCutoff( 800 ) // .stopBandCutoff(800)
.stopBandAmplitude( 0.0 ) // .stopBandAmplitude(0.0)
.stopBandRipple( 0.03 ) // .stopBandRipple(0.03)
.passBandStart( 1000 ) // .passBandStart(1000)
.passBandAmplitude( 1.0 ) // .passBandAmplitude(1.0)
.passBandRipple( 0.08 ) // .passBandRipple(0.08)
.build(); // .build();
// FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder() // FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
// .sampleRate( 48000 ) // .sampleRate( 48000 )
@@ -71,24 +70,25 @@ public class FilterViewer extends Application
// .passRipple( 0.008) // .passRipple( 0.008)
// .build(); // .build();
float[] taps = null; float[] taps = null;
// taps = FilterFactory.getLowPass( 16000, 2400, 73, WindowType.HANNING ); // taps = FilterFactory.getLowPass(48000, 6750, 7500, 60,
// Window.WindowType.HAMMING, true);
try try
{ {
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner( specification ); RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
if( designer.isValid() ) if(designer.isValid())
{ {
taps = designer.getImpulseResponse(); taps = designer.getImpulseResponse();
} }
} }
catch( FilterDesignException fde ) catch(FilterDesignException fde)
{ {
mLog.error( "Filter design error", fde ); mLog.error("Filter design error", fde);
} }
return taps; return taps;
} }
} }

View File

@@ -103,35 +103,35 @@ public class DecoderFactory
//Low-pass filter with ~60 dB attenuation and 89 taps //Low-pass filter with ~60 dB attenuation and 89 taps
private static final FIRFilterSpecification MPT1327_FILTER_SPECIFICATION = FIRFilterSpecification.lowPassBuilder() private static final FIRFilterSpecification MPT1327_FILTER_SPECIFICATION = FIRFilterSpecification.lowPassBuilder()
.sampleRate( 48000 ) .sampleRate( 48000 ).gridDensity( 16 ).passBandCutoff( 3200 ).passBandAmplitude( 1.0 ).passBandRipple( 0.02 )
.gridDensity( 16 ) .stopBandStart( 4000 ).stopBandAmplitude( 0.0 ).stopBandRipple( 0.03 ).build();
.passBandCutoff( 3200 )
.passBandAmplitude( 1.0 ) private static final FIRFilterSpecification P25_C4FM_IQ_SPECIFICATION = FIRFilterSpecification.lowPassBuilder()
.passBandRipple( 0.02 ) .sampleRate(48000).gridDensity(16).passBandCutoff(6750).passBandAmplitude(1.0).passBandRipple(0.01)
.stopBandStart( 4000 ) .stopBandStart(7000).stopBandAmplitude(0.0).stopBandRipple(0.008).build();
.stopBandAmplitude( 0.0 )
.stopBandRipple( 0.03 ) private static final FIRFilterSpecification P25_C4FM_DEMOD_SPECIFICATION = FIRFilterSpecification.lowPassBuilder()
.build(); .sampleRate(48000).gridDensity(16).passBandCutoff(2500).passBandAmplitude(1.0).passBandRipple(0.01)
.stopBandStart(4000).stopBandAmplitude(0.0).stopBandRipple(0.008).build();
private static float[] MPT1327_LOWPASS_FILTER; private static float[] MPT1327_LOWPASS_FILTER;
private static float[] P25_C4FM_IQ_FILTER;
private static float[] P25_C4FM_DEMOD_FILTER;
static static
{ {
try try
{ {
MPT1327_LOWPASS_FILTER = FilterFactory.getTaps(MPT1327_FILTER_SPECIFICATION); MPT1327_LOWPASS_FILTER = FilterFactory.getTaps(MPT1327_FILTER_SPECIFICATION);
P25_C4FM_IQ_FILTER = FilterFactory.getTaps(P25_C4FM_IQ_SPECIFICATION);
P25_C4FM_DEMOD_FILTER = FilterFactory.getTaps(P25_C4FM_DEMOD_SPECIFICATION);
} }
catch(Exception e) catch(Exception e)
{ {
mLog.error("Couldn't design MPT1327 bandpass filter"); mLog.error("Couldn't design startup filter(s)");
} }
} }
public static final boolean NO_REMOVE_DC = false;
public static final boolean REMOVE_DC = true;
public static final int NO_FREQUENCY_CORRECTION = 0;
/** /**
* Returns a list of one primary decoder and any auxiliary decoders, as * Returns a list of one primary decoder and any auxiliary decoders, as
* specified in the configurations. * specified in the configurations.
@@ -228,7 +228,7 @@ public class DecoderFactory
} }
modules.add(new FMDemodulatorModule(iqPass, iqStop)); modules.add(new FMDemodulatorModule(iqPass, iqStop));
modules.add(new DemodulatedAudioFilterModule(MPT1327_LOWPASS_FILTER, 1.0f)); modules.add(new DemodulatedAudioFilterModule(P25_C4FM_DEMOD_FILTER, 1.0f));
modules.add(new AudioModule(metadata)); modules.add(new AudioModule(metadata));
break; break;
case PASSPORT: case PASSPORT:
@@ -247,7 +247,8 @@ public class DecoderFactory
switch(modulation) switch(modulation)
{ {
case C4FM: case C4FM:
modules.add(new FMDemodulatorModule(6750, 7500)); 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 P25_C4FMDecoder(aliasList, decodeConfig.getAFCMaximumCorrection()));
modules.add(new P25DecoderState(aliasList, channelType, Modulation.C4FM, modules.add(new P25DecoderState(aliasList, channelType, Modulation.C4FM,
p25Config.getIgnoreDataCalls())); p25Config.getIgnoreDataCalls()));

View File

@@ -47,12 +47,10 @@ public class P25_C4FMDecoder extends P25Decoder
private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class ); private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class );
/* Instrumentation Taps */ /* Instrumentation Taps */
private static final String INSTRUMENT_FILTER_OUTPUT = "Tap Point: Pre-Filter Output";
private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output"; 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 static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output";
private List<TapGroup> mAvailableTaps; private List<TapGroup> mAvailableTaps;
private RealFIRFilter_RB_RB mC4FMPreFilter;
private C4FMSymbolFilter mSymbolFilter; private C4FMSymbolFilter mSymbolFilter;
private C4FMSlicer mC4FMSlicer; private C4FMSlicer mC4FMSlicer;
private P25MessageFramer mMessageFramer; private P25MessageFramer mMessageFramer;
@@ -74,14 +72,8 @@ public class P25_C4FMDecoder extends P25Decoder
{ {
super( aliasList ); super( aliasList );
/* Filter demodulated sample buffers */
float[] filter = FilterFactory.getLowPass( 48000, 2500, 4000, 80, WindowType.HANNING, true );
mC4FMPreFilter = new RealFIRFilter_RB_RB( filter, 1.0f );
/* Shape gain and frequency offsets to optimize sample stream */ /* Shape gain and frequency offsets to optimize sample stream */
mSymbolFilter = new C4FMSymbolFilter( frequencyCorrectionMaximum ); mSymbolFilter = new C4FMSymbolFilter( frequencyCorrectionMaximum );
mC4FMPreFilter.setListener( mSymbolFilter );
/* Convert samples to symbols */ /* Convert samples to symbols */
mC4FMSlicer = new C4FMSlicer(); mC4FMSlicer = new C4FMSlicer();
@@ -100,9 +92,6 @@ public class P25_C4FMDecoder extends P25Decoder
{ {
super.dispose(); super.dispose();
mC4FMPreFilter.dispose();
mC4FMPreFilter = null;
mSymbolFilter.dispose(); mSymbolFilter.dispose();
mSymbolFilter = null; mSymbolFilter = null;
@@ -121,7 +110,7 @@ public class P25_C4FMDecoder extends P25Decoder
@Override @Override
public Listener<RealBuffer> getFilteredRealBufferListener() public Listener<RealBuffer> getFilteredRealBufferListener()
{ {
return mC4FMPreFilter; return mSymbolFilter;
} }
/** /**
@@ -136,8 +125,6 @@ public class P25_C4FMDecoder extends P25Decoder
TapGroup group = new TapGroup( "P25 C4FM Decoder" ); TapGroup group = new TapGroup( "P25 C4FM Decoder" );
group.add( new FloatBufferTap( INSTRUMENT_FILTER_OUTPUT, 0, 1.0f ) );
group.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) ); group.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) );
group.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) ); group.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) );
@@ -165,11 +152,6 @@ public class P25_C4FMDecoder extends P25Decoder
switch( tap.getName() ) switch( tap.getName() )
{ {
case INSTRUMENT_FILTER_OUTPUT:
FloatBufferTap filterOutput = (FloatBufferTap)tap;
mC4FMPreFilter.setListener( filterOutput );
filterOutput.setListener( mSymbolFilter );
break;
case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT:
FloatTap symbolTap = (FloatTap)tap; FloatTap symbolTap = (FloatTap)tap;
mSymbolFilter.setListener( symbolTap ); mSymbolFilter.setListener( symbolTap );
@@ -201,9 +183,6 @@ public class P25_C4FMDecoder extends P25Decoder
switch( tap.getName() ) switch( tap.getName() )
{ {
case INSTRUMENT_FILTER_OUTPUT:
mC4FMPreFilter.setListener( mSymbolFilter );
break;
case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT:
mSymbolFilter.setListener( mC4FMSlicer ); mSymbolFilter.setListener( mC4FMSlicer );
break; break;

View File

@@ -62,6 +62,13 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene
mIQFilter.setListener(mDemodulator); mIQFilter.setListener(mDemodulator);
} }
public FMDemodulatorModule(float[] filter)
{
mIQFilter = new ComplexFIRFilter_CB_CB(filter, 1.0f);
mDemodulator = new FMDemodulator_CB(1.0f);
mIQFilter.setListener(mDemodulator);
}
@Override @Override
public Listener<ComplexBuffer> getComplexBufferListener() public Listener<ComplexBuffer> getComplexBufferListener()
{ {