NXDN Type-D Decoding/Trunking; NXDN Tweaks; #115

This commit is contained in:
lwvmobile 2023-04-23 19:52:32 -04:00
parent 3101bbde28
commit c507ed229c
5 changed files with 1041 additions and 255 deletions

View File

@ -672,6 +672,7 @@ typedef struct
//multi-key array
unsigned long long int rkey_array[0xFFFF];
int keyloader; //let us know the keyloader is active
//dmr late entry mi
uint64_t late_entry_mi_fragment[2][7][3];
@ -857,10 +858,15 @@ void ncursesClose ();
//new NXDN Functions start here!
void nxdn_frame (dsd_opts * opts, dsd_state * state);
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_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]);
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type);
//type-d 'idas' deinterleaving/depuncturing functions
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction);
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type);
//end
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
@ -870,6 +876,7 @@ static uint8_t crc6(const uint8_t buf[], int len);
static uint16_t crc12f(const uint8_t buf[], int len);
static uint16_t crc15(const uint8_t buf[], int len);
static uint16_t crc16cac(const uint8_t buf[], int len);
static uint8_t crc7_scch(uint8_t bits[], int len); //converted from op25 crc6
/* NXDN Convolution functions */
void CNXDNConvolution_start(void);
@ -893,6 +900,8 @@ void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_adj_site(dsd_opts * opts, dsd_state * state, uint8_t * Message);
//Type-D SCCH Message Decoder
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction);
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);

View File

@ -894,7 +894,8 @@ initState (dsd_state * state)
state->nxdn_bw = 0;
//multi-key array
memset (state->rkey_array, 0, sizeof(state->rkey_array));
memset (state->rkey_array, 0, sizeof(state->rkey_array));
state->keyloader = 0; //keyloader off
//Remus DMR End Call Alert Beep
state->dmr_end_alert[0] = 0;
@ -1301,10 +1302,11 @@ main (int argc, char **argv)
opts.p25_trunk = 0; //turn off trunking mode if user enabled it
break;
case 'k': //NXDN multi-key loader
case 'k': //multi-key loader
strncpy(opts.key_in_file, optarg, 1023);
opts.key_in_file[1023] = '\0';
csvKeyImport(&opts, &state);
state.keyloader = 1;
break;
case 'Q': //'DSP' Structured Output file for OKDMRlib
@ -1443,17 +1445,9 @@ main (int argc, char **argv)
case 'R':
sscanf (optarg, "%lld", &state.R);
if (state.R > 0x7FFF)
{
state.R = 0x7FFF;
}
// opts.dmr_mute_encL = 0;
// opts.dmr_mute_encR = 0;
if (state.R == 0)
{
// opts.dmr_mute_encL = 1;
// opts.dmr_mute_encR = 1;
}
if (state.R > 0x7FFF) state.R = 0x7FFF;
//disable keyloader in case user tries to use this and it at the same time
state.keyloader = 0;
break;
case 'H':

View File

@ -121,6 +121,14 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
check = check | trellis_buf[84+i]; //80
}
if (crc != check)
{
fprintf (stderr, " FACCH");
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
@ -132,12 +140,12 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
{
fprintf (stderr, "[%02X]", m_data[i]);
}
fprintf (stderr, " - %03X %03X", check, crc);
// fprintf (stderr, " - %03X %03X", check, crc);
}
}
//new sacch
//sacch
void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
{
//see about initializing these variables
@ -237,9 +245,23 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
nsf_sacch[i] = trellis_buf[i+8];
}
if (crc == check)
{
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
state->nxdn_last_ran = ran;
}
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, nsf_sacch);
else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, nsf_sacch);
if (crc != check)
{
fprintf (stderr, " SACCH NSF");
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
if (opts->payload == 1)
{
fprintf (stderr, "\n SACCH NSF ");
@ -247,7 +269,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
{
fprintf (stderr, "[%02X]", m_data[i]);
}
if (crc != check) fprintf (stderr, " CRC ERR - %02X %02X", check, crc);
// if (crc != check) fprintf (stderr, " CRC ERR - %02X %02X", check, crc);
}
}
@ -258,7 +280,7 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
//sf and ran together are denoted as SR in the manual (more confusing acronyms)
//sf (structure field) and RAN will always exist in first 8 bits of each SACCH, then the next 18 bits are the fragment of the superframe
sf = (trellis_buf[0] << 1) | trellis_buf[1];
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
if (sf == 3) part_of_frame = 0;
else if (sf == 2) part_of_frame = 1;
else if (sf == 1) part_of_frame = 2;
@ -269,12 +291,21 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
else fprintf (stderr, " ");
fprintf (stderr, "%s", KNRM);
if (crc != check)
{
fprintf (stderr, " SACCH SF%d", sf);
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
//reset scrambler seed to key value on new superframe
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
if (crc == check)
{
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
state->nxdn_ran = state->nxdn_last_ran = ran;
state->nxdn_sf = sf;
state->nxdn_part_of_frame = part_of_frame;
@ -305,15 +336,14 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
{
fprintf (stderr, "[%02X]", m_data[i]);
}
if (crc != check) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
// if (crc != check) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
}
}
}
void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348])
void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type)
{
uint8_t deperm[500]; //348
uint8_t depunc[500]; //406
uint8_t trellis_buf[400]; //199
@ -394,6 +424,16 @@ void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[34
check = check | trellis_buf[i+184];
}
if (crc != check)
{
fprintf (stderr, "%s", KYEL);
if (type == 0) fprintf (stderr, " UDCH");
if (type == 1) fprintf (stderr, " FACCH2");
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
uint8_t f2u_message_buffer[400]; //199
memset (f2u_message_buffer, 0, sizeof(f2u_message_buffer));
@ -405,33 +445,52 @@ void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[34
}
if (crc == check)
{
fprintf (stderr, " F2/U ");
NXDN_Elements_Content_decode(opts, state, 1, f2u_message_buffer);
{
if (type == 1) NXDN_Elements_Content_decode(opts, state, 1, f2u_message_buffer);
if (type == 0) ; //need handling for user data (text messages and AVL)
}
else if (opts->aggressive_framesync == 0)
{
if (type == 1) NXDN_Elements_Content_decode(opts, state, 0, f2u_message_buffer);
if (type == 0) ; //need handling for user data (text messages and AVL)
}
else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, f2u_message_buffer);
if (opts->payload == 1)
{
fprintf (stderr, "\n");
fprintf (stderr, " F2/U Payload\n ");
if (type == 0) fprintf (stderr, " UDCH");
if (type == 1) fprintf (stderr, " FACCH2");
fprintf (stderr, " Payload\n ");
for (int i = 0; i < 26; i++)
{
if (i == 13) fprintf (stderr, "\n ");
fprintf (stderr, "[%02X]", m_data[i]);
if (i == 12) fprintf (stderr, "\n ");
}
if (crc != check) fprintf (stderr, " CRC ERR ");
// if (crc != check) fprintf (stderr, " CRC ERR ");
if (type == 0)
{
fprintf (stderr, "\n UDCH Data: ASCII - " );
for (int i = 0; i < 24; i++) //remove crc portion
{
if (m_data[i] <= 0x7E && m_data[i] >=0x20)
{
fprintf (stderr, "%c", m_data[i]);
}
else fprintf (stderr, " ");
}
}
}
}
void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
{
uint8_t deperm[500]; //300
uint8_t depunc[500]; //350
uint8_t trellis_buf[400]; //171
int id = 0;
int ran = 0;
uint16_t crc = 0;
memset (deperm, 0, sizeof(deperm));
@ -505,11 +564,27 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
cac_message_buffer[i] = trellis_buf[i+8];
}
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
else fprintf (stderr, " ");
if (crc == 0)
{
fprintf (stderr, " CAC ");
NXDN_Elements_Content_decode(opts, state, 1, cac_message_buffer);
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
state->nxdn_last_ran = ran;
}
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " CAC");
fprintf (stderr, "%s", KNRM);
if (crc != 0)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, cac_message_buffer);
else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, cac_message_buffer);
if (opts->payload == 1)
@ -521,32 +596,320 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
fprintf (stderr, "[%02X]", m_data[i]);
if (i == 10) fprintf (stderr, "\n ");
}
if (crc != 0) fprintf (stderr, " CRC ERR ");
// if (crc != 0) fprintf (stderr, " CRC ERR ");
}
}
//Type-D "IDAS"
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction)
{
fprintf (stderr, "%s", KYEL);
fprintf (stderr, " SCCH");
//see about initializing these variables
uint8_t deperm[200]; //60
uint8_t depunc[200]; //72
uint8_t trellis_buf[400]; //32
memset (deperm, 0, sizeof(deperm));
memset (depunc, 0, sizeof(depunc));
memset (trellis_buf, 0, sizeof(trellis_buf));
int o = 0;
uint8_t crc = 0; //value computed by crc7 on payload
uint8_t check = 0; //value pulled from last 7 bits
int sf = 0;
int part_of_frame = 0;
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[90];
uint8_t s0;
uint8_t s1;
uint8_t m_data[10]; //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;
}
for (int i = 0; i < 8; i++)
{
temp[i+72] = 0;
}
CNXDNConvolution_start();
for (int i = 0U; i < 36U; i++)
{
s0 = temp[(2*i)];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 32U);
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 = crc7_scch(trellis_buf, 25);
for (int i = 0; i < 7; i++)
{
check = check << 1;
check = check | trellis_buf[i+25];
}
//check the sf early for scrambler reset, if required
sf = (trellis_buf[0] << 1) | trellis_buf[1];
if (sf == 3) part_of_frame = 0;
else if (sf == 2) part_of_frame = 1;
else if (sf == 1) part_of_frame = 2;
else if (sf == 0) part_of_frame = 3;
else part_of_frame = 0;
//reset scrambler seed to key value on new superframe
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
/*
4.3.2. Mapping to Functional Channel
When Subscriber Unit performs voice communication in RTCH2, SCCH is allocated as
described below.
SCCH has super Frame Structure with 4 frame unit during communication, and has non
super Frame Structure with single frame unit at the start time and end time of sending.
Allocation of the other channel is not specified particularly.
*/
//English Translation: Superframe when voice, Non Superframe when not voice
//What I've found is that by not using the superframe structure, and decoding each 'unit'
//individually instead, we can get more expedient decoding on elements without having to
//sacrifice an entire superframe for one bad CRC, also each element cleanly divides into a
//single 'unit' except for enc parms IV, which can be stored seperately if needed
//NOTE: scch has its own message format, and thus, doesn't go to content element decoding
//like everything else does
if (crc != check)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
if (crc == check) NXDN_decode_scch (opts, state, trellis_buf, direction);
else if (opts->aggressive_framesync == 0) NXDN_decode_scch (opts, state, trellis_buf, direction);
fprintf (stderr, "%s", KNRM);
if (opts->payload == 1)
{
fprintf (stderr, "\n SCCH Payload ");
for (int i = 0; i < 4; i++)
{
fprintf (stderr, "[%02X]", m_data[i]);
}
// if (crc != check) fprintf (stderr, " CRC ERR - %02X %02X", check, crc);
}
fprintf (stderr, "%s", KNRM);
}
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type)
{
uint8_t deperm[300]; //144
uint8_t depunc[300]; //192
uint8_t trellis_buf[600]; //96
uint8_t f3_udch2[288]; //completed bitstream without crc and tailing bits attached
uint8_t f3_udch2_bytes[36]; //completed bytes - with crc and tail
uint16_t crc[2]; //crc calculated by function
uint16_t check[2]; //crc from payload for comparison
int out;
//switch to the convolutional decoder
uint8_t temp[500];
uint8_t s0;
uint8_t s1;
uint8_t m_data[40]; //13
memset (temp, 0, sizeof(temp));
memset (m_data, 0, sizeof(m_data));
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (deperm, 0, sizeof(deperm));
memset (depunc, 0, sizeof(depunc));
memset (crc, 0, sizeof(crc));
memset (check, 0, sizeof(check));
memset (f3_udch2, 0, sizeof(f3_udch2));
memset (f3_udch2_bytes, 0, sizeof(f3_udch2_bytes));
for (int j=0; j<2; j++)
{
for (int i=0; i<144; i++)
deperm[PERM_16_9[i]] = bits[i+(j*144)];
out = 0;
for (int i=0; i<144; i+=3) {
depunc[out++] = deperm[i+0];
depunc[out++] = 0;
depunc[out++] = deperm[i+1];
depunc[out++] = deperm[i+2];
}
for (int i = 0; i < 192; i++)
{
temp[i] = depunc[i] << 1;
}
for (int i = 0; i < 8; i++)
{
temp[i+192] = 0;
}
CNXDNConvolution_start();
for (int i = 0U; i < 100U; i++)
{
s0 = temp[(2*i)];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 96U);
for(int i = 0; i < 12; 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[j] = crc12f (trellis_buf, 84); //84
for (int i = 0; i < 12; i++)
{
check[j] = check[j] << 1;
check[j] = check[j] | trellis_buf[84+i]; //84
}
//transfer to storage sans crc and tail bits
for (int i = 0; i < 80; i++) f3_udch2[i+(j*80)] = trellis_buf[i];
for (int i = 0; i < 12; i++) f3_udch2_bytes[i+(j*12)] = m_data[i];
}
if (crc[0] != check[0] || crc[1] != check[1])
{
if (type == 0) fprintf (stderr, " UDCH2");
if (type == 1) fprintf (stderr, " FACCH3");
fprintf (stderr, "%s", KRED);
fprintf (stderr, " (CRC ERR)");
fprintf (stderr, "%s", KNRM);
}
if (crc[0] == check[0] && crc[1] == check[1])
{
if (type == 1) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
if (type == 0) ; //need handling for user data (text messages and AVL)
}
else if (opts->aggressive_framesync == 0)
{
if (type == 1) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
if (type == 0) ; //need handling for user data (text messages and AVL)
}
if (opts->payload == 1)
{
fprintf (stderr, "\n");
if (type == 0) fprintf (stderr, " UDCH2");
if (type == 1) fprintf (stderr, " FACCH3");
fprintf (stderr, " Payload \n ");
for (int i = 0; i < 24; i++)
{
if (i == 12) fprintf (stderr, "\n ");
fprintf (stderr, "[%02X]", f3_udch2_bytes[i]);
}
// fprintf (stderr, " - %03X %03X", check[0], crc[0]);
// fprintf (stderr, " - %03X %03X", check[1], crc[1]);
if (type == 0)
{
fprintf (stderr, "\n UDCH2 Data: ASCII - " );
for (int i = 0; i < 22; i++) //all but last crc portion
{
if (i == 12) i = 14; //skip first crc portion
if (f3_udch2_bytes[i] <= 0x7E && f3_udch2_bytes[i] >=0x20)
{
fprintf (stderr, "%c", f3_udch2_bytes[i]);
}
else fprintf (stderr, " ");
}
}
}
}
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
{
//NOTE: Most Req/Resp (request and respone) share same message type but differ depending on channel type
//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 == 0x11) fprintf(stderr, " DISC"); //disconnect
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");
else if (MessageType == 0x07) fprintf(stderr, " TX_REL_EX");
else if (MessageType == 0x08) fprintf(stderr, " TX_REL");
else if (MessageType == 0x04) fprintf(stderr, " VCALL_ASSGN");
else if (MessageType == 0x05) fprintf(stderr, " VCALL_ASSGN_DUP");
else if (MessageType == 0x06) fprintf(stderr, " CALL_CONN_RESP");
else if (MessageType == 0x07) fprintf(stderr, " TX_REL_EX");
else if (MessageType == 0x08) fprintf(stderr, " TX_REL");
else if (MessageType == 0x09) fprintf(stderr, " DCALL_HEADER");
else if (MessageType == 0x0A) fprintf(stderr, " DCALL_REC_REQ");
else if (MessageType == 0x0B) fprintf(stderr, " DCALL_UDATA");
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK");
else if (MessageType == 0x0D) fprintf(stderr, " DCALL_ASSGN_DUP");
else if (MessageType == 0x0E) fprintf(stderr, " DCALL_ASSGN");
else if (MessageType == 0x11) fprintf(stderr, " DISC");
else if (MessageType == 0x17) fprintf(stderr, " DST_ID_INFO");
else if (MessageType == 0x18) fprintf(stderr, " SITE_INFO");
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO");
else if (MessageType == 0x1C) fprintf(stderr, " FAIL_STAT_INFO");
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO");
else if (MessageType == 0x1A) fprintf(stderr, " CCH_INFO");
else if (MessageType == 0x1B) fprintf(stderr, " ADJ_SITE_INFO");
else if (MessageType == 0x1C) fprintf(stderr, " FAIL_STAT_INFO");
else if (MessageType == 0x20) fprintf(stderr, " REG_RESP");
else if (MessageType == 0x22) fprintf(stderr, " REG_C_RESP");
else if (MessageType == 0x24) fprintf(stderr, " GRP_REG_RESP");
@ -555,39 +918,20 @@ void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
else if (MessageType == 0x38) fprintf(stderr, " SDCALL_REQ_HEADER");
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 if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK");
else fprintf(stderr, " Unknown M-%02X", MessageType);
fprintf (stderr, "%s", KNRM);
//on nxdn48 trunking -- zero out stale values so they won't persist after a transmit release, idle, or disconnect
if (opts->frame_nxdn48 == 1)
//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)
if (MessageType == 0x08 || MessageType == 0x11)
{
if (MessageType == 0x08 || MessageType == 0x10 || MessageType == 0x11) //tx_rel, idle, or disc
{
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0;
state->nxdn_cipher_type = 0;
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
sprintf (state->nxdn_call_type, "%s", "");
}
}
//if nxdn96, only zero out on disc or tx_rel, some systems have data frames have idle that wipe out the tg/rid and cipher
else
{
if (MessageType == 0x08 || MessageType == 0x11) //tx_rel, or disc
{
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0;
state->nxdn_cipher_type = 0;
// memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
// memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
sprintf (state->nxdn_call_type, "%s", "");
}
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0;
if (state->M == 0) state->nxdn_cipher_type = 0;
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
sprintf (state->nxdn_call_type, "%s", "");
}
}
@ -701,4 +1045,23 @@ static uint16_t crc16cac(const uint8_t buf[], int len)
}
crc = crc ^ 0xffff;
return crc & 0xffff;
}
static uint8_t crc7_scch(uint8_t bits[], int len)
{
uint8_t s[7];
uint8_t a;
for (int i=0;i<7;i++)
s[i] = 1;
for (int i=0;i<len;i++) {
a = bits[i] ^ s[0];
s[0] = s[1];
s[1] = s[2];
s[2] = s[3];
s[3] = a ^ s[4];
s[4] = s[5];
s[5] = s[6];
s[6] = a;
}
return load_i(s, 7);
}

View File

@ -55,8 +55,6 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
MessageType |= (ElementsContent[6] & 1) << 1;
MessageType |= (ElementsContent[7] & 1) << 0;
nxdn_message_type (opts, state, MessageType);
/* Save the "F1" and "F2" flags */
state->NxdnElementsContent.F1 = ElementsContent[0];
state->NxdnElementsContent.F2 = ElementsContent[1];
@ -64,21 +62,30 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
/* Save the "Message Type" field */
state->NxdnElementsContent.MessageType = MessageType;
/* Set the CRC state */
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
/* Decode the right "Message Type" */
switch(MessageType)
{
//VCALL_ASSGN
case 0x04:
//continue flow to DUP, both use same message format
//Debug: Disable DUP messages if they cause random issues with Type-C trunking (i.e. changing SRC ang TGT IDs, hopping in the middle of calls, etc)
//VCALL_ASSGN_DUP
case 0x05:
//DCALL_ASSGN_DUP
case 0x0D:
//VCALL_ASSGN
case 0x04:
//DCALL_ASSGN
case 0x0E:
NXDN_decode_VCALL_ASSGN(opts, state, ElementsContent);
break;
//Alias 0x3F
case 0x3F:
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
NXDN_decode_Alias(opts, state, ElementsContent);
break;
@ -102,9 +109,16 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
NXDN_decode_adj_site(opts, state, ElementsContent);
break;
//VCALL, TX_REL_EXT and TX_REL
case 0x01:
case 0x07:
case 0x08:
NXDN_decode_VCALL(opts, state, ElementsContent);
break;
//DISC
case 0x11:
//NXDN_decode_VCALL(opts, state, ElementsContent);
NXDN_decode_VCALL(opts, state, ElementsContent);
//tune back to CC here - save about 1-2 seconds
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
@ -115,8 +129,6 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
//extra safeguards due to sync issues with NXDN
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));
// state->lastsynctype = -1;
// state->last_cc_sync_time = time(NULL);
opts->p25_is_tuned = 0;
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
@ -128,8 +140,6 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
//extra safeguards due to sync issues with NXDN
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));
// state->lastsynctype = -1;
// state->last_cc_sync_time = time(NULL);
opts->p25_is_tuned = 0;
rtl_udp_tune (opts, state, state->p25_cc_freq);
}
@ -139,60 +149,12 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
//Idle
case 0x10:
{
break;
}
/* VCALL */
case 0x01:
{
/* Set the CRC state */
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
/* Decode the "VCALL" message */
NXDN_decode_VCALL(opts, state, ElementsContent);
/* Check the "Cipher Type" and the "Key ID" validity */
if(CrcCorrect)
{
state->NxdnElementsContent.CipherParameterValidity = 1;
}
else state->NxdnElementsContent.CipherParameterValidity = 0;
break;
} /* End case NXDN_VCALL: */
/* VCALL_IV */
case 0x03:
{
/* Set the CRC state */
state->NxdnElementsContent.VCallIvCrcIsGood = CrcCorrect;
/* Decode the "VCALL_IV" message */
//VCALL_IV
case 0x03:
NXDN_decode_VCALL_IV(opts, state, ElementsContent);
if(CrcCorrect)
{
/* CRC is correct, copy the next theorical IV to use directly from the
* received VCALL_IV */
memcpy(state->NxdnElementsContent.NextIVComputed, state->NxdnElementsContent.IV, 8);
}
else
{
/* CRC is incorrect, compute the next IV to use */
CurrentIV = 0;
/* Convert the 8 bytes buffer into a 64 bits integer */
for(i = 0; i < 8; i++)
{
CurrentIV |= state->NxdnElementsContent.NextIVComputed[i];
CurrentIV = CurrentIV << 8;
}
}
break;
} /* End case NXDN_VCALL_IV: */
/* Unknown Message Type */
default:
@ -201,6 +163,8 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
}
} /* End switch(MessageType) */
nxdn_message_type (opts, state, MessageType);
} /* End NXDN_Elements_Content_decode() */
//externalize multiple sub-element handlers
@ -368,6 +332,20 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
uint8_t DuplexMode[32] = {0};
uint8_t TransmissionMode[32] = {0};
uint8_t MessageType;
/* Get the "Message Type" field */
MessageType = (Message[2] & 1) << 5;
MessageType |= (Message[3] & 1) << 4;
MessageType |= (Message[4] & 1) << 3;
MessageType |= (Message[5] & 1) << 2;
MessageType |= (Message[6] & 1) << 1;
MessageType |= (Message[7] & 1) << 0;
if (MessageType == 0x4) fprintf (stderr, "%s", KGRN); //VCALL_ASSGN
else if (MessageType == 0x05) fprintf (stderr, "%s", KGRN); //VCALL_ASSGN_DUP
else if (MessageType == 0x0E) fprintf (stderr, "%s", KCYN); //DCALL_ASSGN
else if (MessageType == 0x0D) fprintf (stderr, "%s", KCYN); //DCALL_ASSGN_DUP
//DFA specific variables
uint8_t bw = 0;
uint16_t OFN = 0;
@ -406,35 +384,88 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
OFN = (uint16_t)ConvertBitIntoBytes(&Message[64], 16);
IFN = (uint16_t)ConvertBitIntoBytes(&Message[80], 16);
}
//Part 1-E Common Air Interface Ver.1.3 - 6.4.1.23. Voice Call Assignment (VCALL_ASSGN)
//While I've only seen Busy Repeater Message on the SCCH message (different format)
//The manual suggests this message exists on Type-D systems with this configuration
//On Type-D systems, need to truncate to an 11-bit value
//other 5-bits are repeater or prefix value
// uint8_t idas = 0;
// uint8_t rep1 = 0;
// uint8_t rep2 = 0;
// if (strcmp (state->nxdn_location_category, "Type-D") == 0) idas = 1;
// if (idas)
// {
// rep1 = (SourceUnitID >> 11) & 0x1F;
// rep2 = (DestinationID >> 11) & 0x1F;
// SourceUnitID = SourceUnitID & 0x7FF;
// DestinationID = DestinationID & 0x7FF;
// //assign source prefix/rep1 as the tune to channel?
// //In normal VCALL, both prefix/rep values are the same on the Type-D samples I have
// Channel = rep1;
// }
if (MessageType == 0x4 || MessageType == 0x5)
fprintf (stderr, "%s", KGRN); //VCALL_ASSGN
else fprintf (stderr, "%s", KCYN); //DCALL_ASSGN
fprintf (stderr, "\n ");
/* Print the "CC Option" */
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
if(CCOption & 0x40) fprintf(stderr, "Visitor ");
if(CCOption & 0x20) fprintf(stderr, "Priority Paging ");
/* Print the "Call Type" */
fprintf (stderr, "%s", KGRN);
fprintf(stderr, "\n %s - ", NXDN_Call_Type_To_Str(CallType));
fprintf(stderr, "%s - ", NXDN_Call_Type_To_Str(CallType));
/* Print the "Voice Call Option" */
NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
fprintf(stderr, "%s %s - ", DuplexMode, TransmissionMode);
if (MessageType == 0x4 || MessageType == 0x5) NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
if (MessageType == 0x4 || MessageType == 0x5) fprintf(stderr, "%s %s - ", DuplexMode, TransmissionMode);
else fprintf (stderr, " Data Call Assignment - "); //DCALL_ASSGN or DCALL_ASSGN_DUP
/* Print Source ID and Destination ID (Talk Group or Unit ID) */
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
// if (idas) fprintf (stderr, "- Ch: %d ", rep1);
/* Print Channel */
if (state->nxdn_rcn == 0)
fprintf(stderr, "- Channel [%03X][%04d] ", Channel & 0x3FF, Channel & 0x3FF);
if (state->nxdn_rcn == 1)
{
fprintf(stderr, "- DFA Channel [%04X][%05d] ", OFN, OFN);
//running the BW here is a bit repetitive since running the call type/option will echo the same thing
// if (bw == 0) fprintf (stderr, "BW: 6.25 kHz - 4800 bps");
// else if (bw == 1) fprintf (stderr, "BW: 12.5 kHz - 9600 bps");
// else fprintf (stderr, "BW: %d Reserved Value", bw);
//test VCALL_ASSGN_DUP, if no voice sync activity in 1-2 seconds, then convert to assgn and allow tuning
//VCALL_ASSGN_DUP has been seen in the middle of calls, but also on the tail end instead of a TX_REL or DISC
if (MessageType == 0x5 && opts->p25_is_tuned == 1 && opts->p25_trunk == 1)
{
if ( (time(NULL) - state->last_vc_sync_time) > 1 )
{
MessageType = 0x04; //convert to VCALL
opts->p25_is_tuned = 0; //open tuning back up to tune
}
}
//Add support for tuning data and group/private calls on trunking systems
uint8_t tune = 0;
//DCALL_ASSGN and DCALL_ASSGN_DUP
if (MessageType == 0x0D || MessageType == 0x0E )
{
if (opts->trunk_tune_data_calls == 1) tune = 1;
}
else if (MessageType == 0x04) //VCALL_ASSGN and converted VCALL_ASSGN_DUP
{
if (CallType == 4) //individual/private call
{
if (opts->trunk_tune_private_calls) tune = 1;
}
else if (opts->trunk_tune_group_calls) tune = 1;
}
if (tune == 0) goto END_ASSGN;
//run process to figure out frequency value from the channel import or from DFA
long int freq = 0;
if (state->nxdn_rcn == 0)
@ -442,11 +473,6 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
if (state->nxdn_rcn == 1)
freq = nxdn_channel_to_frequency(opts, state, OFN);
//check the rkey array for a scrambler key value
//TGT ID and Key ID could clash though if csv or system has both with different keys
if (state->rkey_array[DestinationID] != 0) state->R = state->rkey_array[DestinationID];
if (state->M == 1) state->nxdn_cipher_type = 0x1;
//check for control channel frequency in the channel map if not available
if (opts->p25_trunk == 1 && state->p25_cc_freq == 0)
{
@ -492,7 +518,7 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
//check to see if the source/target candidate is blocked first
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0)) //DE is digital encrypted, B is block
{
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency already
if (state->p25_cc_freq != 0 && opts->p25_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency
{
//rigctl
if (opts->use_rigctl == 1)
@ -502,7 +528,7 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
state->lastsynctype = -1;
state->last_cc_sync_time = time(NULL);
state->last_vc_sync_time = time(NULL); //useful?
state->last_vc_sync_time = time(NULL);
//
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
@ -511,9 +537,19 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
//set rid and tg when we actually tune to it
state->nxdn_last_rid = SourceUnitID & 0xFFFF;
state->nxdn_last_tg = (DestinationID & 0xFFFF);
state->nxdn_last_rid = SourceUnitID;
state->nxdn_last_tg = DestinationID;
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
//check the rkey array for a scrambler key value
//TGT ID and Key ID could clash though if csv or system has both with different keys
if (state->rkey_array[DestinationID] != 0)
{
state->R = state->rkey_array[DestinationID];
fprintf (stderr, " %s", KYEL);
fprintf (stderr, " Key Loaded: %lld", state->rkey_array[DestinationID]);
}
if (state->M == 1) state->nxdn_cipher_type = 0x1;
}
//rtl_udp
else if (opts->audio_in_type == 3)
@ -531,14 +567,25 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
opts->p25_is_tuned = 1;
//set rid and tg when we actually tune to it
state->nxdn_last_rid = SourceUnitID & 0xFFFF;
state->nxdn_last_tg = (DestinationID & 0xFFFF);
state->nxdn_last_rid = SourceUnitID;
state->nxdn_last_tg = DestinationID;
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
//check the rkey array for a scrambler key value
//TGT ID and Key ID could clash though if csv or system has both with different keys
if (state->rkey_array[DestinationID] != 0)
{
state->R = state->rkey_array[DestinationID];
fprintf (stderr, " %s", KYEL);
fprintf (stderr, " Key Loaded: %lld", state->rkey_array[DestinationID]);
}
if (state->M == 1) state->nxdn_cipher_type = 0x1;
}
}
}
END_ASSGN: ; //do nothing
fprintf (stderr, "%s", KNRM);
} /* End NXDN_decode_VCALL_ASSGN() */
@ -954,6 +1001,20 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
uint8_t TransmissionMode[32] = {0};
unsigned long long int FullMessage = 0;
uint8_t MessageType;
/* Get the "Message Type" field */
MessageType = (Message[2] & 1) << 5;
MessageType |= (Message[3] & 1) << 4;
MessageType |= (Message[4] & 1) << 3;
MessageType |= (Message[5] & 1) << 2;
MessageType |= (Message[6] & 1) << 1;
MessageType |= (Message[7] & 1) << 0;
if (MessageType == 0x1) fprintf (stderr, "%s", KGRN); //VCALL
else if (MessageType == 0x07) fprintf (stderr, "%s", KYEL); //TX_REL_EXT
else if (MessageType == 0x08) fprintf (stderr, "%s", KYEL); //TX_REL
else if (MessageType == 0x11) fprintf (stderr, "%s", KRED); //DISC
/* Decode "CC Option" */
CCOption = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
state->NxdnElementsContent.CCOption = CCOption;
@ -974,6 +1035,20 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
DestinationID = (uint16_t)ConvertBitIntoBytes(&Message[40], 16);
state->NxdnElementsContent.DestinationID = DestinationID;
//On Type-D systems, need to truncate to an 11-bit value
//other 5-bits are repeater or prefix value
uint8_t idas = 0;
uint8_t rep1 = 0;
uint8_t rep2 = 0;
if (strcmp (state->nxdn_location_category, "Type-D") == 0) idas = 1;
if (idas)
{
rep1 = (SourceUnitID >> 11) & 0x1F;
rep2 = (DestinationID >> 11) & 0x1F;
SourceUnitID = SourceUnitID & 0x7FF;
DestinationID = DestinationID & 0x7FF;
}
/* Decode the "Cipher Type" */
CipherType = (uint8_t)ConvertBitIntoBytes(&Message[56], 2);
state->NxdnElementsContent.CipherType = CipherType;
@ -982,6 +1057,8 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
KeyID = (uint8_t)ConvertBitIntoBytes(&Message[58], 6);
state->NxdnElementsContent.KeyID = KeyID;
fprintf (stderr, "\n ");
/* Print the "CC Option" */
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
if(CCOption & 0x40) fprintf(stderr, "Visitor ");
@ -999,16 +1076,21 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
}
/* Print the "Call Type" */
fprintf (stderr, "%s", KGRN);
fprintf(stderr, "\n %s - ", NXDN_Call_Type_To_Str(CallType));
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
fprintf (stderr, "%s - ", NXDN_Call_Type_To_Str(CallType));
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
/* Print the "Voice Call Option" */
NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
fprintf(stderr, "%s %s - ", DuplexMode, TransmissionMode);
if (MessageType == 0x1) NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
if (MessageType == 0x1) fprintf(stderr, "%s %s - ", DuplexMode, TransmissionMode);
else if (MessageType == 0x07) fprintf (stderr, "Transmission Release Ex - "); //TX_REL_EX
else if (MessageType == 0x08) fprintf (stderr, " Transmission Release - "); //TX_REL
else if (MessageType == 0x11) fprintf (stderr, " Disconnect - "); //DISC
/* Print Source ID and Destination ID (Talk Group or Unit ID) */
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
if (idas) fprintf (stderr, "- Ch: %d ", rep1);
fprintf (stderr, "%s", KNRM);
//check the rkey array for a scrambler key value
@ -1017,58 +1099,38 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
if (state->rkey_array[KeyID] != 0) state->R = state->rkey_array[KeyID];
else if (state->rkey_array[DestinationID] != 0) state->R = state->rkey_array[DestinationID];
//consider removing code below, if we get this far (under good crc),
//then we will always have a correct cipher type and won't need to
//force it, its only needed on the vcall_assgn where no cipher is set
//dsd_mbe can also force the scrambler without triggering the settings here
// if (state->M == 1)
// {
// state->nxdn_cipher_type = 0x1;
// CipherType = 0x1;
// }
//debug
// if (CipherType > 0x1) state->R = 0; //don't use on manual key entry
//Don't zero key if no keyloader
if (CipherType != 0x1 && state->keyloader == 1) state->R = 0;
/* Print the "Cipher Type" */
if(CipherType != 0)
if(CipherType != 0 && MessageType == 0x1)
{
fprintf (stderr, "\n %s", KYEL);
fprintf(stderr, "%s - ", NXDN_Cipher_Type_To_Str(CipherType));
}
/* Print the Key ID */
if(CipherType != 0)
if(CipherType != 0 && MessageType == 0x1)
{
fprintf(stderr, "Key ID %u - ", KeyID & 0xFF);
fprintf (stderr, "%s", KNRM);
}
if (state->nxdn_cipher_type == 0x01 && state->R > 0) //scrambler key value
if (CipherType == 0x01 && state->R > 0) //scrambler key value
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, "Value: %05lld", state->R);
fprintf (stderr, "%s", KNRM);
}
//only grab if CRC is okay
if(state->NxdnElementsContent.VCallCrcIsGood)
//only grab if VCALL
if(MessageType == 0x1)
{
if ( (SourceUnitID & 0xFFFF) > 0 ) //
{
state->nxdn_last_rid = SourceUnitID & 0xFFFF;
state->nxdn_last_tg = (DestinationID & 0xFFFF);
state->nxdn_key = (KeyID & 0xFF);
state->nxdn_cipher_type = CipherType;
}
}
else
{
fprintf (stderr, "%s", KRED);
fprintf(stderr, "(CRC ERR) ");
fprintf (stderr, "%s", KNRM);
state->nxdn_last_rid = SourceUnitID;
state->nxdn_last_tg = DestinationID;
state->nxdn_key = KeyID;
state->nxdn_cipher_type = CipherType;
}
//set enc bit here so we can tell playSynthesizedVoice whether or not to play enc traffic
@ -1086,20 +1148,328 @@ void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint32_t i;
state->payload_miN = 0; //zero out
/* Extract the IV from the VCALL_IV message */
for(i = 0; i < 8; i++)
{
state->NxdnElementsContent.IV[i] = (uint8_t)ConvertBitIntoBytes(&Message[(i + 1) * 8], 8);
uint64_t IV = 0;
state->payload_miN = state->payload_miN << 8 | state->NxdnElementsContent.IV[i];
}
//NOTE: On Type-D systems, the FACCH1 version of message only carries a 4-octet payload and also only carries a 22-bit IV
//which makes no sense since you still have the full 80-bits of FACCH1 to use
uint8_t idas = 0;
if (strcmp (state->nxdn_location_category, "Type-D") == 0) idas = 1;
if (!idas) IV = (uint64_t)ConvertBitIntoBytes(&Message[8], 64);
else IV = (uint64_t)ConvertBitIntoBytes(&Message[8], 22);
//At this point, I would assume an LFSR function is needed to expand the 22-bit IV collected here into a 64-bit IV, or 128-bit IV
state->payload_miN = IV;
state->NxdnElementsContent.PartOfCurrentEncryptedFrame = 2;
state->NxdnElementsContent.PartOfNextEncryptedFrame = 1;
fprintf (stderr, "\n VCALL_IV: %016llX", state->payload_miN);
} /* End NXDN_decode_VCALL_IV() */
//SCCH messages have a unique format that can be used in a super frame, but each 'unit'
//can also (mostly) be decoded seperately, except an enc IV
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction)
{
uint8_t sf = (uint8_t)ConvertBitIntoBytes(&Message[0], 2);
uint8_t opcode = (direction << 2 | sf);
fprintf (stderr, "\n " ); //initial line break
if (opts->payload == 1)
{
if (direction == 0) fprintf (stderr, "ISM ");
else fprintf (stderr, "OSM ");
if (sf == 0) fprintf (stderr, "INFO4 ");
else if (sf == 1) fprintf (stderr, "INFO3 ");
else if (sf == 2) fprintf (stderr, "INFO2 ");
else fprintf (stderr, "INFO1 ");
fprintf (stderr, "- ");
fprintf (stderr, "%02X ",opcode);
}
//elements used will be determined by other elements in the ISM/OSM messages
//OSM 4 elements
uint8_t area = Message[2];
uint8_t rep1 = (uint8_t)ConvertBitIntoBytes(&Message[3], 5); //repeater 1 value
uint8_t rep2 = (uint8_t)ConvertBitIntoBytes(&Message[8], 5); //repeater 2 value
uint16_t id = (uint16_t)ConvertBitIntoBytes(&Message[13], 11); //id is context dependent
uint8_t sitet = (uint8_t)ConvertBitIntoBytes(&Message[3], 5); //site type on id=2041 only
uint8_t gu = Message[24]; //group or unit bit
//OSM 2 and 3 elements -- unique only
uint64_t iv_a = (uint64_t)ConvertBitIntoBytes(&Message[13], 12); //initialization vector (0-11)
//OSM 1 elements -- unique only -- DOUBLE CHECK ALL OF THESE!
uint64_t iv_b = (uint64_t)ConvertBitIntoBytes(&Message[18], 6); //initialization vector (b12-17)
uint64_t iv_c = (uint64_t)ConvertBitIntoBytes(&Message[8], 5); //initialization vector (b18-22)
uint8_t iv_type = Message[24]; //0 or 1 tells us whether or not this is for IV, or for cipher/key
uint8_t call_opt = (uint8_t)ConvertBitIntoBytes(&Message[13], 3); //Call Options
uint8_t key_id = (uint8_t)ConvertBitIntoBytes(&Message[18], 6); //Key ID
uint8_t cipher = (uint8_t)ConvertBitIntoBytes(&Message[16], 2); //Cipher Type
uint8_t DuplexMode[32] = {0};
uint8_t TransmissionMode[32] = {0};
//ISM messages are seemingly exactly the same info as OSM, so just going to use OSM versions
//(manual doesn't specify other than that ISM 4 is the same as ISM2, but probably doesn't have IDLE/HALT/SITE ID messages)
//set category to "Type-D" to alert users this is an a Distributed Trunking System
sprintf (state->nxdn_location_category, "Type-D");
//placeholder numbers, using 99 if these aren't set by the Site ID Message (single sites)
if (state->nxdn_location_site_code == 0) state->nxdn_location_site_code = 99;
if (state->nxdn_location_sys_code == 0) state->nxdn_location_sys_code = 99;
if (state->nxdn_last_ran == -1) state->nxdn_last_ran = 99;
state->last_cc_sync_time = time(NULL);
//OSM messages
if (opcode == 0x4 || opcode == 0x0) //INFO 4
{
if (id == 2046)
{
fprintf (stderr, "Idle Repeater Message - ");
fprintf (stderr, "Area: %d; ", area);
fprintf (stderr, "Repeater 1: %d; ", rep1);
fprintf (stderr, "Repeater 2: %d; ", rep2);
}
else if (id == 2045)
{
fprintf (stderr, "Halt Repeater Message - ");
fprintf (stderr, "Area: %d; ", area);
fprintf (stderr, "Repeater 1: %d; ", rep1);
fprintf (stderr, "Repeater 2: %d; ", rep2);
}
else if (id == 2044)
{
fprintf (stderr, "Free Repeater Message - ");
fprintf (stderr, "Area: %d; ", area);
fprintf (stderr, "Free Repeater 1: %d; ", rep1);
fprintf (stderr, "Free Repeater 2: %d; ", rep2);
}
else if (id == 2041)
{
fprintf (stderr, "Site ID Message - ");
fprintf (stderr, "Area: %d; ", area);
fprintf (stderr, "Site Type: %d ", sitet); //Roaming Algorithm by SU; See below;
if (sitet == 0) fprintf (stderr, "Reserved; ");
else if (sitet == 1) fprintf (stderr, "Wide; ");
else if (sitet == 2) fprintf (stderr, "Middle; ");
else fprintf (stderr, "Narrow; ");
fprintf (stderr, "Site Code: %d ", rep2);
if (rep2 == 0) fprintf (stderr, "Reserved; ");
else if (rep2 < 251) fprintf (stderr, "Open Access; "); //Usable voluntary every TRS...?
else fprintf (stderr, "Reserved; ");
state->nxdn_location_site_code = sitet;
state->nxdn_location_sys_code = sitet;
state->nxdn_last_ran = sitet;
}
else //Busy Repeater Message is essentially our 'call in progess' message for a repeater
{
if (gu && rep1 == 0) fprintf (stderr, "REG_COMM; ");
else fprintf (stderr, "Busy Repeater Message - ");
fprintf (stderr, "Area: %d; ", area);
fprintf (stderr, "Go to Repeater: %d; ", rep1);
fprintf (stderr, "Home Repeater: %d; ", rep2);
if (rep1 == 31)
{
fprintf (stderr, "\n%s ", KRED);
state->nxdn_last_tg = 0;
state->nxdn_last_rid = 0;
}
else
{
fprintf (stderr, "\n%s ", KGRN);
//only set this is during a voice tx, and not a data tx
if ( time(NULL) - state->last_vc_sync_time < 1 )
state->nxdn_last_tg = id;
}
fprintf (stderr, " Channel Update - CH: %d - TGT: %d ", rep1, id);
if (gu == 0) fprintf (stderr, "Group Call ");
else fprintf (stderr, "Private Call ");
if (rep1 == 31) fprintf (stderr, "Termination ");
//start tuning section here
uint8_t tune = 0; //use this to check to see if okay to tune
//only tune group calls if user set
if (gu == 0 && opts->trunk_tune_group_calls == 1) tune = 1;
//only tune private calls if user set
if (gu == 1 && opts->trunk_tune_private_calls == 1) tune = 1;
//tune back to 'channel 31' on termination, will be user provided 'default' listening channel
if (rep1 == 31) tune = 1;
//begin tuning
if (tune == 1 && rep1 != 0)
{
//check for control channel frequency in the channel map if not available
if (state->trunk_chan_map[31] != 0) state->p25_cc_freq = state->trunk_chan_map[31]; //user provided channel to go to for '31' -- may change to 0 later?
else if (state->trunk_chan_map[rep2] != 0) state->p25_cc_freq = state->trunk_chan_map[rep2]; //rep2 is home repeater under this message
//run group/tgt analysis and tune if available/desired
//group list mode so we can look and see if we need to block tuning any groups, etc
char mode[8]; //allow, block, digital, enc, etc
//if we are using allow/whitelist mode, then write 'B' to mode for block
//comparison below will look for an 'A' to write to mode if it is allowed
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == id) //tg/tgt only on info4 unit
{
fprintf (stderr, " [%s]", state->group_array[i].groupName);
strcpy (mode, state->group_array[i].groupMode);
}
}
long int freq = 0;
freq = nxdn_channel_to_frequency(opts, state, rep1);
//check to see if the source/target candidate is blocked first
if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0)) //DE is digital encrypted, B is block
{
if (state->p25_cc_freq != 0 && state->last_vc_sync_time > 1 && freq != 0) //if we aren't already on a VC and have a valid frequency already
{
//rigctl
if (opts->use_rigctl == 1)
{
//extra safeguards due to sync issues with NXDN
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));
state->lastsynctype = -1;
state->last_cc_sync_time = time(NULL);
state->last_vc_sync_time = time(NULL); //should we use this here, or not?
//
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
SetFreq(opts->rigctl_sockfd, freq);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
opts->p25_is_tuned = 1;
//check the rkey array for a scrambler key value
//TGT ID and Key ID could clash though if csv or system has both with different keys
if (state->rkey_array[id] != 0) state->R = state->rkey_array[id];
if (state->M == 1) state->nxdn_cipher_type = 0x1;
}
//rtl_udp
else if (opts->audio_in_type == 3)
{
//extra safeguards due to sync issues with NXDN
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));
state->lastsynctype = -1;
state->last_cc_sync_time = time(NULL);
state->last_vc_sync_time = time(NULL); //should we use this here, or not?
//
rtl_udp_tune (opts, state, freq);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
opts->p25_is_tuned = 1;
//check the rkey array for a scrambler key value
//TGT ID and Key ID could clash though if csv or system has both with different keys
if (state->rkey_array[id] != 0) state->R = state->rkey_array[id];
if (state->M == 1) state->nxdn_cipher_type = 0x1;
}
}
}
} //end tuning -- NOTE: since we are only going by last time voice sync detected, tuning here is always 'unlocked'
} //end 'busy' repeater
}
if (opcode == 0x5 || opcode == 0x1) //INFO 3
{
fprintf (stderr, "Source Message - ");
fprintf (stderr, "Area: %d; ", area);
fprintf (stderr, "Free Repeater 1: %d; ", rep1);
if (id == 31)
{
fprintf (stderr, "\n%s ", KYEL);
fprintf (stderr, " Call IV A: %04lX", iv_a);
}
else
{
//rep2 is the home repeater/source prefix in this instance
fprintf (stderr, "\n%s ", KGRN);
fprintf (stderr, " Source Update - Prefix CH: %d SRC: %d - (%d-%d) ", rep2, id, rep2, id); //rep2
//only set this is during a voice tx, and not a data tx
if ( time(NULL) - state->last_vc_sync_time < 1 )
state->nxdn_last_rid = id;
}
}
if (opcode == 0x6 || opcode == 0x2) //OSM INFO 2 -- same as 4
{
fprintf (stderr, "Target Message - ");
fprintf (stderr, "Area: %d; ", area);
fprintf (stderr, "Go to Repeater: %d; ", rep1);
if (id == 31)
{
fprintf (stderr, "\n%s ", KYEL);
fprintf (stderr, " Call IV A: %04lX; ", iv_a); //MSB 0-11
//zero out IV storage and append this chunk to MSB position
state->payload_miN = 0;
state->payload_miN = state->payload_miN | (iv_a << 11); //MSB 11, so shift 11 (22 total bits).
}
else
{
//rep2 is the home repeater/source prefix in this instance
fprintf (stderr, "\n%s ", KGRN);
fprintf (stderr, " Target Update - Prefix CH: %d SRC: %d - (%d-%d) ", rep2, id, rep2, id); //rep2
//only set this is during a voice tx, and not a data tx
if ( time(NULL) - state->last_vc_sync_time < 1 )
state->nxdn_last_tg = id;
}
}
if (opcode == 0x7 || opcode == 0x3) //INFO 1 - Call Option/Encryption Parms
{
fprintf (stderr, "Call Option - ");
fprintf (stderr, "Area: %d; ", area);
fprintf (stderr, "Free Repeater 1: %d; ", rep1);
if (iv_type == 0)
{
fprintf (stderr, "Free Repeater 2: %d; ", rep2); //or Pass Character (31) on ISM
fprintf (stderr, "\n%s ", KYEL);
NXDN_Voice_Call_Option_To_Str(call_opt, DuplexMode, TransmissionMode);
fprintf(stderr, " %s %s ", DuplexMode, TransmissionMode);
if (cipher)
{
fprintf(stderr, "- %s - ", NXDN_Cipher_Type_To_Str(cipher));
fprintf (stderr, "Key ID: %d; ", key_id);
state->nxdn_cipher_type = cipher;
state->nxdn_key = key_id;
}
}
else
{
fprintf (stderr, "\n%s ", KYEL);
fprintf (stderr, "Call IV B: %04lX; ", iv_b);
fprintf (stderr, "Call IV C: %04lX; ", iv_c);
//Append to Call IV storage
state->payload_miN = state->payload_miN | (iv_c << 6); //middle 5 bits..shifts 6 to accomodate last 6 bits below
state->payload_miN = state->payload_miN | (iv_b << 0); //last 6 bits...no shift needed
//At this point, I would assume an LFSR function is needed to expand the 22-bit IV collected here into a 64-bit IV, or 128-bit IV
fprintf (stderr, "Completed IV: %016llX", state->payload_miN);
}
}
}
char * NXDN_Call_Type_To_Str(uint8_t CallType)
{
@ -1107,14 +1477,14 @@ char * NXDN_Call_Type_To_Str(uint8_t CallType)
switch(CallType)
{
case 0: Ptr = "Broadcast Call"; break;
case 1: Ptr = "Group Call"; break;
case 2: Ptr = "Unspecified Call"; break;
case 3: Ptr = "Reserved"; break;
case 4: Ptr = "Individual Call"; break;
case 5: Ptr = "Reserved"; break;
case 6: Ptr = "Interconnect Call"; break;
case 7: Ptr = "Speed Dial Call"; break;
case 0: Ptr = "Broadcast Call"; break;
case 1: Ptr = "Group Call"; break;
case 2: Ptr = "Transmission Release"; break; //"Unspecified Call" This value is used only on TX_REL message.
case 3: Ptr = "Session Call"; break; //"reserved" is session call on Type D
case 4: Ptr = "Private Call"; break;
case 5: Ptr = "Reserved"; break;
case 6: Ptr = "PSTN Interconnect Call"; break;
case 7: Ptr = "PSTN Speed Dial Call"; break;
default: Ptr = "Unknown Call Type"; break;
}

View File

@ -32,16 +32,17 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
// length is implicitly 192, with frame sync in first 10 dibits
uint8_t dbuf[182];
uint8_t lich;
int answer_len=0;
int answer_len = 0;
uint8_t answer[32];
uint8_t sacch_answer[32];
int lich_parity_received;
int lich_parity_computed;
int voice=0;
int facch=0;
int facch2=0;
int sacch=0;
int cac=0;
int voice = 0;
int facch = 0;
int facch2 = 0;
int udch = 0;
int sacch = 0;
int cac = 0;
int sr_structure;
int sr_ran;
@ -51,12 +52,19 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
int facch3 = 0;
int udch2 = 0;
//new breakdown of lich codes
uint8_t lich_rf = 0; //RF Channel Type
uint8_t lich_fc = 0; //Functional Channel Type
uint8_t lich_op = 0; //Options
uint8_t direction; //inbound or outbound direction
uint8_t lich_dibits[8];
uint8_t sacch_bits[60];
uint8_t facch_bits_a[144];
uint8_t facch_bits_b[144];
uint8_t cac_bits[300];
uint8_t facch2_bits[348]; //facch2 or udch, same amount of bits
uint8_t facch3_bits[288]; //facch3 or udch2, same amoount of bits
//nxdn bit buffer, for easy assignment handling
int nxdn_bit_buffer[364];
@ -89,7 +97,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
lich = lich >> 1;
if (lich_parity_received != lich_parity_computed)
{
// state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
goto END;
}
@ -104,7 +112,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (lich % 2 == 0 && opts->p25_trunk == 1)
{
if (opts->payload == 1) fprintf(stderr, " Simplex/Inbound NXDN lich on trunking system - type 0x%02X\n", lich);
// state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
goto END;
}
@ -115,13 +123,15 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
break;
case 0x28: //facch2 types
case 0x29:
case 0x2e:
case 0x2f:
case 0x48:
case 0x49:
facch2 = 1;
break;
case 0x2e: //udch types
case 0x2f:
case 0x4e:
case 0x4f:
facch2 = 1;
udch = 1;
break;
case 0x32: //facch in 1, vch in 2
case 0x33:
@ -134,7 +144,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
case 0x34: //vch in 1, facch in 2
case 0x35:
case 0x54:
case 0x55:
case 0x55: //disabled for testing, IDAS system randomly triggers this one, probably due to poor signal
voice = 1;
facch = 2;
sacch = 1;
@ -155,7 +165,6 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
case 0x41:
case 0x50:
case 0x51:
voice = 0;
facch = 3;
sacch = 1;
@ -166,47 +175,52 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
break;
//NXDN "Type-D" or "IDAS" Specific Lich Codes
case 0x76: //normal voice (in one and two)
case 0x76: //normal vch voice (in one and two)
case 0x77:
idas = 1;
scch = 1;
voice = 3;
break;
case 0x74: //vch in 1, scch in 2 (scch 2 steal)
case 0x74: //vch in 1, facch1 in 2 (facch 2 steal)
case 0x75:
idas = 1;
scch = 1;
voice = 1;
scch = 2;
facch = 2;
break;
case 0x72: //scch in 1, vch in 2 (scch 1 steal)
case 0x72: //facch in 1, vch in 2 (facch 1 steal)
case 0x73:
idas = 1;
scch = 1;
voice = 2;
scch = 2;
facch = 1;
break;
case 0x70: //scch in 1 and 2
case 0x70: //facch steal in vch 1 and vch 2 (during voice only)
case 0x71:
idas = 1;
scch = 4; //total of three scch positions
scch = 1;
facch = 3;
break;
case 0x6E: //udch2
case 0x6F:
idas = 1;
scch = 1;
udch2 = 1;
break;
case 0x68:
case 0x69: //facch3
idas = 1;
scch = 1;
facch3 = 1;
break;
case 0x62:
case 0x63: //facch1 in 1, n/post in 2
case 0x63: //facch1 in 1, null data and post field in 2
idas = 1;
scch = 1;
facch = 1;
break;
case 0x60:
case 0x61: //facch1 in both
case 0x61: //facch1 in both (non vch)
idas = 1;
scch = 1;
facch = 3;
@ -230,7 +244,8 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
{
printFrameSync (opts, state, "IDAS D ", 0, "-");
}
if (opts->payload == 1) fprintf (stderr, "L%02X -", lich);
if (opts->payload == 1)
fprintf (stderr, "L%02X - ", lich);
}
else if (voice || facch || sacch || facch2 || cac)
{
@ -239,7 +254,8 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
printFrameSync (opts, state, "NXDN48 ", 0, "-");
}
else printFrameSync (opts, state, "NXDN96 ", 0, "-");
if (opts->payload == 1) fprintf (stderr, "L%02X -", lich);
if (opts->payload == 1)
fprintf (stderr, "L%02X - ", lich);
}
//now that we have a good LICH, we can collect all of our dibits
@ -258,7 +274,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
nxdn_bit_buffer[i*2+1] = dbuf[i] & 1;
}
//sacch
//sacch or scch bits
for (int i = 0; i < 60; i++)
{
sacch_bits[i] = nxdn_bit_buffer[i+16];
@ -283,28 +299,64 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
facch2_bits[i] = nxdn_bit_buffer[i+16];
}
//udch2 or facch3
for (int i = 0; i < 288; i++)
{
facch3_bits[i] = nxdn_bit_buffer[i+16+60];
}
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
//sacch needs extra handling depending on superframe or non-superframe variety
if (voice && !facch && scch < 2) //voice only, no facch/scch steal
//Add advanced decoding of LICH (RF, FC, OPT, and Direction
lich_rf = (lich >> 5) & 0x3;
lich_fc = (lich >> 3) & 0x3;
lich_op = (lich >> 1) & 0x3;
if (lich % 2 == 0) direction = 0;
else direction = 1;
// RF Channel Type
if (lich_rf == 0) fprintf (stderr, "RCCH ");
else if (lich_rf == 1) fprintf (stderr, "RTCH ");
else if (lich_rf == 1) fprintf (stderr, "RDCH ");
else
{
if (lich < 0x60) fprintf (stderr, "RTCH_C ");
else fprintf (stderr, "RTCH2 ");
}
// Functional Channel Type -- things start to get really convoluted here
// These will echo when handled, either with the decoded message type, or relevant crc err
// if (lich_rf == 0) //CAC Type
// {
// //Technically, we should be checking direction as well, but the fc never has split meaning on CAC
// if (lich_fc == 0) fprintf (stderr, "CAC ");
// else if (lich_fc == 1) fprintf (stderr, "Long CAC ");
// else if (lich_fc == 3) fprintf (stderr, "Short CAC ");
// else fprintf (stderr, "Reserved ");
// }
// else //USC Type
// {
// if (lich_fc == 0) fprintf (stderr, "NSF SACCH ");
// else if (lich_fc == 1) fprintf (stderr, "UDCH ");
// else if (lich_fc == 2) fprintf (stderr, "SF SACCH ");
// else if (lich_fc == 3) fprintf (stderr, "SF SACCH/IDLE ");
// }
//Option/Steal Flags echoed in Voice, V+F, or Data
if (voice && !facch) //voice only, no facch steal
{
fprintf (stderr, "%s", KGRN);
fprintf (stderr, " Voice ");
fprintf (stderr, "%s", KNRM);
}
else if (voice && facch) //voice with facch steal
else if (voice && facch) //voice with facch1 steal
{
fprintf (stderr, "%s", KGRN);
fprintf (stderr, " V%d+F%d ", 3 - facch, facch); //print which position on each
fprintf (stderr, "%s", KNRM);
}
else if (voice && scch > 1) //voice with scch steal
{
fprintf (stderr, "%s", KGRN);
fprintf (stderr, " V%d+S%d ", 4 - scch, scch); //print which position on each
fprintf (stderr, "%s", KNRM);
}
else
else //Covers FACCH1 in both, FACCH2, UDCH, UDCH2, CAC
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " Data ");
@ -342,28 +394,22 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) state->nxdn_sacch_non_superframe = TRUE;
else state->nxdn_sacch_non_superframe = FALSE;
//TODO Later: Add Direction to all decoding functions
uint8_t direction;
if (lich % 2 == 0) direction = 0;
else direction = 1;
//TODO Later: Add Direction and/or LICH to all decoding functions
if (scch) nxdn_deperm_scch (opts, state, sacch_bits, direction);
//TODO: SCCH handling -- direction will be needed
if (scch) fprintf (stderr, " SCCH ");
//TODO Much Later: FACCH3 and UDCH2 handling
if (facch3) fprintf (stderr, " FACCH3 ");
if (udch2) fprintf (stderr, " UDCH2 ");
if (udch2) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 0);
if (facch3) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 1);
if (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits);
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
//testing purposes -- don't run facch steals on enc -- causing issues with key loader (VCALL_ASSGN_DUP)
if (state->nxdn_cipher_type == 0 || state->R == 0) //!voice
{
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
if (facch & 2) nxdn_deperm_facch(opts, state, facch_bits_b);
}
//Seperated UDCH user data from facch2 data
if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0);
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1);
//SHOULD be okay to run facch1's again on steal frames, will need testing
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
if (facch & 2) nxdn_deperm_facch(opts, state, facch_bits_b);
if (voice)
{
@ -371,26 +417,29 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
//update last voice sync time
state->last_vc_sync_time = time(NULL);
//turn on scrambler if forced by user option
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
//process voice frame
nxdn_voice (opts, state, voice, dbuf);
}
//close MBE file if no voice and its open
if (!voice)
{
if (opts->mbe_out_f != NULL)
{
if (opts->frame_nxdn96 == 1) //nxdn96 has voice and data mixed together, so we will need to do a time check first
if (opts->frame_nxdn96 == 1) //nxdn96 has pure voice and data frames mixed together, so we will need to do a time check first
{
if ( (time(NULL) - state->last_vc_sync_time) > 1) //test for optimal time, 1 sec should be okay
{
closeMbeOutFile (opts, state);
}
}
if (opts->frame_nxdn48 == 1) closeMbeOutFile (opts, state); //okay to close right away if nxdn48, no data/voice mixing
if (opts->frame_nxdn48 == 1) closeMbeOutFile (opts, state); //okay to close right away if nxdn48, no data/voice frames mixing
}
}
if (voice && facch == 2) //facch steal 2 -- after voice
if (voice && facch == 2) //facch steal 2 -- after voice 1
{
//roll the voice scrambler LFSR here if key available to advance seed -- half rotation on a facch steal
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
@ -404,7 +453,8 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
}
}
if (opts->payload == 1 && !voice) fprintf (stderr, "\n");
else if (opts->payload == 0) fprintf (stderr, "\n");
fprintf (stderr, "\n");
END: ; //do nothing
}