mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
330 WIP - P25 C4FM decoder fully functional using DQPSK decoder and
new symbol timing error detector. Each component now has a subclass that supports instrumentation for use with the DemodulatorViewerFX application.
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 io.github.dsheirer.dsp.psk;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.interpolator.RealInterpolator;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
|
||||
public class InterpolatingSampleBuffer
|
||||
{
|
||||
private static final float MAXIMUM_DEVIATION_SAMPLES_PER_SYMBOL = 0.02f; // +/- 2% deviation
|
||||
|
||||
private Complex mPrecedingSample = new Complex(0,0);
|
||||
private Complex mCurrentSample = new Complex(0,0);
|
||||
|
||||
private float[] mDelayLineInphase;
|
||||
private float[] mDelayLineQuadrature;
|
||||
private int mDelayLinePointer = 0;
|
||||
private int mTwiceSamplesPerSymbol;
|
||||
|
||||
private float mSamplingPoint;
|
||||
private float mSampleCounterGain = 0.05f;
|
||||
private float mDetectedSamplesPerSymbol;
|
||||
private float mDetectedSamplesPerSymbolGain = 0.1f * mSampleCounterGain * mSampleCounterGain;
|
||||
private float mMaximumSamplesPerSymbol;
|
||||
private float mMinimumSamplesPerSymbol;
|
||||
|
||||
private RealInterpolator mInterpolator = new RealInterpolator(1.0f);
|
||||
|
||||
/**
|
||||
* Buffer to store complex sample data and produce interpolated samples.
|
||||
* @param samplesPerSymbol
|
||||
*/
|
||||
public InterpolatingSampleBuffer(float samplesPerSymbol)
|
||||
{
|
||||
mSamplingPoint = samplesPerSymbol;
|
||||
mDetectedSamplesPerSymbol = samplesPerSymbol;
|
||||
mMaximumSamplesPerSymbol = samplesPerSymbol * (1.0f + MAXIMUM_DEVIATION_SAMPLES_PER_SYMBOL);
|
||||
mMinimumSamplesPerSymbol = samplesPerSymbol * (1.0f - MAXIMUM_DEVIATION_SAMPLES_PER_SYMBOL);
|
||||
mTwiceSamplesPerSymbol = (int)Math.floor(2.0 * samplesPerSymbol);
|
||||
mDelayLineInphase = new float[2 * mTwiceSamplesPerSymbol];
|
||||
mDelayLineQuadrature = new float[2 * mTwiceSamplesPerSymbol];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the sample in the buffer and updates pointers.
|
||||
* @param sample
|
||||
*/
|
||||
public void receive(Complex sample)
|
||||
{
|
||||
mSamplingPoint--;
|
||||
|
||||
//Fill up the delay line to use with the interpolator
|
||||
mDelayLineInphase[mDelayLinePointer] = sample.inphase();
|
||||
mDelayLineInphase[mDelayLinePointer + mTwiceSamplesPerSymbol] = sample.inphase();
|
||||
mDelayLineQuadrature[mDelayLinePointer] = sample.quadrature();
|
||||
mDelayLineQuadrature[mDelayLinePointer + mTwiceSamplesPerSymbol] = sample.quadrature();
|
||||
|
||||
//Increment pointer and keep pointer in bounds
|
||||
mDelayLinePointer++;
|
||||
mDelayLinePointer = mDelayLinePointer % mTwiceSamplesPerSymbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the internal sample counter with additional samples to add to the symbol sample counter.
|
||||
* @param samplesToAdd to the sample counter
|
||||
*/
|
||||
public void increaseSampleCounter(float samplesToAdd)
|
||||
{
|
||||
mSamplingPoint += samplesToAdd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts samples per symbol and symbol timing counters and increments the sample counter to collect another
|
||||
* symbol.
|
||||
*
|
||||
* @param symbolTimingError from a symbol timing error detector
|
||||
*/
|
||||
public void resetAndAdjust(float symbolTimingError)
|
||||
{
|
||||
//Adjust detected samples per symbol based on timing error
|
||||
mDetectedSamplesPerSymbol = mDetectedSamplesPerSymbol + (symbolTimingError * mDetectedSamplesPerSymbolGain);
|
||||
|
||||
//Constrain detected samples per symbol to min/max values
|
||||
if(mDetectedSamplesPerSymbol > mMaximumSamplesPerSymbol)
|
||||
{
|
||||
mDetectedSamplesPerSymbol = mMaximumSamplesPerSymbol;
|
||||
}
|
||||
|
||||
if(mDetectedSamplesPerSymbol < mMinimumSamplesPerSymbol)
|
||||
{
|
||||
mDetectedSamplesPerSymbol = mMinimumSamplesPerSymbol;
|
||||
}
|
||||
|
||||
//Add another symbol's worth of samples to the counter and adjust timing based on gardner error
|
||||
increaseSampleCounter((mDetectedSamplesPerSymbol + (symbolTimingError * mSampleCounterGain)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Current value of the detected samples per symbol
|
||||
*/
|
||||
public float getSamplingPoint()
|
||||
{
|
||||
return mSamplingPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this buffer has accumulated a enough samples to represent a full symbol
|
||||
*/
|
||||
public boolean hasSymbol()
|
||||
{
|
||||
return mSamplingPoint < 1.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-interpolated sample that precedes the current interpolated sampling point. The current sampling point falls
|
||||
* somewhere between sample index 3 and index 4, therefore the preceding sample is located at index 3.
|
||||
*/
|
||||
public Complex getPrecedingSample()
|
||||
{
|
||||
mPrecedingSample.setValues(mDelayLineInphase[mDelayLinePointer + 3], mDelayLineQuadrature[mDelayLinePointer + 3]);
|
||||
return mPrecedingSample;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolated middle sample for the symbol. Note: this method should only be invoked after testing for a
|
||||
* complete symbol with the hasSymbol() method.
|
||||
*/
|
||||
public Complex getCurrentSample()
|
||||
{
|
||||
/* Calculate interpolated current sample */
|
||||
mCurrentSample.setValues(getInphase(mSamplingPoint), getQuadrature(mSamplingPoint));
|
||||
return mCurrentSample;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interpolated inphase value for the specified offset
|
||||
* @param interpolation into the buffer to calculate the interpolated sample
|
||||
* @return inphase value of the interpolated sample
|
||||
*/
|
||||
public float getInphase(float interpolation)
|
||||
{
|
||||
return mInterpolator.filter(mDelayLineInphase, mDelayLinePointer, interpolation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interpolated quadrature value for the specified offset
|
||||
* @param interpolation into the buffer to calculate the interpolated sample
|
||||
* @return quadrature value of the interpolated sample
|
||||
*/
|
||||
public float getQuadrature(float interpolation)
|
||||
{
|
||||
return mInterpolator.filter(mDelayLineQuadrature, mDelayLinePointer, interpolation);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user