#2120 DMR Decoder resolves index out of bounds error and also updates Hamming 16/17 decoders to correctly identify multi-bit errors when the hamming parity bit is the syndrome. (#2121)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2025-01-01 09:51:41 -05:00
committed by GitHub
parent 70fb74b787
commit 694ea83a80
3 changed files with 50 additions and 12 deletions

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 * 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 * it under the terms of the GNU General Public License as published by
@@ -47,6 +47,13 @@ public class Hamming16 implements IHamming
return NO_ERRORS; return NO_ERRORS;
} }
//If the syndrome indicates the error is in the final parity bit position, and we already have odd parity, then
//flag it as invalid for multiple errors.
if(syndrome == 1 && message.getSubMessage(offset, offset + 16).cardinality() % 2 == 1) //check final parity bit
{
return MULTIPLE_ERRORS;
}
for(int index = 0; index < CHECKSUMS.length; index++) for(int index = 0; index < CHECKSUMS.length; index++)
{ {
if(syndrome == CHECKSUMS[index]) if(syndrome == CHECKSUMS[index])

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 * 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 * it under the terms of the GNU General Public License as published by
@@ -46,6 +46,13 @@ public class Hamming17 implements IHamming
return NO_ERRORS; return NO_ERRORS;
} }
//If the syndrome indicates the error is in the final parity bit position, and we already have odd parity, then
//flag it as invalid for multiple errors.
if(syndrome == 1 && message.getSubMessage(offset, offset + 17).cardinality() % 2 == 1) //check final parity bit
{
return MULTIPLE_ERRORS;
}
for(int index = 0; index < CHECKSUMS.length; index++) for(int index = 0; index < CHECKSUMS.length; index++)
{ {
if(syndrome == CHECKSUMS[index]) if(syndrome == CHECKSUMS[index])

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 * 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 * it under the terms of the GNU General Public License as published by
@@ -58,6 +58,7 @@ public class DMRSoftSymbolProcessor
private static final int BUFFER_LENGTH_DIBITS = BUFFER_PROTECTED_REGION_DIBITS + BUFFER_WORKSPACE_LENGTH_DIBITS; private static final int BUFFER_LENGTH_DIBITS = BUFFER_PROTECTED_REGION_DIBITS + BUFFER_WORKSPACE_LENGTH_DIBITS;
private static final float MAX_POSITIVE_SOFT_SYMBOL = Dibit.D01_PLUS_3.getIdealPhase(); private static final float MAX_POSITIVE_SOFT_SYMBOL = Dibit.D01_PLUS_3.getIdealPhase();
private static final float MAX_NEGATIVE_SOFT_SYMBOL = Dibit.D11_MINUS_3.getIdealPhase(); private static final float MAX_NEGATIVE_SOFT_SYMBOL = Dibit.D11_MINUS_3.getIdealPhase();
private static final float SAMPLES_PER_SYMBOL_ALLOWABLE_DEVIATION = 0.005f; //.5%
private static final float SYMBOL_QUADRANT_BOUNDARY = (float)(Math.PI / 2.0); private static final float SYMBOL_QUADRANT_BOUNDARY = (float)(Math.PI / 2.0);
private static final float SYNC_DETECTION_THRESHOLD = 60; private static final float SYNC_DETECTION_THRESHOLD = 60;
private DMRSyncModeMonitor mSyncModeMonitor = new DMRSyncModeMonitor(); private DMRSyncModeMonitor mSyncModeMonitor = new DMRSyncModeMonitor();
@@ -73,11 +74,14 @@ public class DMRSoftSymbolProcessor
private float mLaggingSyncOffset2; private float mLaggingSyncOffset2;
private float mObservedSamplesPerSymbol; private float mObservedSamplesPerSymbol;
private float mSamplesPerSymbol; private float mSamplesPerSymbol;
private float mMaxSamplesPerSymbol;
private float mMinSamplesPerSymbol;
private float mSamplePoint; private float mSamplePoint;
private float[] mBuffer; private float[] mBuffer;
private int mBufferLoadPointer; private int mBufferLoadPointer;
private int mBufferPointer; private int mBufferPointer;
private int mBufferWorkspaceLength; private int mBufferWorkspaceLength;
private int mBufferInterpolatorReservedRegion;
private int mSymbolsSinceLastSync = 0; private int mSymbolsSinceLastSync = 0;
/** /**
@@ -158,7 +162,7 @@ public class DMRSoftSymbolProcessor
samplesPointer += copyLength; samplesPointer += copyLength;
mBufferLoadPointer += copyLength; mBufferLoadPointer += copyLength;
while(mBufferPointer < (mBufferLoadPointer - 7)) //Interpolator needs 1 and optimizer needs 6 pad spaces while(mBufferPointer < (mBufferLoadPointer - mBufferInterpolatorReservedRegion))
{ {
mBufferPointer++; mBufferPointer++;
mSamplePoint--; mSamplePoint--;
@@ -253,7 +257,7 @@ public class DMRSoftSymbolProcessor
float offset = (mBufferPointer + mSamplePoint) + additionalOffset - (mObservedSamplesPerSymbol * 23); float offset = (mBufferPointer + mSamplePoint) + additionalOffset - (mObservedSamplesPerSymbol * 23);
//Find the optimal symbol timing //Find the optimal symbol timing
float stepSize = mObservedSamplesPerSymbol / 10.0f; float stepSize = mSyncLock ? (mObservedSamplesPerSymbol / 40.0f) : (mObservedSamplesPerSymbol / 10.0f);
float stepSizeMin = 0.03f; float stepSizeMin = 0.03f;
float adjustment = 0.0f; float adjustment = 0.0f;
float adjustmentMax = mObservedSamplesPerSymbol / 2.0f; float adjustmentMax = mObservedSamplesPerSymbol / 2.0f;
@@ -343,9 +347,18 @@ public class DMRSoftSymbolProcessor
//Adjust the observed samples per symbol using the timing error measured across one or two bursts when we're in //Adjust the observed samples per symbol using the timing error measured across one or two bursts when we're in
//fine sync mode and the timing error is not excessive. //fine sync mode and the timing error is not excessive.
if(mSyncLock && adjustment < 0.5 && mSymbolsSinceLastSync > 143 && mSymbolsSinceLastSync < 289) if(mSyncLock && Math.abs(adjustment) < 0.5 && mSymbolsSinceLastSync > 143 && mSymbolsSinceLastSync < 289)
{ {
mObservedSamplesPerSymbol += (float)((double)adjustment / (double)mSymbolsSinceLastSync * 0.2); mObservedSamplesPerSymbol += (float)((double)adjustment / (double)mSymbolsSinceLastSync * 0.2);
if(mObservedSamplesPerSymbol > mMaxSamplesPerSymbol)
{
mObservedSamplesPerSymbol = mMaxSamplesPerSymbol;
}
else if(mObservedSamplesPerSymbol < mMinSamplesPerSymbol)
{
mObservedSamplesPerSymbol = mMinSamplesPerSymbol;
}
} }
//If the timing error adjustment is high enough, resample the symbols. Otherwise, overwrite the captured sync //If the timing error adjustment is high enough, resample the symbols. Otherwise, overwrite the captured sync
@@ -402,15 +415,22 @@ public class DMRSoftSymbolProcessor
{ {
float score = 0; float score = 0;
float[] symbols = pattern.toSymbols(); float[] symbols = pattern.toSymbols();
float[] softSymbols = new float[24]; int integral, maxPointer = mBuffer.length - 1;
int integral; float softSymbol = 0.0f;
for(int x = 0; x < 24; x++) for(int x = 0; x < 24; x++)
{ {
float softSymbol = PhaseAwareLinearInterpolator.calculate(mBuffer[bufferPointer], mBuffer[bufferPointer + 1], fractional); if(bufferPointer < maxPointer)
softSymbol = Math.min(softSymbol, MAX_POSITIVE_SOFT_SYMBOL); {
softSymbol = Math.max(softSymbol, MAX_NEGATIVE_SOFT_SYMBOL); softSymbol = PhaseAwareLinearInterpolator.calculate(mBuffer[bufferPointer], mBuffer[bufferPointer + 1], fractional);
softSymbols[x] = softSymbol; softSymbol = Math.min(softSymbol, MAX_POSITIVE_SOFT_SYMBOL);
softSymbol = Math.max(softSymbol, MAX_NEGATIVE_SOFT_SYMBOL);
}
else
{
softSymbol = 0.0f;
}
score += softSymbol * symbols[x]; score += softSymbol * symbols[x];
fractional += samplesPerSymbol; fractional += samplesPerSymbol;
integral = (int)Math.floor(fractional); integral = (int)Math.floor(fractional);
@@ -428,6 +448,10 @@ public class DMRSoftSymbolProcessor
public void setSamplesPerSymbol(float samplesPerSymbol) public void setSamplesPerSymbol(float samplesPerSymbol)
{ {
mSamplesPerSymbol = samplesPerSymbol; mSamplesPerSymbol = samplesPerSymbol;
mMaxSamplesPerSymbol = samplesPerSymbol * (1.0f + SAMPLES_PER_SYMBOL_ALLOWABLE_DEVIATION);
mMinSamplesPerSymbol = samplesPerSymbol * (1.0f - SAMPLES_PER_SYMBOL_ALLOWABLE_DEVIATION);
//Protected region for score() method to avoid IndexOutOfBounds = Max Adjustment + Max Step Size for Max SPS
mBufferInterpolatorReservedRegion = (int)Math.ceil((mMaxSamplesPerSymbol / 2.0f) + (mMaxSamplesPerSymbol / 10.0f));
mObservedSamplesPerSymbol = mSamplesPerSymbol; mObservedSamplesPerSymbol = mSamplesPerSymbol;
mSamplePoint = mSamplesPerSymbol; mSamplePoint = mSamplesPerSymbol;
mLaggingSyncOffset1 = mSamplesPerSymbol / 3; mLaggingSyncOffset1 = mSamplesPerSymbol / 3;