#2226 DMR Decoder adds an equalizer to adjust symbol constellation balance/skew and gain to optimize symbol quadrant alignment/mapping. Reduces sync detectors from 3 to 2 and sets minimum objective channel sample rate to 19.2 kHz. (#2245)

Co-authored-by: denny <dennis.sheirer@gmail.com>
This commit is contained in:
Denny Sheirer
2025-06-24 04:38:10 -04:00
committed by GitHub
parent 189173e289
commit 15b7923a4f
27 changed files with 948 additions and 325 deletions

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -42,6 +42,7 @@ public class DecimationFilterFactory
switch(decimationRate)
{
case 0:
case 1:
return new RealDecimateX0Filter();
case 2:
return new RealDecimateX2Filter();

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -20,13 +20,10 @@
package io.github.dsheirer.dsp.filter.interpolator;
/**
* Linear interpolator for values in the range: -PI > 0 > PI with awareness of phase wrapping across the
* (+/-) PI boundary.
* Linear interpolator.
*/
public class PhaseAwareLinearInterpolator
public class LinearInterpolator
{
public static final float TWO_PI = (float)(Math.PI * 2.0);
/**
* Calculates an interpolated value between x1 and x2 at a linear position mu between 0.0 and 1.0
* @param x1 first value
@@ -36,39 +33,16 @@ public class PhaseAwareLinearInterpolator
*/
public static float calculate(float x1, float x2, float mu)
{
//Detect phase wrap at the +PI/-PI boundary ... ignore opposite side of the unit circle at 0 axis
if(x1 * x2 < -Math.PI)
if(mu < 0 || mu > 1)
{
if(x1 > 0)
{
float value = x1 + ((TWO_PI + x2 - x1) * mu);
if(value > Math.PI)
{
return -TWO_PI + value;
}
else
{
return value;
}
}
else
{
float value = x1 + ((-TWO_PI + x2 - x1) * mu);
if(value < -Math.PI)
{
return TWO_PI + value;
}
else
{
return value;
}
}
}
else
{
return x1 + ((x2 - x1) * mu);
throw new IllegalArgumentException("mu [" + mu + "] must be between 0 and 1");
}
return x1 + ((x2 - x1) * mu);
}
public static float calculate(float x1, float x2, double mu)
{
return calculate(x1, x2, (float)mu);
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -17,16 +17,16 @@
* ****************************************************************************
*/
package io.github.dsheirer.dsp.psk.dqpsk;
package io.github.dsheirer.dsp.psk.demod;
import io.github.dsheirer.dsp.filter.interpolator.Interpolator;
import io.github.dsheirer.dsp.filter.interpolator.InterpolatorFactory;
import io.github.dsheirer.dsp.fm.IDemodulator;
/**
* DQPSK demodulator base class
* Differential demodulator base class
*/
public abstract class DQPSKDemodulator implements IDemodulator
public abstract class DifferentialDemodulator implements IDemodulator
{
private int mSymbolRate;
private float mSampleRate;
@@ -43,7 +43,7 @@ public abstract class DQPSKDemodulator implements IDemodulator
* @param sampleRate in Hertz
* @param symbolRate symbols per second
*/
public DQPSKDemodulator(double sampleRate, int symbolRate)
public DifferentialDemodulator(double sampleRate, int symbolRate)
{
mSampleRate = (float) sampleRate;
mSymbolRate = symbolRate;
@@ -51,6 +51,12 @@ public abstract class DQPSKDemodulator implements IDemodulator
mMu = mSamplesPerSymbol % 1; //Fractional part of the samples per symbol rate
mInterpolationOffset = (int) Math.floor(mSamplesPerSymbol) - 4; //Interpolate at the middle of 8x samples
mBufferOverlap = (int) Math.floor(mSamplesPerSymbol) + 4;
while(mInterpolationOffset < 0)
{
mInterpolationOffset++;
mBufferOverlap++;
}
}
/**

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -17,7 +17,7 @@
* ****************************************************************************
*/
package io.github.dsheirer.dsp.psk.dqpsk;
package io.github.dsheirer.dsp.psk.demod;
import io.github.dsheirer.vector.calibrate.CalibrationManager;
import io.github.dsheirer.vector.calibrate.CalibrationType;
@@ -26,11 +26,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Factory for creating DQPSK demodulators
* Factory for creating PSK differential demodulators
*/
public class DQPSKDemodulatorFactory
public class DifferentialDemodulatorFactory
{
private static final Logger mLog = LoggerFactory.getLogger(DQPSKDemodulatorFactory.class);
private static final Logger mLog = LoggerFactory.getLogger(DifferentialDemodulatorFactory.class);
/**
* Creates the optimal demodulator using calibration data to select the optimal implementation from scalar and
@@ -39,17 +39,17 @@ public class DQPSKDemodulatorFactory
* @param symbolRate in Hertz
* @return demodulator instance
*/
public static DQPSKDemodulator getDemodulator(double sampleRate, int symbolRate)
public static DifferentialDemodulator getDemodulator(double sampleRate, int symbolRate)
{
Implementation implementation = CalibrationManager.getInstance().getImplementation(CalibrationType.DQPSK_DEMODULATOR);
Implementation implementation = CalibrationManager.getInstance().getImplementation(CalibrationType.DIFFERENTIAL_DEMODULATOR);
return switch(implementation)
{
case VECTOR_SIMD_64 -> new DQPSKDemodulatorVector64(sampleRate, symbolRate);
case VECTOR_SIMD_128 -> new DQPSKDemodulatorVector128(sampleRate, symbolRate);
case VECTOR_SIMD_256 -> new DQPSKDemodulatorVector256(sampleRate, symbolRate);
case VECTOR_SIMD_512 -> new DQPSKDemodulatorVector512(sampleRate, symbolRate);
default -> new DQPSKDemodulatorScalar(sampleRate, symbolRate); //Includes SCALAR case
case VECTOR_SIMD_64 -> new DifferentialDemodulatorVector64(sampleRate, symbolRate);
case VECTOR_SIMD_128 -> new DifferentialDemodulatorVector128(sampleRate, symbolRate);
case VECTOR_SIMD_256 -> new DifferentialDemodulatorVector256(sampleRate, symbolRate);
case VECTOR_SIMD_512 -> new DifferentialDemodulatorVector512(sampleRate, symbolRate);
default -> new DifferentialDemodulatorScalar(sampleRate, symbolRate); //Includes SCALAR case
};
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -17,25 +17,25 @@
* ****************************************************************************
*/
package io.github.dsheirer.dsp.psk.dqpsk;
package io.github.dsheirer.dsp.psk.demod;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DQPSK demodulator that uses scalar calculations for demodulating the sample stream.
* Differential demodulator that uses scalar calculations for demodulating the sample stream.
*/
public class DQPSKDemodulatorScalar extends DQPSKDemodulator
public class DifferentialDemodulatorScalar extends DifferentialDemodulator
{
private static final Logger LOGGER = LoggerFactory.getLogger(DQPSKDemodulatorScalar.class);
private static final Logger LOGGER = LoggerFactory.getLogger(DifferentialDemodulatorScalar.class);
/**
* Constructor
*
* @param symbolRate symbols per second
*/
public DQPSKDemodulatorScalar(double sampleRate, int symbolRate)
public DifferentialDemodulatorScalar(double sampleRate, int symbolRate)
{
super(sampleRate, symbolRate);
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -17,7 +17,7 @@
* ****************************************************************************
*/
package io.github.dsheirer.dsp.psk.dqpsk;
package io.github.dsheirer.dsp.psk.demod;
import java.util.Arrays;
import jdk.incubator.vector.FloatVector;
@@ -27,11 +27,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DQPSK demodulator that uses Vector SIMD 128 calculations for demodulating the sample stream.
* Differential demodulator that uses Vector SIMD 128 calculations for demodulating the sample stream.
*/
public class DQPSKDemodulatorVector128 extends DQPSKDemodulator
public class DifferentialDemodulatorVector128 extends DifferentialDemodulator
{
private static final Logger LOGGER = LoggerFactory.getLogger(DQPSKDemodulatorVector128.class);
private static final Logger LOGGER = LoggerFactory.getLogger(DifferentialDemodulatorVector128.class);
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
/**
@@ -40,7 +40,7 @@ public class DQPSKDemodulatorVector128 extends DQPSKDemodulator
* @param sampleRate in Hertz
* @param symbolRate symbols per second
*/
public DQPSKDemodulatorVector128(double sampleRate, int symbolRate)
public DifferentialDemodulatorVector128(double sampleRate, int symbolRate)
{
super(sampleRate, symbolRate);
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -17,7 +17,7 @@
* ****************************************************************************
*/
package io.github.dsheirer.dsp.psk.dqpsk;
package io.github.dsheirer.dsp.psk.demod;
import java.util.Arrays;
import jdk.incubator.vector.FloatVector;
@@ -25,9 +25,9 @@ import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* DQPSK demodulator that uses Vector SIMD calculations for demodulating the sample stream.
* Differential demodulator that uses Vector SIMD calculations for demodulating the sample stream.
*/
public class DQPSKDemodulatorVector256 extends DQPSKDemodulator
public class DifferentialDemodulatorVector256 extends DifferentialDemodulator
{
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_256;
@@ -37,7 +37,7 @@ public class DQPSKDemodulatorVector256 extends DQPSKDemodulator
* @param sampleRate in Hertz
* @param symbolRate symbols per second
*/
public DQPSKDemodulatorVector256(double sampleRate, int symbolRate)
public DifferentialDemodulatorVector256(double sampleRate, int symbolRate)
{
super(sampleRate, symbolRate);
}
@@ -68,7 +68,6 @@ public class DQPSKDemodulatorVector256 extends DQPSKDemodulator
//Append new samples to the residual samples from the previous buffer.
System.arraycopy(i, 0, mIBuffer, bufferOverlap, sampleLength);
System.arraycopy(q, 0, mQBuffer, bufferOverlap, sampleLength);
//mIDecoded, mQDecoded and mPhase will be filled below during the decoding process.
float[] interpolatedI = new float[VECTOR_SPECIES.length()];
float[] interpolatedQ = new float[VECTOR_SPECIES.length()];

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -17,7 +17,7 @@
* ****************************************************************************
*/
package io.github.dsheirer.dsp.psk.dqpsk;
package io.github.dsheirer.dsp.psk.demod;
import java.util.Arrays;
import jdk.incubator.vector.FloatVector;
@@ -27,11 +27,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DQPSK demodulator that uses Vector SIMD 512 calculations for demodulating the sample stream.
* Differential demodulator that uses Vector SIMD 512 calculations for demodulating the sample stream.
*/
public class DQPSKDemodulatorVector512 extends DQPSKDemodulator
public class DifferentialDemodulatorVector512 extends DifferentialDemodulator
{
private static final Logger LOGGER = LoggerFactory.getLogger(DQPSKDemodulatorVector512.class);
private static final Logger LOGGER = LoggerFactory.getLogger(DifferentialDemodulatorVector512.class);
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_512;
/**
@@ -39,7 +39,7 @@ public class DQPSKDemodulatorVector512 extends DQPSKDemodulator
*
* @param symbolRate symbols per second
*/
public DQPSKDemodulatorVector512(double sampleRate, int symbolRate)
public DifferentialDemodulatorVector512(double sampleRate, int symbolRate)
{
super(sampleRate, symbolRate);
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -17,7 +17,7 @@
* ****************************************************************************
*/
package io.github.dsheirer.dsp.psk.dqpsk;
package io.github.dsheirer.dsp.psk.demod;
import java.util.Arrays;
import jdk.incubator.vector.FloatVector;
@@ -27,11 +27,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DQPSK demodulator that uses Vector SIMD 64 calculations for demodulating the sample stream.
* Differential demodulator that uses Vector SIMD 64 calculations for demodulating the sample stream.
*/
public class DQPSKDemodulatorVector64 extends DQPSKDemodulator
public class DifferentialDemodulatorVector64 extends DifferentialDemodulator
{
private static final Logger LOGGER = LoggerFactory.getLogger(DQPSKDemodulatorVector64.class);
private static final Logger LOGGER = LoggerFactory.getLogger(DifferentialDemodulatorVector64.class);
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_64;
/**
@@ -40,7 +40,7 @@ public class DQPSKDemodulatorVector64 extends DQPSKDemodulator
* @param sampleRate in Hertz
* @param symbolRate symbols per second
*/
public DQPSKDemodulatorVector64(double sampleRate, int symbolRate)
public DifferentialDemodulatorVector64(double sampleRate, int symbolRate)
{
super(sampleRate, symbolRate);
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
* Copyright (C) 2014-2025 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
@@ -21,10 +21,10 @@ package io.github.dsheirer.dsp.symbol;
public enum Dibit
{
D01_PLUS_3(false, true, 1, 4, 1, (float)(Math.PI / 4 * 3), (float)-Math.sin(Math.toRadians(45)), (float)Math.sin(Math.toRadians(45))),
D00_PLUS_1(false, false, 0, 0, 0, (float)(Math.PI / 4.0), (float)Math.sin(Math.toRadians(45)), (float)Math.sin(Math.toRadians(45))),
D10_MINUS_1(true, false, 2, 8, 2, (float)(Math.PI / -4.0), (float)Math.sin(Math.toRadians(45)), (float)-Math.sin(Math.toRadians(45))),
D11_MINUS_3(true, true, 3, 12, 3, (float)(Math.PI / 4 * -3), (float)-Math.sin(Math.toRadians(45)), (float)-Math.sin(Math.toRadians(45)));
D01_PLUS_3(false, true, 1, 4, 1, (float)((Math.PI / 4) * 3)),
D00_PLUS_1(false, false, 0, 0, 0, (float)(Math.PI / 4)),
D10_MINUS_1(true, false, 2, 8, 2, (float)(Math.PI / -4)),
D11_MINUS_3(true, true, 3, 12, 3, (float)((Math.PI / 4) * -3));
private boolean mBit1;
private boolean mBit2;
@@ -32,8 +32,6 @@ public enum Dibit
private int mHighValue;
private int mValue;
private float mPhase;
private float mIdealI;
private float mIdealQ;
/**
* Dibit constructor.
@@ -43,10 +41,8 @@ public enum Dibit
* @param highValue integer value of the high order bit
* @param value integer value combined of both bits.
* @param phase optimal phase value for this symbol when transmitted.
* @param idealI ideal inphase value for this symbol when transmitted.
* @param idealQ ideal quadrature value for this symbol when transmitted.
*/
Dibit(boolean bit1, boolean bit2, int lowValue, int highValue, int value, float phase, float idealI, float idealQ)
Dibit(boolean bit1, boolean bit2, int lowValue, int highValue, int value, float phase)
{
mBit1 = bit1;
mBit2 = bit2;
@@ -54,8 +50,6 @@ public enum Dibit
mHighValue = highValue;
mValue = value;
mPhase = phase;
mIdealI = idealI;
mIdealQ = idealQ;
}
/**
@@ -75,24 +69,6 @@ public enum Dibit
return mPhase;
}
/**
* Ideal inphase value when this symbol is transmitted.
* @return ideal I
*/
public float getIdealI()
{
return mIdealI;
}
/**
* Ideal quadrature value when this symbol is transmitted.
* @return ideal Q
*/
public float getIdealQ()
{
return mIdealQ;
}
/**
* Allowable transition state 1 for trellis coded modulation. Indicates
* the dibit that differs from this dibit in the LSB bit position.