[WIP] Creates a new AFSK-1200 correlation decoder and updates the

Fleetsync II decoder to use it.  Adds instrumented version of the decoder
and a new Fleetsync2 FX pane to the DemodulatorViewerFX tool.

Note: the remaining AFSK-1200 based decoders will all be ported to use
this new AFSK1200 decoder (MDC1200, Tait1200, LowJack1200).
This commit is contained in:
Denny
2018-06-09 08:17:18 -04:00
parent a5c8552937
commit 9dda1f95a1
14 changed files with 547 additions and 207 deletions

View File

@@ -13,10 +13,9 @@
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.dsp.fsk;
package io.github.dsheirer.dsp.afsk;
import io.github.dsheirer.bits.MessageFramer;
import io.github.dsheirer.buffer.BooleanAveragingBuffer;
import io.github.dsheirer.buffer.FloatAveragingBuffer;
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
@@ -25,16 +24,21 @@ import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
import io.github.dsheirer.dsp.filter.resample.RealResampler;
import io.github.dsheirer.dsp.mixer.IOscillator;
import io.github.dsheirer.dsp.mixer.Oscillator;
import io.github.dsheirer.dsp.symbol.ISyncDetectListener;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Binary Frequency Shift Keying (2FSK) correlation decoder. Provides normal or inverted decoded output.
* Audio Frequency Shift Keying (AFSK) 1200-baud correlation decoder for decoding 8 kHz FM-demodulated audio samples.
*
* Note: internally the incoming 8 kHz sample rate is resampled to 7200 Hz to be an integral of the 1200 Hz baud rate
* which equates to six samples per symbol. Each symbol is correlated over an eight sample period with correlation
* values averaged over a seven correlation value period.
*
* Provides normal or inverted decoded output.
*/
public class AFSK1200Decoder implements Listener<ReusableBuffer>, ISyncDetectListener, ISyncStateListener
public class AFSK1200Decoder implements Listener<ReusableBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(AFSK1200Decoder.class);
@@ -43,11 +47,15 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>, ISyncDetectLis
NORMAL, INVERTED
}
public static final double TARGET_SAMPLE_RATE = 7200.0d;
public static final int SAMPLES_PER_SYMBOL = 6; //TARGET_SAMPLE_RATE / 1200;
public static final float COARSE_TIMING_GAIN = 1.0f / 3.0f;
protected static final float MEDIUM_TIMING_GAIN = 1.0f / 4.0f;
protected static final float FINE_TIMING_GAIN = 1.0f / 5.0f;
public static final double SAMPLE_RATE = 7200.0d;
public static final int SAMPLES_PER_SYMBOL = 6; //SAMPLE_RATE / 1200 baud;
//Correlation period of 8 samples works well and should align nicely with SIMD intrinsic vector sizes
public static final int CORRELATION_PERIOD = SAMPLES_PER_SYMBOL + 2;
public static final int AVERAGING_PERIOD = SAMPLES_PER_SYMBOL + 1;
public static final double MARK = 1200.0;
public static final double SPACE = 1800.0;
public static final float TIMING_ERROR_GAIN = 1.0f / 3.0f; //Timing error adjustments over 3 symbol periods
private static float[] sBandPassFilterCoefficients;
@@ -78,38 +86,47 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>, ISyncDetectLis
}
}
private Correlator mCorrelator1200 = new Correlator(TARGET_SAMPLE_RATE, 1200.0, SAMPLES_PER_SYMBOL);
private Correlator mCorrelator1800 = new Correlator(TARGET_SAMPLE_RATE, 1800.0, SAMPLES_PER_SYMBOL);
private Correlator mCorrelator1200 = new Correlator(SAMPLE_RATE, MARK, AVERAGING_PERIOD, CORRELATION_PERIOD);
private Correlator mCorrelator1800 = new Correlator(SAMPLE_RATE, SPACE, AVERAGING_PERIOD, CORRELATION_PERIOD);
private float[] mCorrelationValues1200;
private float[] mCorrelationValues1800;
protected boolean mNormalOutput;
protected float mSymbolTimingGain = COARSE_TIMING_GAIN;
protected SampleBuffer mSampleBuffer;
protected ZeroCrossingErrorDetector mTimingErrorDetector = new ZeroCrossingErrorDetector(SAMPLES_PER_SYMBOL);
protected SynchronizationMonitor mSynchronizationMonitor;
protected float mSymbolTimingGain = TIMING_ERROR_GAIN;
protected AFSKSampleBuffer mSampleBuffer;
protected AFSKTimingErrorDetector mTimingErrorDetector = new AFSKTimingErrorDetector(SAMPLES_PER_SYMBOL);
private RealFIRFilter2 mBandPassFilter = new RealFIRFilter2(sBandPassFilterCoefficients);
protected MessageFramer mMessageFramer;
private boolean mSampleDecision;
//Resample to an integral of the baud rate 1200 baud * 6 samples per symbol = 7200.0 Hertz
private RealResampler mResampler = new RealResampler(8000.0, TARGET_SAMPLE_RATE, 2000, 1);
private RealResampler mResampler = new RealResampler(8000.0, SAMPLE_RATE, 2000, 1);
public AFSK1200Decoder(int messageLength, SampleBuffer sampleBuffer, ZeroCrossingErrorDetector detector, boolean invertOutput)
/**
* Constructs a decoder using the provided arguments.
*
* @param sampleBuffer for storing incoming samples and calculating symbols
* @param detector for timing error alignment
* @param output NORMAL: 1200Hz = Mark(1) and 1800Hz = Space(0), or INVERTED (vice-versa)
*/
public AFSK1200Decoder(AFSKSampleBuffer sampleBuffer, AFSKTimingErrorDetector detector, Output output)
{
mSynchronizationMonitor = new SynchronizationMonitor(messageLength);
mSynchronizationMonitor.setListener(this);
mTimingErrorDetector = detector;
mSampleBuffer = sampleBuffer;
mSampleBuffer.setTimingGain(mSymbolTimingGain);
mResampler.setListener(new Decoder(SAMPLES_PER_SYMBOL));
mNormalOutput = !invertOutput;
mResampler.setListener(new Decoder());
mNormalOutput = (output == Output.NORMAL);
}
public AFSK1200Decoder(int messageLength, boolean invertOutput)
/**
* Constructs a decoder using the provided arguments.
*
* @param output NORMAL: 1200Hz = Mark(1) and 1800Hz = Space(0), or INVERTED (vice-versa)
*/
public AFSK1200Decoder(Output output)
{
this(messageLength, new SampleBuffer(SAMPLES_PER_SYMBOL, COARSE_TIMING_GAIN),
new ZeroCrossingErrorDetector(SAMPLES_PER_SYMBOL), invertOutput);
this(new AFSKSampleBuffer(SAMPLES_PER_SYMBOL, TIMING_ERROR_GAIN),
new AFSKTimingErrorDetector(SAMPLES_PER_SYMBOL), output);
}
/**
@@ -119,19 +136,6 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>, ISyncDetectLis
{
}
/**
* Implements the ISyncDetectedListener interface to be notified of message sync detection events.
*
* This allows the internal timing error detector to adjust symbol timing error gain levels according to the
* message synchronization state to quickly adjust to initial signal streams or to reduce gain levels once
* synchronization has been achieved.
*/
@Override
public void syncDetected()
{
mSynchronizationMonitor.syncDetected();
}
/**
* Processes the buffer samples by converting all samples to boolean values reflecting if the sample value is
* greater than zero (or not). Average symbol timing offset is calculated for the full buffer and the offset is
@@ -142,6 +146,8 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>, ISyncDetectLis
@Override
public void receive(ReusableBuffer buffer)
{
//TODO: remove the passband filter ... the correlators should be able to deal with the noise and the filters may be
//TODO: introducing noise anyway
// ReusableBuffer bandPassFiltered = mBandPassFilter.filter(buffer);
// mResampler.resample(bandPassFiltered);
mResampler.resample(buffer);
@@ -174,46 +180,8 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>, ISyncDetectLis
}
/**
* Implements the ISyncStateListener interface to receive synchronization state events and adjust the symbol timing
* error gain levels on the internal timing error detector.
*
* Gain levels are defined relative to a unit gain of 1.0 over a number of symbol periods. LTR uses an initial
* ramp-up symbol reversal pattern of 0101 which provides three zero crossing detection opportunities for the timing
* error detector, therefore we react to a COARSE gain state to average timing error over 3 symbols.
*
* @param syncState from an external synchronization state monitor
*/
@Override
public void setSyncState(SyncState syncState)
{
switch(syncState)
{
case FINE:
mSymbolTimingGain = FINE_TIMING_GAIN;
break;
case MEDIUM:
mSymbolTimingGain = MEDIUM_TIMING_GAIN;
break;
case COARSE:
mSymbolTimingGain = COARSE_TIMING_GAIN;
break;
default:
throw new IllegalArgumentException("Unrecognized sync state level: " + syncState.name());
}
mSampleBuffer.setTimingGain(mSymbolTimingGain);
}
public class Decoder implements Listener<ReusableBuffer>
{
private BooleanAveragingBuffer mSampleAveragingBuffer;
private Decoder(int samplesPerSymbol)
{
mSampleAveragingBuffer = new BooleanAveragingBuffer(samplesPerSymbol);
}
@Override
public void receive(ReusableBuffer buffer)
{
@@ -227,26 +195,30 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>, ISyncDetectLis
{
//1200 = Mark (1) and 1800 = Space (0)
mSampleDecision = mCorrelationValues1200[x] > mCorrelationValues1800[x];
// mSampleDecision = mSampleAveragingBuffer.getAverage(mSampleDecision);
mSampleBuffer.receive(mSampleDecision);
mTimingErrorDetector.receive(mSampleDecision);
if(mSampleBuffer.hasSymbol())
{
dispatch(mSampleBuffer.getSymbol());
// mSampleBuffer.resetAndAdjust(-mTimingErrorDetector.getError());
mSampleBuffer.resetAndAdjust(0.0f);
mSynchronizationMonitor.increment();
mSampleBuffer.resetAndAdjust(mTimingErrorDetector.getError());
}
}
}
}
/**
* Generates a correlation value for each FM demodulated signal against an established reference frequency,
* correlated and averaged over separate periods relative to the samples per symbol period.
* Generates a correlation value for each FM demodulated sample against samples generated for an established
* reference frequency, correlated and averaged over separate periods relative to the samples per symbol period.
*
* The sample rate of the correlator should be an integral value of the target frequency for optimal performance,
* This correlator only works across integral sample periods and does not provide intra-sample interpolation.
*
* Correlation period (ie number of samples) should be relative to the samples per symbol. You may have to
* experiment with various values of (samplesPerSymbol +/- 1, 2 or 3) to obtain the optimal decoder performance.
*
* Averaging period defines the number of correlation values to average before producing the final correlation
* value for each sample.
*/
public class Correlator
{
@@ -257,24 +229,21 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>, ISyncDetectLis
private float mCorrelationAccumulator;
/**
* Constructs a correlator instance for the specified parameters
* Constructs a correlator instance,
*
* @param sampleRate of the incoming sample stream. Note: this should be an integral of the symbol rate.
* @param frequency of the mark or space symbol to test for correlation
* @param samplesPerSymbol to determine the correlation and averaging periods
* @param averagingPeriod is the number of correlation values to average each period
* @param correlationPeriod is the number of samples to correlate each period
*/
public Correlator(double sampleRate, double frequency, int samplesPerSymbol)
public Correlator(double sampleRate, double frequency, int averagingPeriod, int correlationPeriod)
{
//Adjust correlation period and averaging period to provide optimal correlation for a given samples/symbol.
//These values are optimized for 6 samples per symbol with averaging over 7 samples and correlation over an
//8 sample period.
int correlationPeriod = samplesPerSymbol + 2;
mAveragingBuffer = new FloatAveragingBuffer(averagingPeriod);
mAveragingBuffer = new FloatAveragingBuffer(samplesPerSymbol + 1);
IOscillator referenceSignalGenerator = new Oscillator(frequency, sampleRate);
mReferenceSamples = referenceSignalGenerator.generateReal(correlationPeriod);
mDemodulatedSamples = new float[correlationPeriod];
IOscillator referenceGenerator = new Oscillator(frequency, sampleRate);
mReferenceSamples = referenceGenerator.generateReal(correlationPeriod);
}
/**
@@ -298,7 +267,7 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>, ISyncDetectLis
for(int x = 0; x < samples.length; x++)
{
//Note: we're using array copy and dot product structures that the JRE can promote to SIMD intrinsics
//when the host processor provides SIMD capabilities
//when the host processor supports SIMD instructions
System.arraycopy(mDemodulatedSamples, 1, mDemodulatedSamples, 0, mDemodulatedSamples.length - 1);
mDemodulatedSamples[mDemodulatedSamples.length - 1] = samples[x];
@@ -310,7 +279,8 @@ public class AFSK1200Decoder implements Listener<ReusableBuffer>, ISyncDetectLis
}
//Add the absolute value of correlation accumulator value to the averaging buffer and store the current
// average as the correlation value for this sample
//average as the correlation value for this sample. We use absolute value because we don't care if the
//signal is out of phase with the reference samples
mCorrelationValues[x] = mAveragingBuffer.get(Math.abs(mCorrelationAccumulator));
}

View File

@@ -13,7 +13,7 @@
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.dsp.fsk;
package io.github.dsheirer.dsp.afsk;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
@@ -22,20 +22,20 @@ public class AFSK1200DecoderInstrumented extends AFSK1200Decoder
{
private Broadcaster<Boolean> mSymbolBroadcaster = new Broadcaster<>();
public AFSK1200DecoderInstrumented(int messageLength, SampleBuffer sampleBuffer, ZeroCrossingErrorDetector detector, boolean invertOutput)
public AFSK1200DecoderInstrumented(boolean invertOutput)
{
super(messageLength, new SampleBufferInstrumented(SAMPLES_PER_SYMBOL, COARSE_TIMING_GAIN),
new ZeroCrossingErrorDetectorInstrumented(SAMPLES_PER_SYMBOL), invertOutput);
super(new AFSKSampleBufferInstrumented(SAMPLES_PER_SYMBOL, TIMING_ERROR_GAIN),
new AFSKTimingErrorDetectorInstrumented(SAMPLES_PER_SYMBOL), Output.NORMAL);
}
public SampleBufferInstrumented getSampleBuffer()
public AFSKSampleBufferInstrumented getSampleBuffer()
{
return (SampleBufferInstrumented)mSampleBuffer;
return (AFSKSampleBufferInstrumented)mSampleBuffer;
}
public ZeroCrossingErrorDetectorInstrumented getErrorDetector()
public AFSKTimingErrorDetectorInstrumented getErrorDetector()
{
return (ZeroCrossingErrorDetectorInstrumented)mTimingErrorDetector;
return (AFSKTimingErrorDetectorInstrumented)mTimingErrorDetector;
}
protected void dispatch(boolean symbol)

View File

@@ -0,0 +1,121 @@
/*******************************************************************************
* 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.afsk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AFSKSampleBuffer
{
private final static Logger mLog = LoggerFactory.getLogger(AFSKSampleBuffer.class);
private final static float SYMBOL_SAMPLE_THRESHOLD = 1.0f;
protected boolean[] mDelayLine;
protected int mDelayLinePointer = 0;
private int mHalfIntegerSamplesPerSymbol;
private int mPositiveSampleDecisionCount;
protected int mTwiceSamplesPerSymbol;
protected int mSymbolStart;
protected int mSymbolEnd;
protected int mSamplesPerSymbol;
protected float mSymbolSamplingPoint;
protected float mSymbolTimingGain;
/**
* Buffer to store complex sample data and produce interpolated samples.
* @param samplesPerSymbol
*/
public AFSKSampleBuffer(int samplesPerSymbol, float symbolTimingGain)
{
mSamplesPerSymbol = samplesPerSymbol;
mSymbolTimingGain = symbolTimingGain;
mHalfIntegerSamplesPerSymbol = samplesPerSymbol / 2;
mSymbolSamplingPoint = samplesPerSymbol;
mTwiceSamplesPerSymbol = 2 * samplesPerSymbol;
mDelayLine = new boolean[2 * mTwiceSamplesPerSymbol];
}
/**
* Stores the sample in the sample buffer to support making symbol decisions.
*
* Note: use the hasSymbol() method to determine if sufficient samples have been collected to make a symbol
* determination.
*
* @param sample to store in the sample buffer
*/
public void receive(boolean sample)
{
mSymbolSamplingPoint--;
mDelayLine[mDelayLinePointer] = sample;
mDelayLine[mDelayLinePointer + mTwiceSamplesPerSymbol] = sample;
//Increment pointer and keep pointer in bounds
mDelayLinePointer++;
mDelayLinePointer = mDelayLinePointer % mTwiceSamplesPerSymbol;
}
/**
* 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)
{
mSymbolSamplingPoint += (mSamplesPerSymbol + (symbolTimingError * mSymbolTimingGain));
}
public void setTimingGain(float gain)
{
mSymbolTimingGain = gain;
}
/**
* Indicates if this buffer has accumulated enough samples to represent a full symbol
*/
public boolean hasSymbol()
{
return mSymbolSamplingPoint < SYMBOL_SAMPLE_THRESHOLD;
}
/**
* Calculates the symbol as a majority decision where the number of positive/true samples are greater than half
* of the samples per symbol. Symbol decision is based on the samples collected over the preceeding 1.0 to 2.0
* symbol periods.
*
* @return a true symbol if the majority of the samples were true or positive, otherwise a false symbol.
*/
public boolean getSymbol()
{
mPositiveSampleDecisionCount = 0;
mSymbolStart = mDelayLinePointer;
mSymbolEnd = mDelayLinePointer + mSamplesPerSymbol - 1;
for(int x = mSymbolStart; x <= mSymbolEnd; x++)
{
if(mDelayLine[x])
{
mPositiveSampleDecisionCount++;
}
}
return mPositiveSampleDecisionCount > mHalfIntegerSamplesPerSymbol;
}
}

View File

@@ -0,0 +1,90 @@
/*******************************************************************************
* 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.afsk;
public class AFSKSampleBufferInstrumented extends AFSKSampleBuffer
{
public boolean mLastSymbol;
/**
* Buffer to store complex sample data and produce interpolated samples.
*
* @param samplesPerSymbol
* @param symbolTimingGain
*/
public AFSKSampleBufferInstrumented(int samplesPerSymbol, float symbolTimingGain)
{
super(samplesPerSymbol, symbolTimingGain);
}
/**
* Sample delay buffer containing boolean (ie positive or negative) samples.
*/
public boolean[] getDelayLine()
{
return mDelayLine;
}
/**
* Current delay buffer pointer which points to where the next sample will be stored and/or the oldest sample in
* the buffer.
*/
public int getDelayLinePointer()
{
return mDelayLinePointer;
}
public int getDelayLineSecondPointer()
{
return mDelayLinePointer + mTwiceSamplesPerSymbol;
}
/**
* Current symbol timing gain value
* @return
*/
public float getSymbolTimingGain()
{
return mSymbolTimingGain;
}
public float getSamplesPerSymbol()
{
return mSamplesPerSymbol;
}
@Override
public boolean getSymbol()
{
mLastSymbol = super.getSymbol();
return mLastSymbol;
}
public boolean getLastSymbol()
{
return mLastSymbol;
}
public int getSymbolStart()
{
return mSymbolStart;
}
public int getSymbolEnd()
{
return mSymbolEnd;
}
}

View File

@@ -13,7 +13,7 @@
* If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package io.github.dsheirer.dsp.fsk;
package io.github.dsheirer.dsp.afsk;
import io.github.dsheirer.buffer.FloatAveragingBuffer;
import io.github.dsheirer.dsp.mixer.IOscillator;

View File

@@ -0,0 +1,103 @@
/*******************************************************************************
* 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.afsk;
/**
* Zero crossing error detector. Provides symbol timing error correction values by detecting zero-crossings within
* each symbol period. Ideally there should be only 0 or 1 zero crossings within each symbol period. However, when
* there is a slight DC offset in the demodulated samples, there can be up to 2 zero crossings within a symbol
* period.
*
* This detector will only provide error corrections when there are 1 or 2 zero crossings detected. If the number
* of zero crossings is greater than 2, then it is assumed that there is no signal present and the returned timing
* error value is zero. This detector provides an error correction value relative to (approximately) the middle of
* a symbol period and therefore continuously attempts to align zero crossings to the middle of the period.
*
* Since this detector does NOT use interpolation, the error signal will contain minor levels of symbol timing
* jitter. Error signal values are reported as integrals of the sampling period.
*/
public class AFSKTimingErrorDetector
{
protected boolean[] mBuffer;
protected int mDetectedZeroCrossing;
protected float mSymbolMiddle;
private int mZeroCrossingCount;
/**
* Constructs the detector for the specified samples per symbol.
*
* @param samplesPerSymbol
*/
public AFSKTimingErrorDetector(int samplesPerSymbol)
{
mBuffer = new boolean[samplesPerSymbol];
mSymbolMiddle = (float)(samplesPerSymbol - 1) / 2.0f;
}
/**
* Stores the sample in the buffer. Use the getError() method once enough samples have been processed to determine
* the error for a symbol period.
*
* @param sample
*/
public void receive(boolean sample)
{
//Shift all samples left and store new sample at the end
System.arraycopy(mBuffer, 1, mBuffer, 0, mBuffer.length - 1);
mBuffer[mBuffer.length - 1] = sample;
}
/**
* Determines the symbol timing error for the samples processed thus far. This method should be invoked once per
* symbol period, triggered via an external samples per symbol tracking process.
*
* @return symbol timing error calculated as the distance between the approximate symbol center and the next closest
* zero crossing index. An error value of zero is returned if the number of zero crossings is less than one or
* greater than two.
*/
public int getError()
{
mZeroCrossingCount = 0;
for(int x = 0; x < mBuffer.length - 1; x++)
{
if(mBuffer[x] ^ mBuffer[x + 1])
{
mDetectedZeroCrossing = x + 1;
mZeroCrossingCount++;
if(mZeroCrossingCount > 1)
{
return 0; //Too many crossings
}
}
}
if(mZeroCrossingCount == 1)
{
if(mDetectedZeroCrossing > mSymbolMiddle)
{
return -(mBuffer.length - mDetectedZeroCrossing);
}
else
{
return mDetectedZeroCrossing + 1;
}
}
return 0;
}
}

View File

@@ -0,0 +1,53 @@
/*******************************************************************************
* 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.afsk;
import javafx.beans.property.SimpleIntegerProperty;
public class AFSKTimingErrorDetectorInstrumented extends AFSKTimingErrorDetector
{
public SimpleIntegerProperty timingError = new SimpleIntegerProperty();
/**
* Constructs the detector for the specified samples per symbol.
*
* @param samplesPerSymbol
*/
public AFSKTimingErrorDetectorInstrumented(int samplesPerSymbol)
{
super(samplesPerSymbol);
}
@Override
public int getError()
{
int error = super.getError();
timingError.setValue(error);
return error;
}
public int getDetectedZeroCrossing()
{
return mDetectedZeroCrossing;
}
public boolean[] getBuffer()
{
return mBuffer;
}
}