mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-09 15:27:22 -05:00
P25p1: 1/2 Trellis Viterbi Improvements;
P25p1 and DMR: 3/4 Trellis Viterbi Improvements;
This commit is contained in:
@@ -1376,7 +1376,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
||||
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]);
|
||||
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25]);
|
||||
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18]); //simplier trellis decoder
|
||||
uint32_t viterbi_r34 (uint8_t * input, uint8_t * output);
|
||||
void beeper (dsd_opts * opts, dsd_state * state, int lr, int id, int ad, int len);
|
||||
void dmr_gateway_identifier (uint32_t source, uint32_t target); //translate special addresses
|
||||
|
||||
|
||||
476
src/dmr_34.c
476
src/dmr_34.c
@@ -1,12 +1,35 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_34.c
|
||||
* DMR (and P25) 3/4 Rate Simple Trellis Decoder
|
||||
* DMR (and P25) 3/4 Rate Viterbi Trellis Decoder (Improved)
|
||||
*
|
||||
* Code Sourced From: Unknown
|
||||
* No License Provided
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2023-12 DSD-FME Florida Man Edition
|
||||
* 2026-02 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#define INRANGE 8 // Serial input is grouped in 3 bits, each input value ranges 0~7
|
||||
#define INSTATIC 8 // Number of possible states in the state machine, range 0~7
|
||||
#define INDATA 49 // Input 144 bits, grouped into 3-bit units → outputs 48 values (0~15) + one final 0 to determine end state
|
||||
#define INSYMBOL 49 // Number of input symbols
|
||||
#define PATH 64 // For the i-th (48≥i≥2) input, there are 64 branch metrics → 64 possible paths
|
||||
#define SELECTPATH 8 // Among the 64 paths, select the 8 with smallest metrics, then choose the smallest one as final output
|
||||
#define BITELEN 9 //
|
||||
|
||||
/******************************************************************************
|
||||
* Header file includes
|
||||
*******************************************************************************/
|
||||
#include "dsd.h"
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/******************************************************************************
|
||||
* Global variable definitions
|
||||
*******************************************************************************/
|
||||
|
||||
uint8_t interleave[98] = {
|
||||
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
|
||||
@@ -14,111 +37,288 @@ uint8_t interleave[98] = {
|
||||
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
|
||||
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95};
|
||||
|
||||
//this is a convertion table for converting the dibit pairs into constellation points
|
||||
uint8_t constellation_map[16] = {
|
||||
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
|
||||
static int16_t staticX[INSTATIC][INRANGE] =
|
||||
{
|
||||
{ 1,-3,-3,1,3,-1,-1,3 },
|
||||
{ -3,1,3,-1,-1,3,1,-3 },
|
||||
{ -1,3,3,-1,-3,1,1,-3 },
|
||||
{ 3,-1,-3,1,1,-3,-1,3 },
|
||||
{ -3,1,1,-3,-1,3,3,-1 },
|
||||
{ 1,-3,-1,3,3,-1,-3,1 },
|
||||
{ 3,-1,-1,3,1,-3,-3,1 },
|
||||
{ -1,3,1,-3,-3,1,3,-1 }
|
||||
|
||||
};
|
||||
|
||||
//digitized dibit to OTA symbol conversion for reference
|
||||
//0 = +1; 1 = +3;
|
||||
//2 = -1; 3 = -3;
|
||||
|
||||
//finite state machine values
|
||||
uint8_t fsm[64] = {
|
||||
0, 8, 4, 12, 2, 10, 6, 14,
|
||||
4, 12, 2, 10, 6, 14, 0, 8,
|
||||
1, 9, 5, 13, 3, 11, 7, 15,
|
||||
5, 13, 3, 11, 7, 15, 1, 9,
|
||||
3, 11, 7, 15, 1, 9, 5, 13,
|
||||
7, 15, 1, 9, 5, 13, 3, 11,
|
||||
2, 10, 6, 14, 0, 8, 4, 12,
|
||||
6, 14, 0, 8, 4, 12, 2, 10};
|
||||
|
||||
//attempt to find the surviving path, or the 'best' path available (most positions gained)
|
||||
uint8_t fix_34(uint8_t * p, uint8_t state, int position)
|
||||
static int16_t staticY[INSTATIC][INRANGE] =
|
||||
{
|
||||
int i, j, k, counter, best_p, best_v, survivors;
|
||||
uint8_t temp_s, tri, t;
|
||||
{ -1,3,-1,3,-3,1,-3,1 },
|
||||
{ -1,3,-3,1,-3,1,-1,3 },
|
||||
{ -1,3,-1,3,-3,1,-3,1 },
|
||||
{ -1,3,-3,1,-3,1,-1,3 },
|
||||
{ -3,1,-3,1,-1,3,-1,3 },
|
||||
{ -3,1,-1,3,-1,3,-3,1 },
|
||||
{ -3,1,-3,1,-1,3,-1,3 },
|
||||
{ -3,1,-1,3,-1,3,-3,1 }
|
||||
|
||||
//status of surviving paths -- debug
|
||||
int s[8];
|
||||
memset (s, 0, 8*sizeof(int));
|
||||
};
|
||||
|
||||
//assign all potentially correct points to temporary storage
|
||||
uint8_t temp_p[8];
|
||||
temp_p[0] = (p[position] ^ 1) & 0xF;
|
||||
temp_p[1] = (p[position] ^ 3) & 0xF;
|
||||
temp_p[2] = (p[position] ^ 5) & 0xF;
|
||||
temp_p[3] = (p[position] ^ 7) & 0xF;
|
||||
temp_p[4] = (p[position] ^ 9) & 0xF;
|
||||
temp_p[5] = (p[position] ^ 11) & 0xF;
|
||||
temp_p[6] = (p[position] ^ 13) & 0xF;
|
||||
temp_p[7] = (p[position] ^ 15) & 0xF;
|
||||
|
||||
best_p = 0; //best position
|
||||
best_v = 0; //best path value
|
||||
|
||||
for (k = 0; k < 8; k++)
|
||||
{
|
||||
temp_s = state;
|
||||
counter = 0;
|
||||
tri = 0;
|
||||
for (i = position; i < 49; i++)
|
||||
{
|
||||
//assign temp t either as our temp_p, or next point of i
|
||||
if (i == position) t = temp_p[k];
|
||||
else t = p[i];
|
||||
|
||||
if (tri != 0xFF) //while the path survives
|
||||
{
|
||||
tri = 0xFF;
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
if ( fsm[(temp_s*8)+j] == t )
|
||||
{
|
||||
//return our tribit value and state for the next point
|
||||
tri = temp_s = (uint8_t)j;
|
||||
counter++;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (counter > best_p)
|
||||
{
|
||||
best_p = counter;
|
||||
best_v = k; //if we make it further on current path, assign as best path
|
||||
}
|
||||
|
||||
// surviving path -- made it to 49
|
||||
if (i == 48) s[k] = 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//NOTE: If there are no surviving paths, then that means there is another bit/point err
|
||||
//Ideally, at the end of a single or multiple fixes, there should only be one survivor, but in practice,
|
||||
//I've seen up to 4 surviving paths and somehow, get a good CRC and a good LRRP or LOCN decode
|
||||
//multiple survivors seems common when the starting err position is very close to 49
|
||||
|
||||
//tally number of surviving paths
|
||||
survivors = 0;
|
||||
for (k = 0; k < 8; k++)
|
||||
{
|
||||
if (s[k] == 1) survivors++;
|
||||
}
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, "START: %d; BEST_P: %d; BEST_V: %d; Survivors: %d; Point: %d; ", position, best_p, best_v, survivors, temp_p[best_v]);
|
||||
|
||||
return temp_p[best_v]; //return the point value of the best path's starting point value
|
||||
/**
|
||||
* @brief Convert 0~7 symbols into 3-bit (0/1) bitstream
|
||||
*
|
||||
* @Param [in] psymbol Input decimal symbol values
|
||||
* @Param [in] num Number of input symbols
|
||||
* @Param [out] pbit Output 3-bit stream
|
||||
*
|
||||
*/
|
||||
void TriBit(uint16_t *psymbol, uint16_t num, uint16_t *pbit)
|
||||
{
|
||||
int16_t temp, i;
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
temp = psymbol[i];
|
||||
*pbit++ = (temp >> 2) & 0x01;
|
||||
*pbit++ = (temp >> 1) & 0x01;
|
||||
*pbit++ = temp & 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18])
|
||||
/**
|
||||
* @brief Organize the decoded 3-bit stream into 16-bit word format for output
|
||||
*
|
||||
* @Param [in] ptriBitDecRslt Input 3-bit decoded bitstream
|
||||
* @Param [out] poutdata Output packed 16-bit words
|
||||
*
|
||||
*/
|
||||
void TriBitToShort(uint16_t *ptriBitDecRslt, uint16_t *poutdata)
|
||||
{
|
||||
int i, j;
|
||||
int16_t i, j, k = 0;
|
||||
uint16_t bitArray[144] = { 0 };
|
||||
memset(poutdata, 0, sizeof(uint16_t) * 9);
|
||||
|
||||
TriBit(ptriBitDecRslt, 48, bitArray);
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
for (j = 15; j >= 0; j--)
|
||||
{
|
||||
poutdata[i] += bitArray[k++] << j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Among 8 paths, select the one with the smallest metric and return its index
|
||||
*
|
||||
* @Param [in] pdata Array of 8 path metrics
|
||||
* @return Index of the path with minimum metric
|
||||
*
|
||||
*/
|
||||
uint16_t MinPath(uint16_t *pdata)
|
||||
{
|
||||
uint16_t i, j = 0;
|
||||
uint16_t min = pdata[0]; // Take first value as initial minimum
|
||||
|
||||
for (i = 0; i < SELECTPATH; i++)
|
||||
{
|
||||
if (pdata[i] < min) // If current value is smaller
|
||||
{
|
||||
min = pdata[i]; // Update minimum
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
return j; // Return index of minimum path
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief From 64 paths, select the 8 paths with smallest metrics,
|
||||
* and output corresponding state and input values
|
||||
*
|
||||
* @Param [in] parrary Array of 64 branch metrics
|
||||
* @Param [out] pipath_ed Sorted 8 smallest path metrics
|
||||
* @Param [out] pstate States corresponding to the 8 best paths
|
||||
* @Param [out] pdata Input values corresponding to the 8 best paths
|
||||
*
|
||||
*/
|
||||
void PathFunc(uint16_t *parrary, uint16_t *pipath_ed, uint16_t *pstate, uint16_t *pdata)
|
||||
{
|
||||
int16_t i, j, locT = 0, ValT = 0;
|
||||
uint16_t dataT[PATH] = { 0 };
|
||||
|
||||
memcpy(dataT, parrary, sizeof(uint16_t)*PATH);
|
||||
|
||||
for (i = 0; i < INRANGE; i++)
|
||||
{
|
||||
ValT = dataT[0];
|
||||
locT = 0;
|
||||
for (j = 1; j < PATH; j++)
|
||||
{
|
||||
if (dataT[j] < ValT)
|
||||
{
|
||||
ValT = dataT[j];
|
||||
locT = j;
|
||||
}
|
||||
}
|
||||
dataT[locT] = 10000;
|
||||
pipath_ed[i] = ValT;
|
||||
pstate[i] = locT / 8; // quotient → state
|
||||
pdata[i] = locT % 8; // remainder → input symbol
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Calculate path metric (Manhattan distance)
|
||||
*
|
||||
* @Param [in] dataAX X-coordinate of received constellation point A
|
||||
* @Param [in] dataBX X-coordinate of reference constellation point B
|
||||
* @Param [in] dataAY Y-coordinate of received constellation point A
|
||||
* @Param [in] dataBY Y-coordinate of reference constellation point B
|
||||
* @return Path metric result
|
||||
*
|
||||
*/
|
||||
uint16_t DistanceCal(int16_t dataAX, int16_t dataBX, int16_t dataAY, int16_t dataBY)
|
||||
{
|
||||
uint16_t DistanceRslt = 0;
|
||||
DistanceRslt = abs(dataAX - dataBX) + abs(dataAY - dataBY);
|
||||
//DistanceRslt = sqrt((dataAX - dataBX)*(dataAX - dataBX) + (dataAY - dataBY)*(dataAY - dataBY));
|
||||
return DistanceRslt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert 2-bit value to constellation symbol (±1, ±3)
|
||||
*
|
||||
* @Param [in] InBit Input 2-bit value (0~3)
|
||||
* @return Constellation symbol value
|
||||
*
|
||||
*/
|
||||
int16_t BitToSymbol(int16_t InBit)
|
||||
{
|
||||
switch (InBit)
|
||||
{
|
||||
case 0:
|
||||
return 1;
|
||||
case 1:
|
||||
return 3;
|
||||
case 2:
|
||||
return -1;
|
||||
case 3:
|
||||
return -3;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Map input symbols (0~15) to constellation points (±1, ±3) in I/Q
|
||||
*
|
||||
* @Param [in] pinstar Input symbol array (0~15)
|
||||
* @Param [in] num Number of symbols
|
||||
* @Param [out] pdataX Output I-channel (X) coordinates
|
||||
* @Param [out] pdataY Output Q-channel (Y) coordinates
|
||||
*
|
||||
*/
|
||||
void MapStar(uint16_t *pinstar, uint16_t num, int16_t *pdataX, int16_t *pdataY)
|
||||
{
|
||||
int16_t i, itemp;
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
itemp = (pinstar[i] >> 2) & 0x03;
|
||||
*pdataX++ = BitToSymbol(itemp);
|
||||
itemp = pinstar[i] & 0x03;
|
||||
*pdataY++ = BitToSymbol(itemp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Viterbi decoding function — selects the most likely sequence
|
||||
*
|
||||
* @Param [in] pindata Input symbol sequence (values 0~15)
|
||||
* @Param [out] outdata Decoded output (packed 16-bit words)
|
||||
*
|
||||
*/
|
||||
void Virterbi(uint16_t *pindata, uint16_t outdata[])
|
||||
{
|
||||
int16_t i, p, j = 1;
|
||||
uint16_t lastDistnc[INRANGE] = { 0 }; // Branch metrics for final input (8 values)
|
||||
uint16_t crntDistnc[64] = { 0 }; // All 64 possible branch metrics for current step
|
||||
uint16_t DecodeOut[INDATA][INRANGE] = { {0 }}; // Possible input sequences up to current step (8 survivors)
|
||||
uint16_t DecodeOutEnd[INDATA][INRANGE] = { {0 }}; // Temporary buffer for path history
|
||||
uint16_t exInDistnc[INRANGE] = { 0 }; // 8 survivor path metrics
|
||||
uint16_t triBitDecRslt[INDATA] = { 0 }; // Final decoded 3-bit symbols
|
||||
int16_t starX[INDATA] = { 0 }, starY[INDATA] = { 0 }; // Constellation point coordinates (I/Q)
|
||||
|
||||
uint16_t minDistnc = 0;
|
||||
uint16_t ConstelPData[INRANGE] = { 0 }; // Input symbols leading to next states
|
||||
uint16_t ConstelPState[INSTATIC] = { 0 }; // Survivor states
|
||||
|
||||
MapStar(pindata, INDATA, starX, starY); // Map input symbols to constellation points
|
||||
|
||||
// Branch metrics for the first input symbol (starting from state 0)
|
||||
for (i = 0; i < INRANGE; i++)
|
||||
{
|
||||
ConstelPData[i] = i; // Next state value
|
||||
DecodeOut[0][i] = i; // Possible input symbols from state 0
|
||||
exInDistnc[i] = DistanceCal(starX[0], staticX[0][i], starY[0], staticY[0][i]);
|
||||
}
|
||||
|
||||
// Compute branch metrics for each subsequent input symbol
|
||||
for (j = 1; j < INDATA - 1; j++) // for each input symbol
|
||||
{
|
||||
for (p = 0; p < INSTATIC; p++) // for each current survivor state
|
||||
{
|
||||
for (i = 0; i < INRANGE; i++) // for each possible input
|
||||
{
|
||||
crntDistnc[p * 8 + i] = DistanceCal(starX[j], staticX[ConstelPData[p]][i], starY[j], staticY[ConstelPData[p]][i]) + exInDistnc[p];
|
||||
}
|
||||
}
|
||||
|
||||
PathFunc(crntDistnc, exInDistnc, ConstelPState, ConstelPData); // Select 8 best paths
|
||||
|
||||
for (p = 0; p <= j; p++)
|
||||
{
|
||||
for (i = 0; i < INRANGE; i++)
|
||||
{
|
||||
if (p < j)
|
||||
{
|
||||
DecodeOutEnd[p][i] = DecodeOut[p][ConstelPState[i]];
|
||||
}
|
||||
else
|
||||
{
|
||||
DecodeOut[p][i] = ConstelPData[i];
|
||||
DecodeOutEnd[p][i] = DecodeOut[p][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
memcpy(DecodeOut, DecodeOutEnd, j * 8 * sizeof(int16_t));
|
||||
}
|
||||
|
||||
// Final step — force ending in state 0
|
||||
for (i = 0; i < INRANGE; i++)
|
||||
{
|
||||
lastDistnc[i] = DistanceCal(starX[INDATA - 1], staticX[DecodeOut[INDATA - 2][i]][0], starY[INDATA - 1], staticY[DecodeOut[INDATA - 2][i]][0]) + exInDistnc[i];
|
||||
}
|
||||
|
||||
minDistnc = MinPath(lastDistnc); // Find best final path
|
||||
|
||||
for (i = 0; i < INDATA - 1; i++)
|
||||
{
|
||||
triBitDecRslt[i] = DecodeOutEnd[i][minDistnc];
|
||||
}
|
||||
|
||||
TriBitToShort(triBitDecRslt, outdata);
|
||||
}
|
||||
|
||||
uint32_t viterbi_r34 (uint8_t * input, uint8_t * output)
|
||||
{
|
||||
|
||||
int i = 0;
|
||||
uint32_t irr_err = 0; //irrecoverable errors
|
||||
|
||||
uint8_t deinterleaved_dibits[98];
|
||||
@@ -135,80 +335,20 @@ uint32_t dmr_34(uint8_t * input, uint8_t treturn[18])
|
||||
for (i = 0; i < 49; i++)
|
||||
nibs[i] = (deinterleaved_dibits[i*2+0] << 2) | (deinterleaved_dibits[i*2+1] << 0);
|
||||
|
||||
//convert our dibit pairs into constellation point values
|
||||
uint8_t point[49];
|
||||
memset (point, 0xFF, sizeof(point));
|
||||
uint16_t pindata[49]; memset (pindata, 0, sizeof(pindata));
|
||||
uint16_t outdata[18]; memset (outdata, 0, sizeof(outdata));
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
point[i] = constellation_map[nibs[i]];
|
||||
for (int i = 0; i < 49; i++)
|
||||
pindata[i] = (uint16_t)nibs[i];
|
||||
|
||||
//debug view points
|
||||
// fprintf (stderr, "\n P =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", point[i]);
|
||||
Virterbi(pindata, outdata);
|
||||
|
||||
//convert constellation points into tribit values using the FSM
|
||||
uint8_t state = 0;
|
||||
uint32_t tribits[49];
|
||||
memset (tribits, 0xF, sizeof(tribits));
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
int k = 0;
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
if ( fsm[(state*8)+j] == point[i] )
|
||||
{
|
||||
//return our tribit value and state for the next point
|
||||
tribits[i] = state = (uint8_t)j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if tribit value is greater than 7, then we have a decoding error
|
||||
if (tribits[i] > 7)
|
||||
{
|
||||
irr_err++; //tally number of errors
|
||||
|
||||
//debug point, position of error, and state value
|
||||
// fprintf (stderr, "\n P: %d, %d:%d; ", point[i], i, state);
|
||||
|
||||
//attempt to fix point by looking for the best path from here
|
||||
point[i] = fix_34(point, state, i);
|
||||
|
||||
//Make a hard decision and flip point to fit in current state
|
||||
// point[i] ^= 7; //lucky number 7 (0111) //fallback
|
||||
|
||||
//decrement one and resume decoding
|
||||
i--;
|
||||
|
||||
}
|
||||
|
||||
output[k++] = (outdata[i] >> 8) & 0xFF;
|
||||
output[k++] = (outdata[i] >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
//debug view tribits/states
|
||||
// fprintf (stderr, "\n T =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", tribits[i]);
|
||||
|
||||
//convert tribits into a return payload
|
||||
uint32_t temp = 0;
|
||||
|
||||
//break into chunks of 24 bit values and shuffle into 8-bit (byte) treturn values
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
temp = (tribits[(i*8)+0] << 21) + (tribits[(i*8)+1] << 18) + (tribits[(i*8)+2] << 15) + (tribits[(i*8)+3] << 12) +
|
||||
(tribits[(i*8)+4] << 9) + (tribits[(i*8)+5] << 6) + (tribits[(i*8)+6] << 3) + (tribits[(i*8)+7] << 0);
|
||||
|
||||
treturn[(i*3)+0] = (temp >> 16) & 0xFF;
|
||||
treturn[(i*3)+1] = (temp >> 8) & 0xFF;
|
||||
treturn[(i*3)+2] = (temp >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
//trellis point/state err tally
|
||||
// if (irr_err != 0)
|
||||
// fprintf (stderr, " P_ERR = %d", irr_err);
|
||||
|
||||
return (0);
|
||||
return irr_err; //TODO: Need to pull viterbi metrics from upper functions
|
||||
}
|
||||
|
||||
|
||||
@@ -438,7 +438,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||
|
||||
uint8_t TrellisReturn[18];
|
||||
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
||||
IrrecoverableErrors = dmr_34(tdibits, TrellisReturn);
|
||||
IrrecoverableErrors = viterbi_r34(tdibits, TrellisReturn);
|
||||
|
||||
//NOTE: IrrecoverableErrors in this context are a tally of errors from trellis
|
||||
//they may have been successfully corrected, the CRC will reveal as much
|
||||
|
||||
186
src/p25_12.c
186
src/p25_12.c
@@ -1,22 +1,24 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* p25_12.c
|
||||
* P25p1 1/2 Rate Simple Trellis Decoder
|
||||
* P25p1 1/2 Rate Viterbi Trellis Decoder (Improved)
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2023-10 DSD-FME Florida Man Edition
|
||||
* 2026-02 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include <limits.h> // For UINT8_MAX as infinity proxy
|
||||
|
||||
uint8_t p25_interleave[98] = {
|
||||
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
|
||||
2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, 67, 74, 75, 82, 83, 90, 91,
|
||||
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
|
||||
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95};
|
||||
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
|
||||
2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, 67, 74, 75, 82, 83, 90, 91,
|
||||
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
|
||||
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95
|
||||
};
|
||||
|
||||
//this is a convertion table for converting the dibit pairs into constellation points
|
||||
uint8_t p25_constellation_map[16] = {
|
||||
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
|
||||
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
|
||||
};
|
||||
|
||||
//digitized dibit to OTA symbol conversion for reference
|
||||
@@ -25,19 +27,21 @@ uint8_t p25_constellation_map[16] = {
|
||||
|
||||
//finite state machine values
|
||||
uint8_t p25_fsm[16] = {
|
||||
0,15,12,3,
|
||||
4,11,8,7,
|
||||
13,2,1,14,
|
||||
9,6,5,10 };
|
||||
0,15,12,3,
|
||||
4,11,8,7,
|
||||
13,2,1,14,
|
||||
9,6,5,10
|
||||
};
|
||||
|
||||
//this is a dibit-pair to trellis dibit transition matrix (SDRTrunk and Ossmann)
|
||||
//when evaluating hamming distance, we want to use this xor the dibit-pair nib,
|
||||
//and not the fsm xor the constellation point
|
||||
uint8_t p25_dtm[16] = {
|
||||
2,12,1,15,
|
||||
14,0,13,3,
|
||||
9,7,10,4,
|
||||
5,11,6,8 };
|
||||
2,12,1,15,
|
||||
14,0,13,3,
|
||||
9,7,10,4,
|
||||
5,11,6,8
|
||||
};
|
||||
|
||||
int count_bits(uint8_t b, int slen)
|
||||
{
|
||||
@@ -50,37 +54,13 @@ int count_bits(uint8_t b, int slen)
|
||||
return i;
|
||||
}
|
||||
|
||||
uint8_t find_min(uint8_t list[4], int len)
|
||||
{
|
||||
int min = list[0];
|
||||
uint8_t index = 0;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < len; i++)
|
||||
{
|
||||
if (list[i] < min)
|
||||
{
|
||||
min = list[i];
|
||||
index = (uint8_t)i;
|
||||
}
|
||||
|
||||
//NOTE: Disqualifying result on uniqueness can impact decoding
|
||||
//let the CRC determine if the result is good or bad
|
||||
//its not uncommon for two values of the same min
|
||||
//distance to emerge, so its 50/50 each time
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
int p25_12(uint8_t * input, uint8_t treturn[12])
|
||||
{
|
||||
int i, j;
|
||||
int irr_err = 0;
|
||||
int i, t;
|
||||
int total_err = 0;
|
||||
|
||||
uint8_t deinterleaved_dibits[98];
|
||||
memset (deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));
|
||||
memset(deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));
|
||||
|
||||
//deinterleave our input dibits
|
||||
for (i = 0; i < 98; i++)
|
||||
@@ -88,80 +68,86 @@ int p25_12(uint8_t * input, uint8_t treturn[12])
|
||||
|
||||
//pack the input into nibbles (dibit pairs)
|
||||
uint8_t nibs[49];
|
||||
memset (nibs, 0, sizeof(nibs));
|
||||
memset(nibs, 0, sizeof(nibs));
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
nibs[i] = (deinterleaved_dibits[i*2+0] << 2) | (deinterleaved_dibits[i*2+1] << 0);
|
||||
|
||||
//convert our dibit pairs into constellation point values
|
||||
uint8_t point[49];
|
||||
memset (point, 0xFF, sizeof(point));
|
||||
memset(point, 0xFF, sizeof(point));
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
point[i] = p25_constellation_map[nibs[i]];
|
||||
|
||||
//debug view points
|
||||
// fprintf (stderr, "\n P =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", point[i]);
|
||||
// Viterbi setup: 4 states, 49 stages
|
||||
// Use UINT8_MAX / 2 as proxy for infinity (max possible errors per path ~196)
|
||||
const uint8_t INF = UINT8_MAX / 2;
|
||||
uint8_t metric[2][4]; // Ping-pong: [0] prev, [1] curr
|
||||
uint8_t path[49][4]; // path[t][s] = best prev_state to reach state s at stage t
|
||||
uint8_t tdibits[49]; // Decoded trellis dibits (states along best path)
|
||||
|
||||
//convert constellation points into tdibit values using the FSM
|
||||
uint8_t state = 0;
|
||||
uint8_t tdibits[49]; //trellis dibits 1/2 rate
|
||||
memset (tdibits, 0xF, sizeof(tdibits));
|
||||
uint8_t hd[4]; //array of the four fsm words hamming distance
|
||||
memset (hd, 0, sizeof(hd));
|
||||
uint8_t min = 0;
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
{
|
||||
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
if ( p25_fsm[(state*4)+j] == point[i] )
|
||||
{
|
||||
//return our tdibit value and state for the next point
|
||||
tdibits[i] = state = (uint8_t)j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if tdibit value is greater than 3, then we have a decoding error
|
||||
if (tdibits[i] == 0xF)
|
||||
{
|
||||
irr_err++; //increment number of errors
|
||||
|
||||
//debug position of error and state value
|
||||
// fprintf (stderr, " %d:%d;", i, state);
|
||||
|
||||
//this method only seems to be reliable up to 1-2 bit errors...sometimes
|
||||
for (j = 0; j < 4; j++)
|
||||
hd[j] = count_bits ( ((nibs[i] ^ p25_dtm[(state*4)+j]) & 0xF ), 4 );
|
||||
min = find_min (hd, 4);
|
||||
tdibits[i] = state = min;
|
||||
|
||||
memset (hd, 0, sizeof(hd));
|
||||
min = 0;
|
||||
|
||||
}
|
||||
// Initialize: Start from state 0 before first symbol, metric 0
|
||||
int curr = 0, prev = 1;
|
||||
memset(metric, 0xFF, sizeof(metric)); // Set to high value
|
||||
memset(path, 0, sizeof(path)); // Zero-init paths
|
||||
|
||||
// Stage 0 (first symbol): From initial state 0 to each possible next_s
|
||||
memset(metric[curr], INF, sizeof(metric[0]));
|
||||
for (int next_s = 0; next_s < 4; next_s++) {
|
||||
int branch = count_bits(nibs[0] ^ p25_dtm[0 * 4 + next_s], 4);
|
||||
metric[curr][next_s] = (uint8_t)branch;
|
||||
path[0][next_s] = 0; // Prev state is initial 0
|
||||
total_err += branch; // Accumulate for return (total path errors)
|
||||
}
|
||||
|
||||
//debug view tdibits/states
|
||||
// fprintf (stderr, "\n T =");
|
||||
// for (i = 0; i < 49; i++)
|
||||
// fprintf (stderr, " %02d", tdibits[i]);
|
||||
// Stages 1 to 48
|
||||
for (t = 1; t < 49; t++) {
|
||||
// Swap buffers
|
||||
prev = curr;
|
||||
curr = 1 - curr;
|
||||
memset(metric[curr], INF, sizeof(metric[0]));
|
||||
|
||||
//pack tdibits into return payload bytes
|
||||
for (int next_s = 0; next_s < 4; next_s++) {
|
||||
uint8_t min_dist = INF;
|
||||
uint8_t best_prev = 0;
|
||||
for (int prev_s = 0; prev_s < 4; prev_s++) {
|
||||
if (metric[prev][prev_s] == INF) continue;
|
||||
int branch = count_bits(nibs[t] ^ p25_dtm[prev_s * 4 + next_s], 4);
|
||||
uint8_t cand = (uint8_t)(metric[prev][prev_s] + branch);
|
||||
if (cand < min_dist) {
|
||||
min_dist = cand;
|
||||
best_prev = (uint8_t)prev_s;
|
||||
}
|
||||
}
|
||||
metric[curr][next_s] = min_dist;
|
||||
path[t][next_s] = best_prev;
|
||||
}
|
||||
}
|
||||
|
||||
// Find final state with minimum metric
|
||||
uint8_t min_final = 0;
|
||||
uint8_t min_metric = metric[curr][0];
|
||||
for (int s = 1; s < 4; s++) {
|
||||
if (metric[curr][s] < min_metric) {
|
||||
min_metric = metric[curr][s];
|
||||
min_final = (uint8_t)s;
|
||||
}
|
||||
}
|
||||
|
||||
// Traceback: Reconstruct state sequence (tdibits[t] = state after symbol t)
|
||||
tdibits[48] = min_final;
|
||||
for (t = 48; t >= 1; t--) {
|
||||
tdibits[t - 1] = path[t][tdibits[t]];
|
||||
}
|
||||
|
||||
//pack tdibits into return payload bytes (use first 48 as original)
|
||||
for (i = 0; i < 12; i++)
|
||||
treturn[i] = (tdibits[(i*4)+0] << 6) | (tdibits[(i*4)+1] << 4) | (tdibits[(i*4)+2] << 2) | tdibits[(i*4)+3];
|
||||
|
||||
//trellis point/state err tally
|
||||
// if (irr_err != 0)
|
||||
// fprintf (stderr, " P_ERR = %d", irr_err);
|
||||
|
||||
return (irr_err);
|
||||
}
|
||||
|
||||
|
||||
//debug what was the cost metric on this
|
||||
// fprintf (stderr, " R12 Min Metric = %d; ", min_metric);
|
||||
|
||||
// Return total Hamming errors in best path
|
||||
return (int)min_metric;
|
||||
}
|
||||
@@ -143,7 +143,7 @@ void processMPDU(dsd_opts * opts, dsd_state * state)
|
||||
//debug
|
||||
// fprintf (stderr, " J:%d;", j); //use this with the P_ERR inside of 34 rate decoder to see where the failures occur
|
||||
|
||||
ec[j] = dmr_34 (tsbk_dibit, r34byte_b);
|
||||
ec[j] = viterbi_r34 (tsbk_dibit, r34byte_b);
|
||||
|
||||
//shuffle 34 rate data into array
|
||||
if (j != 0) //should never happen, but just in case
|
||||
|
||||
Reference in New Issue
Block a user