NXDN Type-D Decoding/Trunking; NXDN Tweaks; #115
This commit is contained in:
parent
3101bbde28
commit
c507ed229c
|
|
@ -672,6 +672,7 @@ typedef struct
|
||||||
|
|
||||||
//multi-key array
|
//multi-key array
|
||||||
unsigned long long int rkey_array[0xFFFF];
|
unsigned long long int rkey_array[0xFFFF];
|
||||||
|
int keyloader; //let us know the keyloader is active
|
||||||
|
|
||||||
//dmr late entry mi
|
//dmr late entry mi
|
||||||
uint64_t late_entry_mi_fragment[2][7][3];
|
uint64_t late_entry_mi_fragment[2][7][3];
|
||||||
|
|
@ -857,10 +858,15 @@ void ncursesClose ();
|
||||||
//new NXDN Functions start here!
|
//new NXDN Functions start here!
|
||||||
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
||||||
void nxdn_descramble (uint8_t dibits[], int len);
|
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_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_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_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_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]);
|
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 crc12f(const uint8_t buf[], int len);
|
||||||
static uint16_t crc15(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 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 */
|
/* NXDN Convolution functions */
|
||||||
void CNXDNConvolution_start(void);
|
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_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_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);
|
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);
|
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -894,7 +894,8 @@ initState (dsd_state * state)
|
||||||
state->nxdn_bw = 0;
|
state->nxdn_bw = 0;
|
||||||
|
|
||||||
//multi-key array
|
//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
|
//Remus DMR End Call Alert Beep
|
||||||
state->dmr_end_alert[0] = 0;
|
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
|
opts.p25_trunk = 0; //turn off trunking mode if user enabled it
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'k': //NXDN multi-key loader
|
case 'k': //multi-key loader
|
||||||
strncpy(opts.key_in_file, optarg, 1023);
|
strncpy(opts.key_in_file, optarg, 1023);
|
||||||
opts.key_in_file[1023] = '\0';
|
opts.key_in_file[1023] = '\0';
|
||||||
csvKeyImport(&opts, &state);
|
csvKeyImport(&opts, &state);
|
||||||
|
state.keyloader = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Q': //'DSP' Structured Output file for OKDMRlib
|
case 'Q': //'DSP' Structured Output file for OKDMRlib
|
||||||
|
|
@ -1443,17 +1445,9 @@ main (int argc, char **argv)
|
||||||
|
|
||||||
case 'R':
|
case 'R':
|
||||||
sscanf (optarg, "%lld", &state.R);
|
sscanf (optarg, "%lld", &state.R);
|
||||||
if (state.R > 0x7FFF)
|
if (state.R > 0x7FFF) state.R = 0x7FFF;
|
||||||
{
|
//disable keyloader in case user tries to use this and it at the same time
|
||||||
state.R = 0x7FFF;
|
state.keyloader = 0;
|
||||||
}
|
|
||||||
// opts.dmr_mute_encL = 0;
|
|
||||||
// opts.dmr_mute_encR = 0;
|
|
||||||
if (state.R == 0)
|
|
||||||
{
|
|
||||||
// opts.dmr_mute_encL = 1;
|
|
||||||
// opts.dmr_mute_encR = 1;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'H':
|
case 'H':
|
||||||
|
|
|
||||||
|
|
@ -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
|
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);
|
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);
|
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, "[%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])
|
void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
|
||||||
{
|
{
|
||||||
//see about initializing these variables
|
//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];
|
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);
|
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);
|
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)
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n SACCH NSF ");
|
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]);
|
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 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 (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];
|
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;
|
if (sf == 3) part_of_frame = 0;
|
||||||
else if (sf == 2) part_of_frame = 1;
|
else if (sf == 2) part_of_frame = 1;
|
||||||
else if (sf == 1) part_of_frame = 2;
|
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);
|
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
|
||||||
else fprintf (stderr, " ");
|
else fprintf (stderr, " ");
|
||||||
fprintf (stderr, "%s", KNRM);
|
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
|
//reset scrambler seed to key value on new superframe
|
||||||
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
|
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
|
||||||
|
|
||||||
if (crc == check)
|
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_ran = state->nxdn_last_ran = ran;
|
||||||
state->nxdn_sf = sf;
|
state->nxdn_sf = sf;
|
||||||
state->nxdn_part_of_frame = part_of_frame;
|
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]);
|
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 deperm[500]; //348
|
||||||
uint8_t depunc[500]; //406
|
uint8_t depunc[500]; //406
|
||||||
uint8_t trellis_buf[400]; //199
|
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];
|
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
|
uint8_t f2u_message_buffer[400]; //199
|
||||||
memset (f2u_message_buffer, 0, sizeof(f2u_message_buffer));
|
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)
|
if (crc == check)
|
||||||
{
|
{
|
||||||
fprintf (stderr, " F2/U ");
|
if (type == 1) NXDN_Elements_Content_decode(opts, state, 1, f2u_message_buffer);
|
||||||
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)
|
if (opts->payload == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
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++)
|
for (int i = 0; i < 26; i++)
|
||||||
{
|
{
|
||||||
|
if (i == 13) fprintf (stderr, "\n ");
|
||||||
fprintf (stderr, "[%02X]", m_data[i]);
|
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])
|
void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
|
||||||
{
|
{
|
||||||
|
|
||||||
uint8_t deperm[500]; //300
|
uint8_t deperm[500]; //300
|
||||||
uint8_t depunc[500]; //350
|
uint8_t depunc[500]; //350
|
||||||
uint8_t trellis_buf[400]; //171
|
uint8_t trellis_buf[400]; //171
|
||||||
int id = 0;
|
int id = 0;
|
||||||
|
int ran = 0;
|
||||||
uint16_t crc = 0;
|
uint16_t crc = 0;
|
||||||
|
|
||||||
memset (deperm, 0, sizeof(deperm));
|
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];
|
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)
|
if (crc == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, " CAC ");
|
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
|
||||||
NXDN_Elements_Content_decode(opts, state, 1, cac_message_buffer);
|
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);
|
else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, cac_message_buffer);
|
||||||
|
|
||||||
if (opts->payload == 1)
|
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]);
|
fprintf (stderr, "[%02X]", m_data[i]);
|
||||||
if (i == 10) fprintf (stderr, "\n ");
|
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)
|
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);
|
fprintf (stderr, "%s", KYEL);
|
||||||
if (MessageType == 0x10) fprintf(stderr, " IDLE");
|
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 == 0x01) fprintf(stderr, " VCALL");
|
||||||
|
else if (MessageType == 0x02) fprintf(stderr, " VCALL_REC_REQ");
|
||||||
else if (MessageType == 0x03) fprintf(stderr, " VCALL_IV");
|
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 == 0x04) fprintf(stderr, " VCALL_ASSGN");
|
||||||
else if (MessageType == 0x05) fprintf(stderr, " VCALL_ASSGN_DUP");
|
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 == 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 == 0x18) fprintf(stderr, " SITE_INFO");
|
||||||
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO");
|
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO");
|
||||||
else if (MessageType == 0x1C) fprintf(stderr, " FAIL_STAT_INFO");
|
|
||||||
else if (MessageType == 0x1A) fprintf(stderr, " CCH_INFO");
|
else if (MessageType == 0x1A) fprintf(stderr, " CCH_INFO");
|
||||||
else if (MessageType == 0x1B) fprintf(stderr, " ADJ_SITE_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 == 0x20) fprintf(stderr, " REG_RESP");
|
||||||
else if (MessageType == 0x22) fprintf(stderr, " REG_C_RESP");
|
else if (MessageType == 0x22) fprintf(stderr, " REG_C_RESP");
|
||||||
else if (MessageType == 0x24) fprintf(stderr, " GRP_REG_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 == 0x38) fprintf(stderr, " SDCALL_REQ_HEADER");
|
||||||
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA");
|
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA");
|
||||||
else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP");
|
else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP");
|
||||||
|
|
||||||
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS");
|
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS");
|
||||||
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK");
|
|
||||||
else fprintf(stderr, " Unknown M-%02X", MessageType);
|
else fprintf(stderr, " Unknown M-%02X", MessageType);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
//on nxdn48 trunking -- zero out stale values so they won't persist after a transmit release, idle, or disconnect
|
//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 (opts->frame_nxdn48 == 1)
|
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;
|
||||||
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
|
state->nxdn_last_tg = 0;
|
||||||
state->nxdn_last_rid = 0;
|
if (state->M == 0) state->nxdn_cipher_type = 0;
|
||||||
state->nxdn_last_tg = 0;
|
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||||
state->nxdn_cipher_type = 0;
|
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));
|
sprintf (state->nxdn_call_type, "%s", "");
|
||||||
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", "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -701,4 +1045,23 @@ static uint16_t crc16cac(const uint8_t buf[], int len)
|
||||||
}
|
}
|
||||||
crc = crc ^ 0xffff;
|
crc = crc ^ 0xffff;
|
||||||
return 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);
|
||||||
}
|
}
|
||||||
|
|
@ -55,8 +55,6 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||||
MessageType |= (ElementsContent[6] & 1) << 1;
|
MessageType |= (ElementsContent[6] & 1) << 1;
|
||||||
MessageType |= (ElementsContent[7] & 1) << 0;
|
MessageType |= (ElementsContent[7] & 1) << 0;
|
||||||
|
|
||||||
nxdn_message_type (opts, state, MessageType);
|
|
||||||
|
|
||||||
/* Save the "F1" and "F2" flags */
|
/* Save the "F1" and "F2" flags */
|
||||||
state->NxdnElementsContent.F1 = ElementsContent[0];
|
state->NxdnElementsContent.F1 = ElementsContent[0];
|
||||||
state->NxdnElementsContent.F2 = ElementsContent[1];
|
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 */
|
/* Save the "Message Type" field */
|
||||||
state->NxdnElementsContent.MessageType = MessageType;
|
state->NxdnElementsContent.MessageType = MessageType;
|
||||||
|
|
||||||
|
/* Set the CRC state */
|
||||||
|
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
|
||||||
|
|
||||||
/* Decode the right "Message Type" */
|
/* Decode the right "Message Type" */
|
||||||
switch(MessageType)
|
switch(MessageType)
|
||||||
{
|
{
|
||||||
//VCALL_ASSGN
|
//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)
|
||||||
case 0x04:
|
|
||||||
//continue flow to DUP, both use same message format
|
|
||||||
|
|
||||||
//VCALL_ASSGN_DUP
|
//VCALL_ASSGN_DUP
|
||||||
case 0x05:
|
case 0x05:
|
||||||
|
|
||||||
|
//DCALL_ASSGN_DUP
|
||||||
|
case 0x0D:
|
||||||
|
|
||||||
|
//VCALL_ASSGN
|
||||||
|
case 0x04:
|
||||||
|
|
||||||
|
//DCALL_ASSGN
|
||||||
|
case 0x0E:
|
||||||
NXDN_decode_VCALL_ASSGN(opts, state, ElementsContent);
|
NXDN_decode_VCALL_ASSGN(opts, state, ElementsContent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//Alias 0x3F
|
//Alias 0x3F
|
||||||
case 0x3F:
|
case 0x3F:
|
||||||
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
|
|
||||||
NXDN_decode_Alias(opts, state, ElementsContent);
|
NXDN_decode_Alias(opts, state, ElementsContent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -102,9 +109,16 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||||
NXDN_decode_adj_site(opts, state, ElementsContent);
|
NXDN_decode_adj_site(opts, state, ElementsContent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
//VCALL, TX_REL_EXT and TX_REL
|
||||||
|
case 0x01:
|
||||||
|
case 0x07:
|
||||||
|
case 0x08:
|
||||||
|
NXDN_decode_VCALL(opts, state, ElementsContent);
|
||||||
|
break;
|
||||||
|
|
||||||
//DISC
|
//DISC
|
||||||
case 0x11:
|
case 0x11:
|
||||||
//NXDN_decode_VCALL(opts, state, ElementsContent);
|
NXDN_decode_VCALL(opts, state, ElementsContent);
|
||||||
|
|
||||||
//tune back to CC here - save about 1-2 seconds
|
//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)
|
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
|
//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_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
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;
|
opts->p25_is_tuned = 0;
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
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
|
//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_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
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;
|
opts->p25_is_tuned = 0;
|
||||||
rtl_udp_tune (opts, state, state->p25_cc_freq);
|
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
|
//Idle
|
||||||
case 0x10:
|
case 0x10:
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/* VCALL */
|
//VCALL_IV
|
||||||
case 0x01:
|
case 0x03:
|
||||||
{
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
NXDN_decode_VCALL_IV(opts, state, ElementsContent);
|
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;
|
break;
|
||||||
} /* End case NXDN_VCALL_IV: */
|
|
||||||
|
|
||||||
/* Unknown Message Type */
|
/* Unknown Message Type */
|
||||||
default:
|
default:
|
||||||
|
|
@ -201,6 +163,8 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
|
||||||
}
|
}
|
||||||
} /* End switch(MessageType) */
|
} /* End switch(MessageType) */
|
||||||
|
|
||||||
|
nxdn_message_type (opts, state, MessageType);
|
||||||
|
|
||||||
} /* End NXDN_Elements_Content_decode() */
|
} /* End NXDN_Elements_Content_decode() */
|
||||||
|
|
||||||
//externalize multiple sub-element handlers
|
//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 DuplexMode[32] = {0};
|
||||||
uint8_t TransmissionMode[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
|
//DFA specific variables
|
||||||
uint8_t bw = 0;
|
uint8_t bw = 0;
|
||||||
uint16_t OFN = 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);
|
OFN = (uint16_t)ConvertBitIntoBytes(&Message[64], 16);
|
||||||
IFN = (uint16_t)ConvertBitIntoBytes(&Message[80], 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" */
|
/* Print the "CC Option" */
|
||||||
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
|
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
|
||||||
if(CCOption & 0x40) fprintf(stderr, "Visitor ");
|
if(CCOption & 0x40) fprintf(stderr, "Visitor ");
|
||||||
if(CCOption & 0x20) fprintf(stderr, "Priority Paging ");
|
if(CCOption & 0x20) fprintf(stderr, "Priority Paging ");
|
||||||
|
|
||||||
/* Print the "Call Type" */
|
/* Print the "Call Type" */
|
||||||
fprintf (stderr, "%s", KGRN);
|
fprintf(stderr, "%s - ", NXDN_Call_Type_To_Str(CallType));
|
||||||
fprintf(stderr, "\n %s - ", NXDN_Call_Type_To_Str(CallType));
|
|
||||||
|
|
||||||
/* Print the "Voice Call Option" */
|
/* Print the "Voice Call Option" */
|
||||||
NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
|
if (MessageType == 0x4 || MessageType == 0x5) NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
|
||||||
fprintf(stderr, "%s %s - ", 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) */
|
/* Print Source ID and Destination ID (Talk Group or Unit ID) */
|
||||||
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
|
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
|
||||||
|
|
||||||
|
// if (idas) fprintf (stderr, "- Ch: %d ", rep1);
|
||||||
|
|
||||||
/* Print Channel */
|
/* Print Channel */
|
||||||
if (state->nxdn_rcn == 0)
|
if (state->nxdn_rcn == 0)
|
||||||
fprintf(stderr, "- Channel [%03X][%04d] ", Channel & 0x3FF, Channel & 0x3FF);
|
fprintf(stderr, "- Channel [%03X][%04d] ", Channel & 0x3FF, Channel & 0x3FF);
|
||||||
if (state->nxdn_rcn == 1)
|
if (state->nxdn_rcn == 1)
|
||||||
{
|
|
||||||
fprintf(stderr, "- DFA Channel [%04X][%05d] ", OFN, OFN);
|
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");
|
//test VCALL_ASSGN_DUP, if no voice sync activity in 1-2 seconds, then convert to assgn and allow tuning
|
||||||
// else if (bw == 1) fprintf (stderr, "BW: 12.5 kHz - 9600 bps");
|
//VCALL_ASSGN_DUP has been seen in the middle of calls, but also on the tail end instead of a TX_REL or DISC
|
||||||
// else fprintf (stderr, "BW: %d Reserved Value", bw);
|
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
|
//run process to figure out frequency value from the channel import or from DFA
|
||||||
long int freq = 0;
|
long int freq = 0;
|
||||||
if (state->nxdn_rcn == 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)
|
if (state->nxdn_rcn == 1)
|
||||||
freq = nxdn_channel_to_frequency(opts, state, OFN);
|
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
|
//check for control channel frequency in the channel map if not available
|
||||||
if (opts->p25_trunk == 1 && state->p25_cc_freq == 0)
|
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
|
//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 (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
|
//rigctl
|
||||||
if (opts->use_rigctl == 1)
|
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));
|
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||||
state->lastsynctype = -1;
|
state->lastsynctype = -1;
|
||||||
state->last_cc_sync_time = time(NULL);
|
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);
|
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
|
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
|
//set rid and tg when we actually tune to it
|
||||||
state->nxdn_last_rid = SourceUnitID & 0xFFFF;
|
state->nxdn_last_rid = SourceUnitID;
|
||||||
state->nxdn_last_tg = (DestinationID & 0xFFFF);
|
state->nxdn_last_tg = DestinationID;
|
||||||
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
|
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
|
//rtl_udp
|
||||||
else if (opts->audio_in_type == 3)
|
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;
|
opts->p25_is_tuned = 1;
|
||||||
|
|
||||||
//set rid and tg when we actually tune to it
|
//set rid and tg when we actually tune to it
|
||||||
state->nxdn_last_rid = SourceUnitID & 0xFFFF;
|
state->nxdn_last_rid = SourceUnitID;
|
||||||
state->nxdn_last_tg = (DestinationID & 0xFFFF);
|
state->nxdn_last_tg = DestinationID;
|
||||||
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
|
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);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
} /* End NXDN_decode_VCALL_ASSGN() */
|
} /* 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};
|
uint8_t TransmissionMode[32] = {0};
|
||||||
unsigned long long int FullMessage = 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" */
|
/* Decode "CC Option" */
|
||||||
CCOption = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
|
CCOption = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
|
||||||
state->NxdnElementsContent.CCOption = CCOption;
|
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);
|
DestinationID = (uint16_t)ConvertBitIntoBytes(&Message[40], 16);
|
||||||
state->NxdnElementsContent.DestinationID = DestinationID;
|
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" */
|
/* Decode the "Cipher Type" */
|
||||||
CipherType = (uint8_t)ConvertBitIntoBytes(&Message[56], 2);
|
CipherType = (uint8_t)ConvertBitIntoBytes(&Message[56], 2);
|
||||||
state->NxdnElementsContent.CipherType = CipherType;
|
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);
|
KeyID = (uint8_t)ConvertBitIntoBytes(&Message[58], 6);
|
||||||
state->NxdnElementsContent.KeyID = KeyID;
|
state->NxdnElementsContent.KeyID = KeyID;
|
||||||
|
|
||||||
|
fprintf (stderr, "\n ");
|
||||||
|
|
||||||
/* Print the "CC Option" */
|
/* Print the "CC Option" */
|
||||||
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
|
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
|
||||||
if(CCOption & 0x40) fprintf(stderr, "Visitor ");
|
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" */
|
/* Print the "Call Type" */
|
||||||
fprintf (stderr, "%s", KGRN);
|
fprintf (stderr, "%s - ", NXDN_Call_Type_To_Str(CallType));
|
||||||
fprintf(stderr, "\n %s - ", NXDN_Call_Type_To_Str(CallType));
|
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
|
||||||
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
|
|
||||||
|
|
||||||
/* Print the "Voice Call Option" */
|
/* Print the "Voice Call Option" */
|
||||||
NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
|
if (MessageType == 0x1) NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
|
||||||
fprintf(stderr, "%s %s - ", 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) */
|
/* Print Source ID and Destination ID (Talk Group or Unit ID) */
|
||||||
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
|
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
|
||||||
|
|
||||||
|
if (idas) fprintf (stderr, "- Ch: %d ", rep1);
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
//check the rkey array for a scrambler key value
|
//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];
|
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];
|
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),
|
//Don't zero key if no keyloader
|
||||||
//then we will always have a correct cipher type and won't need to
|
if (CipherType != 0x1 && state->keyloader == 1) state->R = 0;
|
||||||
//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
|
|
||||||
|
|
||||||
/* Print the "Cipher Type" */
|
/* Print the "Cipher Type" */
|
||||||
if(CipherType != 0)
|
if(CipherType != 0 && MessageType == 0x1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n %s", KYEL);
|
fprintf (stderr, "\n %s", KYEL);
|
||||||
fprintf(stderr, "%s - ", NXDN_Cipher_Type_To_Str(CipherType));
|
fprintf(stderr, "%s - ", NXDN_Cipher_Type_To_Str(CipherType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print the Key ID */
|
/* Print the Key ID */
|
||||||
if(CipherType != 0)
|
if(CipherType != 0 && MessageType == 0x1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Key ID %u - ", KeyID & 0xFF);
|
fprintf(stderr, "Key ID %u - ", KeyID & 0xFF);
|
||||||
fprintf (stderr, "%s", KNRM);
|
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, "%s", KYEL);
|
||||||
fprintf(stderr, "Value: %05lld", state->R);
|
fprintf(stderr, "Value: %05lld", state->R);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
//only grab if CRC is okay
|
//only grab if VCALL
|
||||||
if(state->NxdnElementsContent.VCallCrcIsGood)
|
if(MessageType == 0x1)
|
||||||
{
|
{
|
||||||
if ( (SourceUnitID & 0xFFFF) > 0 ) //
|
state->nxdn_last_rid = SourceUnitID;
|
||||||
{
|
state->nxdn_last_tg = DestinationID;
|
||||||
state->nxdn_last_rid = SourceUnitID & 0xFFFF;
|
state->nxdn_key = KeyID;
|
||||||
state->nxdn_last_tg = (DestinationID & 0xFFFF);
|
state->nxdn_cipher_type = CipherType;
|
||||||
state->nxdn_key = (KeyID & 0xFF);
|
|
||||||
state->nxdn_cipher_type = CipherType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf(stderr, "(CRC ERR) ");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//set enc bit here so we can tell playSynthesizedVoice whether or not to play enc traffic
|
//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;
|
uint32_t i;
|
||||||
state->payload_miN = 0; //zero out
|
state->payload_miN = 0; //zero out
|
||||||
|
uint64_t IV = 0;
|
||||||
/* 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);
|
|
||||||
|
|
||||||
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.PartOfCurrentEncryptedFrame = 2;
|
||||||
state->NxdnElementsContent.PartOfNextEncryptedFrame = 1;
|
state->NxdnElementsContent.PartOfNextEncryptedFrame = 1;
|
||||||
|
|
||||||
|
fprintf (stderr, "\n VCALL_IV: %016llX", state->payload_miN);
|
||||||
|
|
||||||
} /* End NXDN_decode_VCALL_IV() */
|
} /* 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)
|
char * NXDN_Call_Type_To_Str(uint8_t CallType)
|
||||||
{
|
{
|
||||||
|
|
@ -1107,14 +1477,14 @@ char * NXDN_Call_Type_To_Str(uint8_t CallType)
|
||||||
|
|
||||||
switch(CallType)
|
switch(CallType)
|
||||||
{
|
{
|
||||||
case 0: Ptr = "Broadcast Call"; break;
|
case 0: Ptr = "Broadcast Call"; break;
|
||||||
case 1: Ptr = "Group Call"; break;
|
case 1: Ptr = "Group Call"; break;
|
||||||
case 2: Ptr = "Unspecified Call"; break;
|
case 2: Ptr = "Transmission Release"; break; //"Unspecified Call" This value is used only on TX_REL message.
|
||||||
case 3: Ptr = "Reserved"; break;
|
case 3: Ptr = "Session Call"; break; //"reserved" is session call on Type D
|
||||||
case 4: Ptr = "Individual Call"; break;
|
case 4: Ptr = "Private Call"; break;
|
||||||
case 5: Ptr = "Reserved"; break;
|
case 5: Ptr = "Reserved"; break;
|
||||||
case 6: Ptr = "Interconnect Call"; break;
|
case 6: Ptr = "PSTN Interconnect Call"; break;
|
||||||
case 7: Ptr = "Speed Dial Call"; break;
|
case 7: Ptr = "PSTN Speed Dial Call"; break;
|
||||||
default: Ptr = "Unknown Call Type"; break;
|
default: Ptr = "Unknown Call Type"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
162
src/nxdn_frame.c
162
src/nxdn_frame.c
|
|
@ -32,16 +32,17 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||||
// length is implicitly 192, with frame sync in first 10 dibits
|
// length is implicitly 192, with frame sync in first 10 dibits
|
||||||
uint8_t dbuf[182];
|
uint8_t dbuf[182];
|
||||||
uint8_t lich;
|
uint8_t lich;
|
||||||
int answer_len=0;
|
int answer_len = 0;
|
||||||
uint8_t answer[32];
|
uint8_t answer[32];
|
||||||
uint8_t sacch_answer[32];
|
uint8_t sacch_answer[32];
|
||||||
int lich_parity_received;
|
int lich_parity_received;
|
||||||
int lich_parity_computed;
|
int lich_parity_computed;
|
||||||
int voice=0;
|
int voice = 0;
|
||||||
int facch=0;
|
int facch = 0;
|
||||||
int facch2=0;
|
int facch2 = 0;
|
||||||
int sacch=0;
|
int udch = 0;
|
||||||
int cac=0;
|
int sacch = 0;
|
||||||
|
int cac = 0;
|
||||||
int sr_structure;
|
int sr_structure;
|
||||||
int sr_ran;
|
int sr_ran;
|
||||||
|
|
||||||
|
|
@ -51,12 +52,19 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||||
int facch3 = 0;
|
int facch3 = 0;
|
||||||
int udch2 = 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 lich_dibits[8];
|
||||||
uint8_t sacch_bits[60];
|
uint8_t sacch_bits[60];
|
||||||
uint8_t facch_bits_a[144];
|
uint8_t facch_bits_a[144];
|
||||||
uint8_t facch_bits_b[144];
|
uint8_t facch_bits_b[144];
|
||||||
uint8_t cac_bits[300];
|
uint8_t cac_bits[300];
|
||||||
uint8_t facch2_bits[348]; //facch2 or udch, same amount of bits
|
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
|
//nxdn bit buffer, for easy assignment handling
|
||||||
int nxdn_bit_buffer[364];
|
int nxdn_bit_buffer[364];
|
||||||
|
|
@ -89,7 +97,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||||
lich = lich >> 1;
|
lich = lich >> 1;
|
||||||
if (lich_parity_received != lich_parity_computed)
|
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;
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,7 +112,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||||
if (lich % 2 == 0 && opts->p25_trunk == 1)
|
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);
|
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;
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,13 +123,15 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||||
break;
|
break;
|
||||||
case 0x28: //facch2 types
|
case 0x28: //facch2 types
|
||||||
case 0x29:
|
case 0x29:
|
||||||
case 0x2e:
|
|
||||||
case 0x2f:
|
|
||||||
case 0x48:
|
case 0x48:
|
||||||
case 0x49:
|
case 0x49:
|
||||||
|
facch2 = 1;
|
||||||
|
break;
|
||||||
|
case 0x2e: //udch types
|
||||||
|
case 0x2f:
|
||||||
case 0x4e:
|
case 0x4e:
|
||||||
case 0x4f:
|
case 0x4f:
|
||||||
facch2 = 1;
|
udch = 1;
|
||||||
break;
|
break;
|
||||||
case 0x32: //facch in 1, vch in 2
|
case 0x32: //facch in 1, vch in 2
|
||||||
case 0x33:
|
case 0x33:
|
||||||
|
|
@ -134,7 +144,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||||
case 0x34: //vch in 1, facch in 2
|
case 0x34: //vch in 1, facch in 2
|
||||||
case 0x35:
|
case 0x35:
|
||||||
case 0x54:
|
case 0x54:
|
||||||
case 0x55:
|
case 0x55: //disabled for testing, IDAS system randomly triggers this one, probably due to poor signal
|
||||||
voice = 1;
|
voice = 1;
|
||||||
facch = 2;
|
facch = 2;
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
|
|
@ -155,7 +165,6 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||||
case 0x41:
|
case 0x41:
|
||||||
case 0x50:
|
case 0x50:
|
||||||
case 0x51:
|
case 0x51:
|
||||||
|
|
||||||
voice = 0;
|
voice = 0;
|
||||||
facch = 3;
|
facch = 3;
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
|
|
@ -166,47 +175,52 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//NXDN "Type-D" or "IDAS" Specific Lich Codes
|
//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:
|
case 0x77:
|
||||||
idas = 1;
|
idas = 1;
|
||||||
scch = 1;
|
scch = 1;
|
||||||
voice = 3;
|
voice = 3;
|
||||||
break;
|
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:
|
case 0x75:
|
||||||
idas = 1;
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
voice = 1;
|
voice = 1;
|
||||||
scch = 2;
|
facch = 2;
|
||||||
break;
|
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:
|
case 0x73:
|
||||||
idas = 1;
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
voice = 2;
|
voice = 2;
|
||||||
scch = 2;
|
facch = 1;
|
||||||
break;
|
break;
|
||||||
case 0x70: //scch in 1 and 2
|
case 0x70: //facch steal in vch 1 and vch 2 (during voice only)
|
||||||
case 0x71:
|
case 0x71:
|
||||||
idas = 1;
|
idas = 1;
|
||||||
scch = 4; //total of three scch positions
|
scch = 1;
|
||||||
|
facch = 3;
|
||||||
break;
|
break;
|
||||||
case 0x6E: //udch2
|
case 0x6E: //udch2
|
||||||
case 0x6F:
|
case 0x6F:
|
||||||
idas = 1;
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
udch2 = 1;
|
udch2 = 1;
|
||||||
break;
|
break;
|
||||||
case 0x68:
|
case 0x68:
|
||||||
case 0x69: //facch3
|
case 0x69: //facch3
|
||||||
idas = 1;
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
facch3 = 1;
|
facch3 = 1;
|
||||||
break;
|
break;
|
||||||
case 0x62:
|
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;
|
idas = 1;
|
||||||
scch = 1;
|
scch = 1;
|
||||||
facch = 1;
|
facch = 1;
|
||||||
break;
|
break;
|
||||||
case 0x60:
|
case 0x60:
|
||||||
case 0x61: //facch1 in both
|
case 0x61: //facch1 in both (non vch)
|
||||||
idas = 1;
|
idas = 1;
|
||||||
scch = 1;
|
scch = 1;
|
||||||
facch = 3;
|
facch = 3;
|
||||||
|
|
@ -230,7 +244,8 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
printFrameSync (opts, state, "IDAS D ", 0, "-");
|
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)
|
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, "-");
|
printFrameSync (opts, state, "NXDN48 ", 0, "-");
|
||||||
}
|
}
|
||||||
else printFrameSync (opts, state, "NXDN96 ", 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
|
//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;
|
nxdn_bit_buffer[i*2+1] = dbuf[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//sacch
|
//sacch or scch bits
|
||||||
for (int i = 0; i < 60; i++)
|
for (int i = 0; i < 60; i++)
|
||||||
{
|
{
|
||||||
sacch_bits[i] = nxdn_bit_buffer[i+16];
|
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];
|
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
|
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
|
||||||
//sacch needs extra handling depending on superframe or non-superframe variety
|
//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, "%s", KGRN);
|
||||||
fprintf (stderr, " Voice ");
|
fprintf (stderr, " Voice ");
|
||||||
fprintf (stderr, "%s", KNRM);
|
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, "%s", KGRN);
|
||||||
fprintf (stderr, " V%d+F%d ", 3 - facch, facch); //print which position on each
|
fprintf (stderr, " V%d+F%d ", 3 - facch, facch); //print which position on each
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
else if (voice && scch > 1) //voice with scch steal
|
else //Covers FACCH1 in both, FACCH2, UDCH, UDCH2, CAC
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KGRN);
|
|
||||||
fprintf (stderr, " V%d+S%d ", 4 - scch, scch); //print which position on each
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KCYN);
|
fprintf (stderr, "%s", KCYN);
|
||||||
fprintf (stderr, " Data ");
|
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;
|
if (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) state->nxdn_sacch_non_superframe = TRUE;
|
||||||
else state->nxdn_sacch_non_superframe = FALSE;
|
else state->nxdn_sacch_non_superframe = FALSE;
|
||||||
|
|
||||||
//TODO Later: Add Direction to all decoding functions
|
//TODO Later: Add Direction and/or LICH to all decoding functions
|
||||||
uint8_t direction;
|
if (scch) nxdn_deperm_scch (opts, state, sacch_bits, direction);
|
||||||
if (lich % 2 == 0) direction = 0;
|
|
||||||
else direction = 1;
|
|
||||||
|
|
||||||
//TODO: SCCH handling -- direction will be needed
|
if (udch2) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 0);
|
||||||
if (scch) fprintf (stderr, " SCCH ");
|
if (facch3) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 1);
|
||||||
|
|
||||||
//TODO Much Later: FACCH3 and UDCH2 handling
|
|
||||||
if (facch3) fprintf (stderr, " FACCH3 ");
|
|
||||||
if (udch2) fprintf (stderr, " UDCH2 ");
|
|
||||||
|
|
||||||
if (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
|
if (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
|
||||||
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
|
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
|
||||||
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits);
|
|
||||||
|
|
||||||
//testing purposes -- don't run facch steals on enc -- causing issues with key loader (VCALL_ASSGN_DUP)
|
//Seperated UDCH user data from facch2 data
|
||||||
if (state->nxdn_cipher_type == 0 || state->R == 0) //!voice
|
if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0);
|
||||||
{
|
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1);
|
||||||
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
|
|
||||||
if (facch & 2) nxdn_deperm_facch(opts, state, facch_bits_b);
|
//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)
|
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);
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
//update last voice sync time
|
//update last voice sync time
|
||||||
state->last_vc_sync_time = time(NULL);
|
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
|
//process voice frame
|
||||||
nxdn_voice (opts, state, voice, dbuf);
|
nxdn_voice (opts, state, voice, dbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
//close MBE file if no voice and its open
|
//close MBE file if no voice and its open
|
||||||
if (!voice)
|
if (!voice)
|
||||||
{
|
{
|
||||||
if (opts->mbe_out_f != NULL)
|
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
|
if ( (time(NULL) - state->last_vc_sync_time) > 1) //test for optimal time, 1 sec should be okay
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
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
|
//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)
|
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
|
END: ; //do nothing
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue