NXDN: Add Lich Code 0x46 for Icom DCR Voice and SACCH Payload; #299

This commit is contained in:
lwvmobile
2025-06-30 20:25:47 -04:00
parent bd98e3befb
commit 8d1c92c490
3 changed files with 192 additions and 2 deletions

View File

@@ -1258,6 +1258,7 @@ void nxdn_descramble (uint8_t dibits[], int len);
//nxdn deinterleaving/depuncturing functions
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_sacch2(dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type);
//type-d 'idas' deinterleaving/depuncturing functions

View File

@@ -421,6 +421,176 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
}
//sacch2 (Icom DCR)
void nxdn_deperm_sacch2(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
{
//see about initializing these variables
uint8_t deperm[60]; //60
uint8_t depunc[72]; //72
uint8_t trellis_buf[32]; //32
memset (deperm, 0, sizeof(deperm));
memset (depunc, 0, sizeof(depunc));
memset (trellis_buf, 0, sizeof(trellis_buf));
int o = 0;
uint8_t crc = 1; //value computed by crc6 on payload
uint8_t check = 0; //value pulled from last 6 bits
for (int i=0; i<60; i++)
deperm[PERM_12_5[i]] = bits[i];
for (int p=0; p<60; p+= 10) {
depunc[o++] = deperm[p+0];
depunc[o++] = deperm[p+1];
depunc[o++] = deperm[p+2];
depunc[o++] = deperm[p+3];
depunc[o++] = deperm[p+4];
depunc[o++] = 0;
depunc[o++] = deperm[p+5];
depunc[o++] = deperm[p+6];
depunc[o++] = deperm[p+7];
depunc[o++] = deperm[p+8];
depunc[o++] = deperm[p+9];
depunc[o++] = 0;
}
//switch to the convolutional decoder
uint8_t temp[80];
uint8_t s0;
uint8_t s1;
uint8_t m_data[5]; //5
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
memset (trellis_buf, 0, sizeof(trellis_buf));
for (int i = 0; i < 72; i++)
temp[i] = depunc[i] << 1;
CNXDNConvolution_start();
for (int i = 0; i < 36; i++)
{
s0 = temp[(2*i)];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
//stored as 4 bytes, will need to convert to trellis_buf after running
CNXDNConvolution_chainback(m_data, 32);
for(int i = 0; i < 4; i++)
{
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
}
crc = crc6(trellis_buf, 26);
for (int i = 0; i < 6; i++)
{
check = check << 1;
check = check | trellis_buf[i+26];
}
//debug
// if (crc == check)
// fprintf (stderr, " Pass 1 ");
//if the crc fails, attempt again with the other trellis decoder
if (crc != check)
{
//debug
// fprintf (stderr, " Pass 2 ");
crc = 1; check = 0;
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (m_data, 0, sizeof(m_data));
trellis_decode(trellis_buf, depunc, 32);
//fill m_data bytes with trellis_buf
for(int i = 0; i < 4; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
crc = crc6(trellis_buf, 26); //32
for (int i = 0; i < 6; i++)
{
check = check << 1;
check = check | trellis_buf[i+26];
}
}
uint8_t nsf_sacch[26];
memset (nsf_sacch, 0, sizeof(nsf_sacch));
for (int i = 0; i < 26; i++)
nsf_sacch[i] = trellis_buf[i+6];
//SF configuration is a bit different for this
uint8_t sf_fb = trellis_buf[0]; UNUSED(sf_fb);
uint8_t sf_num = (uint8_t) convert_bits_into_output(trellis_buf+1, 2);
uint8_t ran = (uint8_t) convert_bits_into_output(trellis_buf+3, 3); UNUSED(ran); //unclear if this is a RAN like value or not, but its consistent
//debug
// if (crc == check)
// fprintf (stderr, " SF: %X; LB: %d;", sf_num, sf_fb);
sf_num = 3-sf_num;
if (crc == check)
fprintf (stderr, " PF: %d/4; RAN: %d;", sf_num+1, ran);
memcpy(state->nxdn_sacch_frame_segment[sf_num], nsf_sacch, 18*sizeof(uint8_t));
if (crc == check)
state->nxdn_sacch_frame_segcrc[sf_num] = 0;
else state->nxdn_sacch_frame_segcrc[sf_num] = 1;
if (sf_num == 3) //just showing as MT: 0 (was CALL_RESP)
NXDN_SACCH_Full_decode(opts, state);
//instead, just use static values so event log will log something, and do wav files, etc
//disable this is random false positive for this lich code triggers this often enough
if (crc == check)
{
state->gi[0] = 0;
state->nxdn_last_ran = 7;
state->nxdn_last_tg = 777;
state->nxdn_last_rid = 777;
sprintf (state->generic_talker_alias[0], "%s", "ICOM DCR");
sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s; ", "ICOM DCR");
}
if (opts->payload == 1)
{
fprintf (stderr, "\n ICOM DCR SACCH ");
for (int i = 0; i < 4; i++)
fprintf (stderr, "[%02X]", m_data[i]);
if (crc != check)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
}
//clear out if run, or crc error
if (sf_num == 3)
{
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
}
else if (crc != check)
{
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
}
}
void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type)
{
uint8_t deperm[348]; //348
@@ -1154,7 +1324,7 @@ void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
//RTCH Outbound will take precedent when differences may occur (except CALL_ASSGN)
fprintf (stderr, "%s", KYEL);
if (MessageType == 0x10) fprintf(stderr, " IDLE");
else if (MessageType == 0x00) fprintf(stderr, " CALL_RESP");
// else if (MessageType == 0x00) fprintf(stderr, " CALL_RESP");
else if (MessageType == 0x01) fprintf(stderr, " VCALL");
else if (MessageType == 0x02) fprintf(stderr, " VCALL_REC_REQ");
else if (MessageType == 0x03) fprintf(stderr, " VCALL_IV");
@@ -1185,7 +1355,7 @@ void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA");
else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP");
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS");
else fprintf(stderr, " Unknown M-%02X", MessageType);
else fprintf(stderr, " Unknown Message Type: %02X;", MessageType);
fprintf (stderr, "%s", KNRM);
//Zero out stale values on DISC or TX_REL only (IDLE messaages occur often on NXDN96 VCH, and randomly on Type-C FACCH1 steals for some reason)

View File

@@ -49,6 +49,9 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
int facch3 = 0;
int udch2 = 0;
//Icom DCR Mode Specific Things
int sacch2 = 0; //sacch, but without SF or RAN value (for data dump currently)
//new breakdown of lich codes
uint8_t lich_rf = 0; //RF Channel Type
uint8_t lich_fc = 0; //Functional Channel Type
@@ -173,6 +176,12 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
sacch = 1;
break;
//Icom DCR Mode
case 0x46:
voice = 3;
sacch2 = 1;
break;
//NXDN "Type-D" or "IDAS" Specific Lich Codes
case 0x76: //normal vch voice (in one and two)
case 0x77:
@@ -250,6 +259,15 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (opts->payload == 1)
fprintf (stderr, "L%02X - ", lich);
}
else if (sacch2)
{
if (opts->frame_nxdn48 == 1)
{
printFrameSync (opts, state, "ICOM DCR ", 0, "-");
}
if (opts->payload == 1)
fprintf (stderr, "L%02X - ", lich);
}
else if (voice || facch || sacch || facch2 || udch || cac)
{
if (opts->frame_nxdn48 == 1)
@@ -473,6 +491,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (facch3) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 1);
if (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
if (sacch2) nxdn_deperm_sacch2(opts, state, sacch_bits);
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
//Seperated UDCH user data from facch2 data