mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 00:57:31 -05:00
39 - Adds P25/LSM support to the DemodulatorViewerFX application and
adds Feed forward gain support to the C4FM DQPSK demodulator which seems to improve output from the interpolating sample buffer at each symbol period. Updates the symbol timing gain for C4FM to decrease the gain a bit since the feed forward gain is providing a better interpolated symbol.
This commit is contained in:
@@ -25,7 +25,7 @@ public class DQPSKDecisionDirectedSymbolEvaluator implements IPSKSymbolEvaluator
|
||||
private static final Complex ROTATE_FROM_MINUS_45 = Complex.fromAngle(1.0 * Math.PI / 4.0);
|
||||
private static final Complex ROTATE_FROM_MINUS_135 = Complex.fromAngle(3.0 * Math.PI / 4.0);
|
||||
|
||||
private Complex mSymbolError = new Complex(0, 0);
|
||||
private Complex mEvaluationSymbol = new Complex(0, 0);
|
||||
private float mPhaseError = 0.0f;
|
||||
private float mTimingError = 0.0f;
|
||||
private float mTimingErrorPolarity = 1.0f;
|
||||
@@ -59,46 +59,46 @@ public class DQPSKDecisionDirectedSymbolEvaluator implements IPSKSymbolEvaluator
|
||||
*/
|
||||
public void setSymbol(Complex preceding, Complex current)
|
||||
{
|
||||
mSymbolError.setValues(current);
|
||||
mEvaluationSymbol.setValues(current);
|
||||
|
||||
if(mSymbolError.quadrature() > 0.0f)
|
||||
if(mEvaluationSymbol.quadrature() > 0.0f)
|
||||
{
|
||||
if(mSymbolError.inphase() > 0.0f)
|
||||
if(mEvaluationSymbol.inphase() > 0.0f)
|
||||
{
|
||||
mSymbolDecision = Dibit.D00_PLUS_1;
|
||||
mTimingErrorPolarity = (preceding.quadrature() > current.quadrature() ? 1.0f : -1.0f);
|
||||
mSymbolError.multiply(ROTATE_FROM_PLUS_45);
|
||||
mEvaluationSymbol.multiply(ROTATE_FROM_PLUS_45);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSymbolDecision = Dibit.D01_PLUS_3;
|
||||
mTimingErrorPolarity = (preceding.quadrature() < current.quadrature() ? 1.0f : -1.0f);
|
||||
mSymbolError.multiply(ROTATE_FROM_PLUS_135);
|
||||
mEvaluationSymbol.multiply(ROTATE_FROM_PLUS_135);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mSymbolError.inphase() > 0.0f)
|
||||
if(mEvaluationSymbol.inphase() > 0.0f)
|
||||
{
|
||||
mSymbolDecision = Dibit.D10_MINUS_1;
|
||||
mTimingErrorPolarity = (preceding.quadrature() > current.quadrature() ? 1.0f : -1.0f);
|
||||
mSymbolError.multiply(ROTATE_FROM_MINUS_45);
|
||||
mEvaluationSymbol.multiply(ROTATE_FROM_MINUS_45);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSymbolDecision = Dibit.D11_MINUS_3;
|
||||
mTimingErrorPolarity = (preceding.quadrature() < current.quadrature() ? 1.0f : -1.0f);
|
||||
mSymbolError.multiply(ROTATE_FROM_MINUS_135);
|
||||
mEvaluationSymbol.multiply(ROTATE_FROM_MINUS_135);
|
||||
}
|
||||
}
|
||||
|
||||
//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 = -mSymbolError.quadrature();
|
||||
mPhaseError = -mEvaluationSymbol.quadrature();
|
||||
|
||||
//Timing error is the same as phase error with the sign corrected according to the vector's angular rotation
|
||||
mTimingError = mSymbolError.quadrature() * mTimingErrorPolarity;
|
||||
mTimingError = mEvaluationSymbol.quadrature() * mTimingErrorPolarity;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,10 +20,16 @@ import io.github.dsheirer.sample.complex.Complex;
|
||||
|
||||
public class DQPSKGardnerSymbolEvaluator implements IPSKSymbolEvaluator<Dibit>
|
||||
{
|
||||
private static final Complex ROTATE_FROM_PLUS_135 = Complex.fromAngle(-3.0 * Math.PI / 4.0);
|
||||
private static final Complex ROTATE_FROM_PLUS_45 = Complex.fromAngle(-1.0 * Math.PI / 4.0);
|
||||
private static final Complex ROTATE_FROM_MINUS_45 = Complex.fromAngle(1.0 * Math.PI / 4.0);
|
||||
private static final Complex ROTATE_FROM_MINUS_135 = Complex.fromAngle(3.0 * Math.PI / 4.0);
|
||||
|
||||
private float mPhaseError = 0.0f;
|
||||
private float mTimingError = 0.0f;
|
||||
private Dibit mSymbolDecision = Dibit.D00_PLUS_1;
|
||||
private Complex mPreviousSymbol = new Complex(0, 0);
|
||||
private Complex mEvaluationSymbol = new Complex(0, 0);
|
||||
|
||||
/**
|
||||
* Differential QPSK Decision-directed symbol phase and timing error detector and symbol decision slicer.
|
||||
@@ -53,40 +59,48 @@ public class DQPSKGardnerSymbolEvaluator implements IPSKSymbolEvaluator<Dibit>
|
||||
*/
|
||||
public void setSymbols(Complex middle, Complex current)
|
||||
{
|
||||
/* Gardner timing error calculations */
|
||||
//Gardner timing error calculation
|
||||
float errorInphase = (mPreviousSymbol.inphase() - current.inphase()) * middle.inphase();
|
||||
float errorQuadrature = (mPreviousSymbol.quadrature() - current.quadrature()) * middle.quadrature();
|
||||
mTimingError = normalize(errorInphase + errorQuadrature, 1.0f);
|
||||
mTimingError = normalize(errorInphase + errorQuadrature, .3f);
|
||||
|
||||
//Calculate phase error and determine the transmitted dibit from the symbol
|
||||
if(Math.abs(current.inphase()) > Math.abs(current.quadrature()))
|
||||
//Store the current symbol to use in the next symbol calculation
|
||||
mPreviousSymbol.setValues(current);
|
||||
|
||||
//Phase error and symbol decision calculations ...
|
||||
mEvaluationSymbol.setValues(current);
|
||||
|
||||
if(mEvaluationSymbol.quadrature() > 0.0f)
|
||||
{
|
||||
if(current.inphase() > 0)
|
||||
if(mEvaluationSymbol.inphase() > 0.0f)
|
||||
{
|
||||
mSymbolDecision = Dibit.D10_MINUS_1;
|
||||
mPhaseError = -current.quadrature();
|
||||
mSymbolDecision = Dibit.D00_PLUS_1;
|
||||
mEvaluationSymbol.multiply(ROTATE_FROM_PLUS_45);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSymbolDecision = Dibit.D01_PLUS_3;
|
||||
mPhaseError = current.quadrature();
|
||||
mEvaluationSymbol.multiply(ROTATE_FROM_PLUS_135);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(current.quadrature() > 0)
|
||||
if(mEvaluationSymbol.inphase() > 0.0f)
|
||||
{
|
||||
mSymbolDecision = Dibit.D00_PLUS_1;
|
||||
mPhaseError = current.inphase();
|
||||
mSymbolDecision = Dibit.D10_MINUS_1;
|
||||
mEvaluationSymbol.multiply(ROTATE_FROM_MINUS_45);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSymbolDecision = Dibit.D11_MINUS_3;
|
||||
mPhaseError = -current.inphase();
|
||||
mEvaluationSymbol.multiply(ROTATE_FROM_MINUS_135);
|
||||
}
|
||||
}
|
||||
|
||||
mPreviousSymbol.setValues(current);
|
||||
//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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,7 +32,7 @@ public class InterpolatingSampleBuffer
|
||||
private int mTwiceSamplesPerSymbol;
|
||||
|
||||
private float mSamplingPoint;
|
||||
private float mSampleCounterGain = 1.0f;
|
||||
private float mSampleCounterGain = 0.5f;
|
||||
private float mDetectedSamplesPerSymbol;
|
||||
private float mDetectedSamplesPerSymbolGain = 0.1f * mSampleCounterGain * mSampleCounterGain;
|
||||
private float mMaximumSamplesPerSymbol;
|
||||
@@ -43,9 +43,9 @@ public class InterpolatingSampleBuffer
|
||||
/**
|
||||
* Buffer to store complex sample data and produce interpolated samples.
|
||||
* @param samplesPerSymbol
|
||||
* @param symbolTimingGain for the symbol timing error adjustments
|
||||
* @param sampleCounterGain for the symbol timing error adjustments
|
||||
*/
|
||||
public InterpolatingSampleBuffer(float samplesPerSymbol, float symbolTimingGain)
|
||||
public InterpolatingSampleBuffer(float samplesPerSymbol, float sampleCounterGain)
|
||||
{
|
||||
mSamplingPoint = samplesPerSymbol;
|
||||
mDetectedSamplesPerSymbol = samplesPerSymbol;
|
||||
@@ -54,7 +54,9 @@ public class InterpolatingSampleBuffer
|
||||
mTwiceSamplesPerSymbol = (int)Math.floor(2.0 * samplesPerSymbol);
|
||||
mDelayLineInphase = new float[2 * mTwiceSamplesPerSymbol];
|
||||
mDelayLineQuadrature = new float[2 * mTwiceSamplesPerSymbol];
|
||||
mDetectedSamplesPerSymbolGain = symbolTimingGain * mSampleCounterGain * mSampleCounterGain;
|
||||
|
||||
mSampleCounterGain = sampleCounterGain;
|
||||
mDetectedSamplesPerSymbolGain = 0.1f * mSampleCounterGain * mSampleCounterGain;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user