Implements a complex sample delay buffer integrated into the tuner

channel manager so that channel source requests can have a start
timestamp specified and the channel can be preloaded with samples from
the delay buffer.  If no timestamp is specified, current time is assumed.

This feature requires corresponding updates to any decoder that wishes
to implement the timestamped sample preloading functionality.
This commit is contained in:
Denny
2018-09-09 08:46:21 -04:00
parent bb0bcd3198
commit 570321da3e
21 changed files with 632 additions and 98 deletions

View File

@@ -1,7 +1,7 @@
package io.github.dsheirer.dsp.filter.design;
import io.github.dsheirer.dsp.filter.FilterFactory;
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
@@ -61,14 +61,15 @@ public class FilterViewer extends Application
// .passBandRipple(0.01)
// .build();
FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
.sampleRate(8000)
.stopFrequency1(1000)
.passFrequencyBegin(1100)
.passFrequencyEnd(1900)
.stopFrequency2(2000)
.stopRipple(0.000001)
.passRipple(0.00001)
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
.sampleRate(50000.0)
.gridDensity(16)
.passBandCutoff(6000)
.passBandAmplitude(1.0)
.passBandRipple(0.01)
.stopBandStart(6700)
.stopBandAmplitude(0.0)
.stopBandRipple(0.01)
.build();
float[] taps = null;
@@ -81,37 +82,25 @@ public class FilterViewer extends Application
// catch(FilterDesignException fde)
// {
// mLog.error("Error");
// }
// try
// {
// RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
//
// if(designer.isValid())
// {
// taps = designer.getImpulseResponse();
// }
// }
// catch(FilterDesignException fde)
// {
// mLog.error("Filter design error", fde);
// }
//
// if(taps == null)
// {
// throw new IllegalStateException("Couldn't design filter");
// }
try
{
// taps = FilterFactory.getSincM2Synthesizer(0.25, 2, 9);
// taps = FilterFactory.getSincM2Channelizer(12500.0, 2, 9, true);
taps = FilterFactory.getSincM2Synthesizer( 25000.0, 12500.0, 2, 9);
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
if(designer.isValid())
{
taps = designer.getImpulseResponse();
}
}
catch(FilterDesignException e)
catch(FilterDesignException fde)
{
e.printStackTrace();
mLog.error("Filter design error", fde);
}
if(taps == null)
{
throw new IllegalStateException("Couldn't design filter");
}
return taps;

View File

@@ -95,10 +95,45 @@ public class DQPSKDecisionDirectedSymbolEvaluator implements IPSKSymbolEvaluator
//Since we've rotated the error symbol back to 0 radians, the quadrature value closely approximates the
//arctan of the error angle relative to 0 radians and this provides our error value
mPhaseError = -mEvaluationSymbol.quadrature();
float errorNormalized = normalize(mEvaluationSymbol.quadrature(), 0.3f);
mPhaseError = -errorNormalized;
//Timing error is the same as phase error with the sign corrected according to the vector's angular rotation
mTimingError = mEvaluationSymbol.quadrature() * mTimingErrorPolarity;
mTimingError = errorNormalized * mTimingErrorPolarity;
}
/**
* Constrains value to the range of ( -maximum <> maximum )
*/
private static float clip(float value, float maximum)
{
if(value > maximum)
{
return maximum;
}
else if(value < -maximum)
{
return -maximum;
}
return value;
}
/**
* Constrains timing error to +/- the maximum value and corrects any
* floating point invalid numbers
*/
private float normalize(float error, float maximum)
{
if(Float.isNaN(error))
{
return 0.0f;
}
else
{
return clip(error, maximum);
}
}
/**

View File

@@ -100,7 +100,7 @@ public class DQPSKGardnerSymbolEvaluator implements IPSKSymbolEvaluator<Dibit>
//Since we've rotated the error symbol back to 0 radians, the quadrature value closely approximates the
//arctan of the error angle relative to 0 radians and this provides our error value
mPhaseError = -mEvaluationSymbol.quadrature();
mPhaseError = normalize(-mEvaluationSymbol.quadrature(), 0.3f);
}
/**