NXDN CAC/CCH Messages; Initial Trunking Support;

This commit is contained in:
lwvmobile 2022-11-17 05:50:14 -05:00
parent eefe1bac5e
commit 9f624e0b62
8 changed files with 827 additions and 301 deletions

View File

@ -645,7 +645,12 @@ typedef struct
uint8_t nxdn_sacch_frame_segment[4][18]; //part of frame by 18 bits
uint8_t nxdn_sacch_frame_segcrc[4];
uint8_t nxdn_alias_block_number;
char nxdn_alias_block_segment[4][4][8];
char nxdn_alias_block_segment[4][4][8];
//site/srv/cch info
char nxdn_location_category[14];
uint32_t nxdn_location_sys_code;
uint16_t nxdn_location_site_code;
//Roman DMR End Call Alert Beep
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag

View File

@ -182,11 +182,6 @@ noCarrier (dsd_opts * opts, dsd_state * state)
state->payload_keyid = 0;
state->payload_keyidR = 0;
// state->dmr_fid = 0;
// state->dmr_so = 0;
// state->dmr_fidR = 0;
// state->dmr_soR = 0;
state->HYTL = 0;
state->HYTR = 0;
state->DMRvcL = 0;
@ -228,9 +223,6 @@ noCarrier (dsd_opts * opts, dsd_state * state)
state->data_p_head[0] = 0;
state->data_p_head[1] = 0;
// state->dmr_so = 0; //let TLC or Voice LC/Burst zero or set this instead?
// state->dmr_soR = 0;
state->dmr_encL = 0;
state->dmr_encR = 0;
@ -259,6 +251,7 @@ noCarrier (dsd_opts * opts, dsd_state * state)
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
state->nxdn_alias_block_number = 0;
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
sprintf (state->nxdn_call_type, "%s", "");
}
@ -710,6 +703,10 @@ initState (dsd_state * state)
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
state->nxdn_alias_block_number = 0;
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
//site/srv/cch info
state->nxdn_location_site_code = 0;
state->nxdn_location_sys_code = 0;
sprintf (state->nxdn_location_category, "%s", " ");
//Roman DMR End Call Alert Beep
state->dmr_end_alert[0] = 0;
@ -839,7 +836,7 @@ usage ()
printf (" (See lcn.csv for example)\n");
printf (" -2 <file> Import Group List Allow/Block and Label from csv file (numeral 'two')\n");
printf (" (See group.csv for example)\n");
printf (" -3 Enable Experimental Trunking Features (P25/EDACS for now) with RIGCTL/TCP or RTL Input\n");
printf (" -3 Enable Extremely Experimental Trunking Features (NXDN/P25/EDACS for now) with RIGCTL/TCP or RTL Input\n");
printf (" -5 <udp p> Enable RIGCTL/TCP; Set UDP Port for RIGCTL. (4532 on SDR++)\n");
//printf (" (Currently only available on UDP port 4532)\n");
printf ("\n");

View File

@ -2125,15 +2125,50 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
//NXDN
//if (lls == 8 || lls == 9 || lls == 16 || lls == 17 || lls == 28 || lls == 29)
if (lls == 28 || lls == 29)
{
printw ("| RAN: [%2d] ", rn);
if (opts->p25_trunk == 1)
{
printw ("| ");
if (opts->p25_is_tuned == 0)
{
printw ("Monitoring Control Channel");
if (state->p25_cc_freq != 0)
{
printw (" - CC Freq: [%.06lf] Mhz ", (double)state->p25_cc_freq/1000000);
}
}
else if (opts->p25_is_tuned == 1)
{
printw ("Monitoring Voice Channel");
if (state->p25_vc_freq[0] != 0)
{
printw (" - VC Freq: [%.06lf] Mhz ", (double)state->p25_vc_freq[0]/1000000);
}
}
printw ("\n");
}
printw ("| ");
printw ("NXDN - RAN: [%2d] ", rn);
if (state->nxdn_location_site_code != 0)
{
printw ("Cat [%s] ", state->nxdn_location_category);
printw ("Sys Code [%d] ", state->nxdn_location_sys_code);
printw ("Site Code [%d] ", state->nxdn_location_site_code);
}
printw ("\n");
printw ("| ");
printw ("TGT: [%4d] ", tgn);
printw ("SRC: [%4d] ", src);
if (state->nxdn_alias_block_segment[0][0] > 0) //could not display if this segment is bad
if (state->nxdn_alias_block_segment[0][0] > 0)
{
printw ("[");
printw ("ALIAS: [");
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
@ -2143,10 +2178,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
printw ("]");
}
printw ("\n| ALG: [0x%02X] KEY: [0x%02X] ", state->nxdn_cipher_type, state->nxdn_key);
if (state->carrier == 1)
{
printw("%s ", state->nxdn_call_type);
printw(" %s ", state->nxdn_call_type);
}
printw ("\n|");
if (state->nxdn_cipher_type > 0)
{
printw (" ALG: [0x%02X] KEY: [0x%02X] ", state->nxdn_cipher_type, state->nxdn_key);
}
if (state->nxdn_cipher_type == 0x1 && state->carrier == 1)
{
@ -2156,7 +2195,6 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
attron(COLOR_PAIR(3));
if (state->R != 0)
{
//printw ("\n| ");
attron(COLOR_PAIR(1));
printw ("KEY VALUE: [%05lld] ", state->R );
printw ("SEED: [%04llX]", state->payload_miN);
@ -2165,6 +2203,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
if (state->nxdn_cipher_type == 0x2 && state->carrier == 1)
{
printw ("IV: [%016llX]", state->payload_miN);
attron(COLOR_PAIR(2));
printw ("DES-OFB ");
attroff(COLOR_PAIR(2));
@ -2172,6 +2211,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
if (state->nxdn_cipher_type == 0x3 && state->carrier == 1)
{
printw ("IV: [%016llX]", state->payload_miN);
attron(COLOR_PAIR(2));
printw ("AES-256 ");
attroff(COLOR_PAIR(2));
@ -2184,11 +2224,10 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
attroff(COLOR_PAIR(2));
attron(COLOR_PAIR(3));
}
//printw("%s ", state->nxdn_call_type);
printw("\n");
}
//DMR BS/MS Voice and Data Types
//P25 and DMR BS/MS
if ( lls == 0 || lls == 1 || lls == 12 || lls == 13 || lls == 10 ||
lls == 11 || lls == 32 || lls == 33 || lls == 34 || lls == 35 || lls == 36)
{
@ -2738,10 +2777,32 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
}
refresh();
if (c == 27) //esc key
//keyboard shortcuts - codes same as ascii codes
if (c == 27) //esc key, open menu
{
ncursesMenu (opts, state); //just a quick test
}
if (c == 99) //'c' key, toggle compact mode
{
if (opts->ncurses_compact == 1) opts->ncurses_compact = 0;
else opts->ncurses_compact = 1;
}
if (c == 116) //'t' key, toggle trunking
{
if (opts->p25_trunk == 1) opts->p25_trunk = 0;
else opts->p25_trunk = 1;
}
if (c == 97) //'a' key, toggle call alert beep
{
if (opts->call_alert == 1) opts->call_alert = 0;
else opts->call_alert = 1;
}
if (c == 104) //'h' key, toggle history
{
if (opts->ncurses_history == 1) opts->ncurses_history = 0;
else opts->ncurses_history = 1;
}
} //end ncursesPrinter

View File

@ -59,17 +59,6 @@ static uint64_t * m_dp = NULL;
/* Functions ----------------------------------------------------------------*/
void CNXDNConvolution_start(void)
{
memset(m_metrics1, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
memset(m_metrics2, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
m_oldMetrics = m_metrics1;
m_newMetrics = m_metrics2;
m_dp = m_decisions;
}
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1)
{
uint8_t i = 0;
@ -167,3 +156,15 @@ void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsign
}
}
void CNXDNConvolution_start(void)
{
memset(m_metrics1, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
memset(m_metrics2, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
m_oldMetrics = m_metrics1;
m_newMetrics = m_metrics2;
m_dp = m_decisions;
for (int x = 0; x < 4; x++) CNXDNConvolution_decode(0U, 0U);
}

View File

@ -1,5 +1,6 @@
//NXDN descramble/deperm/depuncture and utility functions
//Reworked portions from Osmocom OP25
/* -*- c++ -*- */
/*
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
@ -52,24 +53,65 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
{
uint8_t deperm[144];
uint8_t depunc[192];
uint8_t trellis_buf[92];
uint8_t trellis_buf[96];
uint16_t crc = 0; //crc calculated by function
uint16_t check = 0; //crc from payload for comparison
int out;
char buf[128];
uint8_t answer[12];
for (int i=0; i<144; i++)
deperm[PERM_16_9[i]] = bits[i];
out = 0;
for (int i=0; i<144; i+=3) {
depunc[out++] = deperm[i+0];
depunc[out++] = 0; //0
depunc[out++] = 0;
depunc[out++] = deperm[i+1];
depunc[out++] = deperm[i+2];
}
trellis_decode(trellis_buf, depunc, 92);
//trellis_decode(trellis_buf, depunc, 92);
//switch to the convolutional decoder
uint8_t temp[210];
uint8_t s0;
uint8_t s1;
uint8_t m_data[13];
memset (temp, 0, sizeof(temp));
memset (m_data, 0, sizeof(m_data));
memset (trellis_buf, 0, sizeof(trellis_buf));
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;
}
//load tail 16 bits into check variable to compare vs computed crc value
for (int i = 0; i < 12; i++)
@ -79,33 +121,31 @@ void nxdn_deperm_facch(dsd_opts * opts, dsd_state * state, uint8_t bits[144])
}
crc = crc12f (trellis_buf, 80);
cfill(answer, trellis_buf, 12); //<-92 bits in 12 bytes, leaves 4 bits on tail to scrub off
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
//else if (opts->payload == 1) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
if (opts->payload == 1)
{
fprintf (stderr, "\n");
fprintf (stderr, " FACCH Payload ");
answer[11] = answer[11] & 0xF0; //scrub the garbage 4 bits from cfill on the end for ppayload print
for (int i = 0; i < 12; i++)
{
fprintf (stderr, "[%02X]", answer[i]);
fprintf (stderr, "[%02X]", m_data[i]);
}
fprintf (stderr, " - %03X %03X", crc, check);
}
if (crc == check) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
//only run data with bad CRC when payload is enabled and hide the ugly
else if (opts->payload == 1) NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
}
//new sacch
void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
{
uint8_t deperm[60];
uint8_t depunc[72];
uint8_t trellis_buf[32];
uint8_t answer[26];
int o=0;
int o = 0;
uint8_t crc = 0; //value computed by crc6 on payload
uint8_t check = 0; //value pulled from last 6 bits
int sf = 0;
@ -120,16 +160,60 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
depunc[o++] = deperm[p+2];
depunc[o++] = deperm[p+3];
depunc[o++] = deperm[p+4];
depunc[o++] = 0; //0
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; //0
depunc[o++] = 0;
}
trellis_decode(trellis_buf, depunc, 32);
//trellis_decode(trellis_buf, depunc, 32);
//switch to the convolutional decoder
uint8_t temp[90];
uint8_t s0;
uint8_t s1;
uint8_t m_data[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 < 40U; i++)
{
s0 = temp[(2*i)];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
//stored as 5 bytes, will need to convert to trellis_buf after running
CNXDNConvolution_chainback(m_data, 36U);
for(int i = 0; i < 4; i++)
{
trellis_buf[(i*8)+0] = (m_data[i] >> 7) & 1;
trellis_buf[(i*8)+1] = (m_data[i] >> 6) & 1;
trellis_buf[(i*8)+2] = (m_data[i] >> 5) & 1;
trellis_buf[(i*8)+3] = (m_data[i] >> 4) & 1;
trellis_buf[(i*8)+4] = (m_data[i] >> 3) & 1;
trellis_buf[(i*8)+5] = (m_data[i] >> 2) & 1;
trellis_buf[(i*8)+6] = (m_data[i] >> 1) & 1;
trellis_buf[(i*8)+7] = (m_data[i] >> 0) & 1;
}
crc = crc6(trellis_buf, 32);
for (int i = 0; i < 6; i++)
@ -138,59 +222,89 @@ void nxdn_deperm_sacch(dsd_opts * opts, dsd_state * state, uint8_t bits[60])
check = check + trellis_buf[i+26];
}
//sf and ran together are denoted as SR in the manual (more confusing acronyms)
//sf (structure field) and RAN will always exist in first 8 bits of each SACCH, then the next 18 bits are the fragment of the superframe
sf = (trellis_buf[0] << 1) | trellis_buf[1];
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
if (sf == 3) part_of_frame = 0;
else if (sf == 2) part_of_frame = 1;
else if (sf == 1) part_of_frame = 2;
else if (sf == 0) part_of_frame = 3;
else part_of_frame = 0;
//gives unusual results when non_superframe SF, not sure why, or if its accurate yet
if (state->nxdn_sacch_non_superframe == FALSE)
//FIRST! If part of a non_superframe, and CRC is good, send directly to NXDN_Elements_Content_decode
if (state->nxdn_sacch_non_superframe == TRUE)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
//fprintf (stderr, " RAN %02d - SF %01d - POF %02d", ran, sf, part_of_frame);
fprintf (stderr, "%s", KNRM);
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
else fprintf (stderr, " ");
uint8_t nsf_sacch[26];
memset (nsf_sacch, 0, sizeof(nsf_sacch));
for (int i = 0; i < 26; i++)
{
nsf_sacch[i] = trellis_buf[i+8];
}
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, nsf_sacch);
if (opts->payload == 1)
{
fprintf (stderr, "\n SACCH NSF ");
for (int i = 0; i < 5; i++)
{
fprintf (stderr, "[%02X]", m_data[i]);
}
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
}
}
else fprintf (stderr, " ");
//reset scrambler seed to key value on new superframe
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
if (crc == 0 && state->nxdn_sacch_non_superframe == FALSE) //only set these when superframe type, or get odd results (can't verify correct)
//If part of superframe, collect the fragments and send to NXDN_SACCH_Full_decode instead
else if (state->nxdn_sacch_non_superframe == FALSE)
{
state->nxdn_ran = state->nxdn_last_ran = ran;
state->nxdn_sf = sf;
state->nxdn_part_of_frame = part_of_frame;
state->nxdn_sacch_frame_segcrc[part_of_frame] = 0; //zero indicates good check
}
else state->nxdn_sacch_frame_segcrc[part_of_frame] = 1; //1 indicates bad check
//sf and ran together are denoted as SR in the manual (more confusing acronyms)
//sf (structure field) and RAN will always exist in first 8 bits of each SACCH, then the next 18 bits are the fragment of the superframe
sf = (trellis_buf[0] << 1) | trellis_buf[1];
ran = (trellis_buf[2] << 5) | (trellis_buf[3] << 4) | (trellis_buf[4] << 3) | (trellis_buf[5] << 2) | (trellis_buf[6] << 1) | trellis_buf[7];
if (sf == 3) part_of_frame = 0;
else if (sf == 2) part_of_frame = 1;
else if (sf == 1) part_of_frame = 2;
else if (sf == 0) part_of_frame = 3;
else part_of_frame = 0;
int sacch_segment = 0;
fprintf (stderr, "%s", KCYN);
if (state->nxdn_last_ran != -1) fprintf (stderr, " RAN %02d ", state->nxdn_last_ran);
else fprintf (stderr, " ");
fprintf (stderr, "%s", KNRM);
//reset scrambler seed to key value on new superframe
if (part_of_frame == 0 && state->nxdn_cipher_type == 0x1) state->payload_miN = 0;
for (int i = 0; i < 18; i++)
{
sacch_segment = sacch_segment << 1;
sacch_segment = sacch_segment + trellis_buf[i+8];
state->nxdn_sacch_frame_segment[part_of_frame][i] = trellis_buf[i+8];
}
if (crc == 0)
{
state->nxdn_ran = state->nxdn_last_ran = ran;
state->nxdn_sf = sf;
state->nxdn_part_of_frame = part_of_frame;
state->nxdn_sacch_frame_segcrc[part_of_frame] = 0; //zero indicates good check
}
else state->nxdn_sacch_frame_segcrc[part_of_frame] = 1; //1 indicates bad check
//Hand off to LEH NXDN SACCH handling
if (part_of_frame == 3 && state->nxdn_sacch_non_superframe == FALSE)
{
NXDN_SACCH_Full_decode (opts, state);
}
int sacch_segment = 0;
if (opts->payload == 1)
{
fprintf (stderr, "\n");
fprintf (stderr, " SACCH Segment #%d - %05X", part_of_frame+1, sacch_segment);
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
}
for (int i = 0; i < 18; i++)
{
sacch_segment = sacch_segment << 1;
sacch_segment = sacch_segment + trellis_buf[i+8];
state->nxdn_sacch_frame_segment[part_of_frame][i] = trellis_buf[i+8];
}
//Hand off to LEH NXDN_SACCH_Full_decode
if (part_of_frame == 3)
{
NXDN_SACCH_Full_decode (opts, state);
}
if (opts->payload == 1)
{
fprintf (stderr, "\n");
fprintf (stderr, " SACCH SF Segment #%d ", part_of_frame+1);
for (int i = 0; i < 5; i++)
{
fprintf (stderr, "[%02X]", m_data[i]);
}
if (crc != 0) fprintf (stderr, " CRC ERR - %02X %02X", crc, check);
}
}
}
@ -222,23 +336,68 @@ void nxdn_deperm_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t bits[34
depunc[id++] = deperm[i*12+10];
depunc[id++] = deperm[i*12+11];
}
trellis_decode(trellis_buf, depunc, 199);
crc = crc15(trellis_buf, 199);
//trellis_decode(trellis_buf, depunc, 199);
uint8_t MessageType = 0;
for (int i = 0; i < 6; i++)
//switch to the convolutional decoder
uint8_t temp[220];
uint8_t s0;
uint8_t s1;
uint8_t m_data[26];
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
for (int i = 0; i < 406; i++)
{
MessageType = MessageType << 1;
MessageType = MessageType + trellis_buf[i+2]; //on +2 on any facch1
temp[i] = depunc[i] << 1;
}
fprintf (stderr, " FACCH2/UDCH ");
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
else
for (int i = 0; i < 8; i++)
{
fprintf (stderr, "CRC ERR ");
//NXDN_Elements_Content_decode(opts, state, 0, trellis_buf);
}
temp[i+203] = 0;
}
CNXDNConvolution_start();
for (int i = 0U; i < 207U; i++)
{
s0 = temp[(2*i)];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
//numerals seem okay now
CNXDNConvolution_chainback(m_data, 203U);
for(int i = 0; i < 26; 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 = crc15(trellis_buf, 199);
fprintf (stderr, " F2/U ");
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, trellis_buf);
if (opts->payload == 1)
{
fprintf (stderr, "\n");
fprintf (stderr, " F2/U Payload\n ");
for (int i = 0; i < 26; i++)
{
fprintf (stderr, "[%02X]", m_data[i]);
if (i == 12) fprintf (stderr, "\n ");
}
if (crc != 0) fprintf (stderr, " CRC ERR ");
}
}
@ -247,7 +406,7 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
uint8_t deperm[300];
uint8_t depunc[350];
uint8_t decode[171];
uint8_t trellis_buf[171];
int id = 0;
uint16_t crc = 0;
@ -270,57 +429,108 @@ void nxdn_deperm_cac(dsd_opts * opts, dsd_state * state, uint8_t bits[300])
depunc[id++] = deperm[i*12+10];
depunc[id++] = deperm[i*12+11];
}
trellis_decode(decode, depunc, 171);
crc = crc16cac(decode, 171);
fprintf (stderr, " CAC ");
//these messages can also be double depending on CAC type, so need to check that first, and split the
//decode first, then we can run each message type and element decode if necessary
//trellis_decode(trellis_buf, depunc, 171);
//switch to the convolutional decoder
uint8_t temp[360];
uint8_t s0;
uint8_t s1;
uint8_t m_data[26];
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
for (int i = 0; i < 350; i++)
{
temp[i] = depunc[i] << 1;
}
for (int i = 0; i < 8; i++)
{
temp[i+350] = 0;
}
CNXDNConvolution_start();
for (int i = 0U; i < 179U; i++)
{
s0 = temp[(2*i)];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 175U);
for(int i = 0; i < 22; 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 = crc16cac(trellis_buf, 171);
fprintf (stderr, " CAC ");
//message type will probably be neccesary beforehand on single/dual meessage runs
//run message, check len, load into a seperate buffer, send to element, do same with other piece?
uint8_t MessageType = 0;
for (int i = 0; i < 6; i++)
{
MessageType = MessageType << 1;
MessageType = MessageType + decode[i+2]; //double check this on CAC
MessageType = MessageType + trellis_buf[i+2]; //double check this on CAC
}
//nxdn_message_type (opts, state, MessageType);
uint8_t cac_message_buffer[171];
memset (cac_message_buffer, 0, sizeof(cac_message_buffer));
//not sure if CAC will contain same type of 'elements' as a FACCH or SACCH
//or if its purely for Control Signalling
//need to check CAC type, CAC can contain two messages, or one long message,
//then we need to break up the messages and process them individually
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, decode);
else
//shift the cac_message into the appropriate byte arrangement for element_decoder
for (int i = 0; i < 160; i++) //in future, we can use this to send multiple messages to decoder, if present
{
fprintf (stderr, " CRC ERR ");
//NXDN_Elements_Content_decode(opts, state, 0, decode);
cac_message_buffer[i] = trellis_buf[i+8];
}
if (crc == 0) NXDN_Elements_Content_decode(opts, state, 1, cac_message_buffer);
if (opts->payload == 1)
{
fprintf (stderr, "\n");
fprintf (stderr, " CAC Payload\n ");
for (int i = 0; i < 22; i++)
{
fprintf (stderr, "[%02X]", m_data[i]);
if (i == 10) fprintf (stderr, "\n ");
}
if (crc != 0) fprintf (stderr, " CRC ERR ");
}
}
//its own function now to consolidate all the message types to one area to keep track of
//may need to add a 'source' of message so we don't call messages not associated with cac, facch, sacch, etc
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
{
//if (opts->payload == 1) fprintf (stderr, " M%02X ", MessageType);
fprintf (stderr, "%s", KYEL);
if (MessageType == 0x10) fprintf(stderr, " IDLE");
else if (MessageType == 0x11) fprintf(stderr, " DISC"); //disconnect
else if (MessageType == 0x01) fprintf(stderr, " VCALL");
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");
//CAC only? valid in all, or just CAC?
else if (MessageType == 0x04) fprintf(stderr, " VCALL ASSGN");
else if (MessageType == 0x0E) fprintf(stderr, " DCALL ASSGN");
else if (MessageType == 0x04) fprintf(stderr, " VCALL_ASSGN");
else if (MessageType == 0x05) fprintf(stderr, " VCALL_ASSGN_DUP");
else if (MessageType == 0x0E) fprintf(stderr, " DCALL_ASSGN");
else if (MessageType == 0x18) fprintf(stderr, " SITE_INFO");
else if (MessageType == 0x19) fprintf(stderr, " SRV_INFO"); //per Op25
else if (MessageType == 0x1C) fprintf(stderr, " FAIL_STAT_INFO"); //per the damn manual
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 == 0x1B) fprintf(stderr, " ADJ_SITE_INFO");
else if (MessageType == 0x20) fprintf(stderr, " REG_RESP");
@ -331,20 +541,23 @@ void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
else if (MessageType == 0x38) fprintf(stderr, " SDCALL_REQ_HEADER");
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA");
else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP");
//end CAC only?
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS"); //observed from DSDPlus and OP25 dumps
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK"); //need to verify these for accuracy vs DSDPlus
else fprintf(stderr, " Unknown M%02X", MessageType);
else if (MessageType == 0x3F) fprintf(stderr, " ALIAS");
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK");
else fprintf(stderr, " Unknown M-%02X", MessageType);
fprintf (stderr, "%s", KNRM);
//zero out stale values so they won't persist after a transmit release
if (MessageType == 0x08 || MessageType == 0x10)
//disable if random messages wipe out the alias
if (MessageType == 0x08 || MessageType == 0x10) //idle, tx_rel
{
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, 0, sizeof(state->nxdn_sacch_frame_segment));
sprintf (state->nxdn_call_type, "%s", "");
}
}

View File

@ -12,53 +12,6 @@
#include "dsd.h"
#define NXDN_VCALL 0b000001 /* VCALL = VCALL_REQ = VCALL_RESP */
#define NXDN_VCALL_IV 0b000011
#define NXDN_DCALL_HDR 0b001001 /* Header Format - NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
#define NXDN_DCALL_USR 0b001011 /* User Data Format */
#define NXDN_DCALL_ACK 0b001100
#define NXDN_REL_EX 0b000111
#define NXDN_HEAD_DLY 0b001111
#define NXDN_SDCALL_REQ_HDR 0b111000 /* Header Format */
#define NXDN_SDCALL_REQ_USR 0b111001 /* User Data Format */
#define NXDN_SDCALL_RESP 0b111011
#define NXDN_SDCALL_IV 0b111010
#define NXDN_STAT_INQ_REQ 0b110000
#define NXDN_STAT_INQ_RESP 0b110001
#define NXDN_STAT_REQ 0b110010
#define NXDN_STAT_RESP 0b110011
#define NXDN_REM_CON_REQ 0b110100
#define NXDN_REM_CON_RESP 0b110101
#define NXDN_REM_CON_E_REQ 0b110110
#define NXDN_REM_CON_E_RESP 0b110111
//NXDN_VCALL_REQ = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
//NXDN_VCALL_RESP = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
#define NXDN_VCALL_REC_REQ = 0b000010 /* NXDN_VCALL_REC_REQ = NXDN_VCALL_REC_RESP */
//NXDN_VCALL_REC_RESP = 0b000010,
#define NXDN_VCALL_CONN_REQ = 0b000011 /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
//NXDN_VCALL_CONN_RESP = 0b000011, /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
#define NXDN_VCALL_ASSGN_DUP = 0b000101
//NXDN_DCALL_REQ = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
//NXDN_DCALL_RESP = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
#define NXDN_DCALL_REC_REQ = 0b001010 /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
//NXDN_DCALL_REC_RESP = 0b001010, /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
#define NXDN_DCALL_ASSGN = 0b001110
#define NXDN_DCALL_ASSGN_DUP = 0b001101
#define NXDN_IDLE 0b010000
#define NXDN_DISC_REQ = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
#define NXDN_DISC = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
/*
* @brief : This function decodes the full SACCH (when 4 voice frame parts
* have been successfully received)
*
* @param opts : Option structure parameters pointer
*
* @param state : State structure parameters pointer
*
* @return None
*
*/
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state)
{
uint8_t SACCH[72];
@ -78,8 +31,8 @@ void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state)
}
/* Decodes the element content */
//run it under crccorrect, or under payload (if incorrect, hide bad data unless payload enabled)
if (CrcCorrect == 1 || opts->payload == 1) NXDN_Elements_Content_decode(opts, state, CrcCorrect, SACCH);
// currently only going to run this if all four CRCs are good
if (CrcCorrect == 1) NXDN_Elements_Content_decode(opts, state, CrcCorrect, SACCH);
} /* End NXDN_SACCH_Full_decode() */
@ -112,23 +65,49 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
/* Decode the right "Message Type" */
switch(MessageType)
{
//VCALL_ASSGN
case 0x04:
//continue flow to DUP, both use same message format
//VCALL_ASSGN_DUP
case 0x05:
NXDN_decode_VCALL_ASSGN(opts, state, ElementsContent);
break;
//Alias 0x3F
case 0x3F:
{
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
NXDN_decode_Alias(opts, state, ElementsContent);
}
break;
break;
//SRV_INFO
case 0x19:
NXDN_decode_srv_info(opts, state, ElementsContent);
break;
//CCH_INFO
case 0x1A:
NXDN_decode_cch_info(opts, state, ElementsContent);
break;
//SITE_INFO
case 0x18:
NXDN_decode_site_info(opts, state, ElementsContent);
break;
//DISC
case 0x11:
//NXDN_decode_VCALL(opts, state, ElementsContent);
break;
//Idle
case NXDN_IDLE:
case 0x10:
{
//sprintf (state->nxdn_call_type, "NXDN IDLE ");
break;
}
/* VCALL */
case NXDN_VCALL:
case 0x01:
{
/* Set the CRC state */
@ -147,7 +126,7 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
} /* End case NXDN_VCALL: */
/* VCALL_IV */
case NXDN_VCALL_IV:
case 0x03:
{
/* Set the CRC state */
@ -181,13 +160,131 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
/* Unknown Message Type */
default:
{
//fprintf(stderr, "Unknown Message type ");
break;
}
} /* End switch(MessageType) */
} /* End NXDN_Elements_Content_decode() */
void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
//just using 'short form' M only data, not the optional data
uint8_t CCOption = 0;
uint8_t CallType = 0;
uint8_t VoiceCallOption = 0;
uint16_t SourceUnitID = 0;
uint16_t DestinationID = 0;
uint8_t CallTimer = 0;
uint16_t Channel = 0;
uint8_t LocationIDOption = 0;
uint8_t DuplexMode[32] = {0};
uint8_t TransmissionMode[32] = {0};
/* Decode "CC Option" */
CCOption = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
state->NxdnElementsContent.CCOption = CCOption;
/* Decode "Call Type" */
CallType = (uint8_t)ConvertBitIntoBytes(&Message[16], 3);
state->NxdnElementsContent.CallType = CallType;
/* Decode "Voice Call Option" */
VoiceCallOption = (uint8_t)ConvertBitIntoBytes(&Message[19], 5);
state->NxdnElementsContent.VoiceCallOption = VoiceCallOption;
/* Decode "Source Unit ID" */
SourceUnitID = (uint16_t)ConvertBitIntoBytes(&Message[24], 16);
state->NxdnElementsContent.SourceUnitID = SourceUnitID;
/* Decode "Destination ID" */
DestinationID = (uint16_t)ConvertBitIntoBytes(&Message[40], 16);
state->NxdnElementsContent.DestinationID = DestinationID;
/* Decode "Call Timer" */ //unsure of format of call timer, not required info for trunking
CallTimer = (uint8_t)ConvertBitIntoBytes(&Message[56], 6);
/* Decode "Channel" */
Channel = (uint16_t)ConvertBitIntoBytes(&Message[62], 10);
/* Decode "Location ID Option" */ //is in the optional field, probably best not to run this one
//LocationIDOption = (uint8_t)ConvertBitIntoBytes(&Message[72], 5);
/* Print the "CC Option" */
if(CCOption & 0x80) fprintf(stderr, "Emergency ");
if(CCOption & 0x40) fprintf(stderr, "Visitor ");
if(CCOption & 0x20) fprintf(stderr, "Priority Paging ");
/* Print the "Call Type" */
fprintf (stderr, "%s", KGRN);
fprintf(stderr, "\n %s - ", NXDN_Call_Type_To_Str(CallType));
/* Print the "Voice Call Option" */
NXDN_Voice_Call_Option_To_Str(VoiceCallOption, DuplexMode, TransmissionMode);
fprintf(stderr, "%s %s - ", DuplexMode, TransmissionMode);
/* Print Source ID and Destination ID (Talk Group or Unit ID) */
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
/* Print Channel (Skipping Call Timer)*/
fprintf(stderr, "- Channel [%03X][%04d] ", Channel & 0x3FF, Channel & 0x3FF);
//run process to figure out frequency value from the channel, or from lcn import array
long int freq = 0;
freq = nxdn_channel_to_frequency(opts, Channel);
//run group/source 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
for (int i = 0; i < state->group_tally; i++)
{
if (state->group_array[i].groupNumber == DestinationID) //source, or destination?
{
fprintf (stderr, " [%s]", state->group_array[i].groupName);
strcpy (mode, state->group_array[i].groupMode);
}
//might not be ideal if both source and group/target are both in the array
else if (state->group_array[i].groupNumber == SourceUnitID) //source, or destination?
{
fprintf (stderr, " [%s]", state->group_array[i].groupName);
strcpy (mode, state->group_array[i].groupMode);
}
}
//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_is_tuned == 0 && freq != 0) //if we aren't already on a VC and have a valid frequency already
{
//rigctl
if (opts->use_rigctl == 1)
{
//may or may not use setmod here, let user control it instead?
if (opts->frame_nxdn48 == 1) SetModulation(opts->rigctl_sockfd, 6250);
else SetModulation(opts->rigctl_sockfd, 12500);
SetFreq(opts->rigctl_sockfd, freq);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
}
//rtl_udp
else if (opts->audio_in_type == 3)
{
rtl_udp_tune (opts, state, freq);
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
opts->p25_is_tuned = 1;
}
//set rid and tg when we actually tune to it
state->nxdn_last_rid = SourceUnitID & 0xFFFF;
state->nxdn_last_tg = (DestinationID & 0xFFFF);
sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType));
}
}
fprintf (stderr, "%s", KNRM);
} /* End NXDN_decode_VCALL_ASSGN() */
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint8_t Alias1 = 0x0; //value of an ascii 'NULL' character
@ -195,8 +292,15 @@ void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message)
uint8_t Alias3 = 0x0;
uint8_t Alias4 = 0x0;
uint8_t blocknumber = 0;
uint8_t CrcCorrect = 1;
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
uint8_t CrcCorrect = 0;
//alias can also hit on a facch1 so that would be with a non_sf_sacch attached
if (state->nxdn_sacch_non_superframe == FALSE)
{
CrcCorrect = state->NxdnElementsContent.VCallCrcIsGood;
}
else CrcCorrect = 1; //FACCH1 with bad CRC won't make it this far anyways, so set as 1
//FACCH Payload [3F][68][82][04][2 <- block number4] "[69][6F][6E][20]" <- 4 alias octets [00][7F][1C]
blocknumber = (uint8_t)ConvertBitIntoBytes(&Message[32], 4) & 0x7; // & 0x7, might just be three bits, unsure
@ -226,9 +330,11 @@ void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message)
else ; //sprintf (state->nxdn_alias_block_segment[blocknumber-1][3], "%c", 0);
}
if (CrcCorrect) //was by blocknumber being 4, but wasn't as frequent as I would like in the console
//crc errs in one repitition may occlude an otherwise good alias, so test and change if needed
//completed alias should still appear in ncurses terminal regardless, so this may be okay
if (CrcCorrect)
{
fprintf (stderr, " "); //probably just want a space instead
fprintf (stderr, " "); //spacer
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
@ -243,25 +349,197 @@ void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message)
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint8_t location_id = 0;
uint8_t channel_ifno = 0;
uint32_t location_id = 0;
uint8_t channel1sts = 0;
uint16_t channel1 = 0;
uint8_t channel2sts = 0;
uint16_t channel2 = 0;
long int freq1 = 0;
long int freq2 = 0;
location_id = (uint32_t)ConvertBitIntoBytes(&Message[8], 24);
channel1sts = (uint8_t)ConvertBitIntoBytes(&Message[32], 6);
channel1 = (uint16_t)ConvertBitIntoBytes(&Message[38], 10);
channel2sts = (uint8_t)ConvertBitIntoBytes(&Message[48], 6);
channel2 = (uint16_t)ConvertBitIntoBytes(&Message[54], 10);
fprintf (stderr, "%s", KYEL);
nxdn_location_id_handler (state, location_id);
fprintf (stderr, "\n Control Channel Information \n");
fprintf (stderr, " Location ID [%06X] CC1 [%03X][%04d] CC2 [%03X][%04d] Status: ", location_id, channel1, channel1, channel2, channel2);
//check the sts bits to determine if current, new, add, or delete
if (channel1sts & 0x20) fprintf (stderr, "Current ");
else if (channel1sts & 0x10) fprintf (stderr, "New ");
else if (channel1sts & 0x8) fprintf (stderr, "Candidate Added ");
else if (channel1sts & 0x4) fprintf (stderr, "Candidate Deleted ");
freq1 = nxdn_channel_to_frequency (opts, channel1);
freq2 = nxdn_channel_to_frequency (opts, channel2);
//add handling for adding (or deleting?) frequencies to CC list
if (channel1sts & 0x20 || channel1sts & 0x8) //current or new only
{
if (freq1 != 0) state->p25_cc_freq = freq1;
}
else if (channel1sts & 0x8) //Candidate Added
{
if (freq1 != 0)
{
//add to trunk_lcn_freq list?
for (int i = 0; i <= state->lcn_freq_count; i++) //<= pretty sure is correct here since we need to go through atleast one rep
{
if (freq1 == state->trunk_lcn_freq[i]) goto SKIP;
}
//assign if freq1 not found in array
state->trunk_lcn_freq[state->lcn_freq_count] = freq1;
state->lcn_freq_count++;
}
}
//if a delete, let's not bother trying to remove it from the list for now
SKIP: ; //do nothing
//channel 2 should only be valid on 'current' status, but not sure what value it would contain vs channel 1
fprintf (stderr, "%s", KNRM);
}
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint32_t location_id = 0;
uint16_t svc_info = 0; //service information
uint32_t rst_info = 0; //restriction information
location_id = (uint32_t)ConvertBitIntoBytes(&Message[8], 24);
svc_info = (uint16_t)ConvertBitIntoBytes(&Message[32], 16);
rst_info = (uint32_t)ConvertBitIntoBytes(&Message[48], 24);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "\n Service Information - ");
fprintf (stderr, "Location ID [%06X] SVC [%04X] RST [%06X] ", location_id, svc_info, rst_info);
nxdn_location_id_handler (state, location_id);
fprintf (stderr, "%s", KNRM);
}
//externalize the location handler to its own function
void nxdn_location_id_handler (dsd_state * state, uint32_t location_id)
{
//6.5.2 Location ID
uint8_t category_bit = location_id >> 22;
uint32_t sys_code = 0;
uint16_t site_code = 0;
char category[14]; //G, R, or L
if (category_bit == 0)
{
sys_code = ( (location_id & 0x3FFFFF) >> 12); //10 bits
site_code = location_id & 0x3FF; //12 bits
sprintf (category, "%s", "Global");
}
else if (category_bit == 2)
{
sys_code = ( (location_id & 0x3FFFFF) >> 8); //14 bits
site_code = location_id & 0xFF; //8 bits
sprintf (category, "%s", "Regional");
}
else if (category_bit == 1)
{
sys_code = ( (location_id & 0x3FFFFF) >> 5); //17 bits
site_code = location_id & 0x1F; //5 bits
sprintf (category, "%s", "Local");
}
else
{
//err, or we shouldn't ever get here
sprintf (category, "%s", "Reserved/Err");
}
//not entirely convinced that site code and ran are the exact same thing due to bit len differences
//site code is meant to be a color code (but this could also be like the P25 p1 nac and p2 cc being the same)
state->nxdn_last_ran = site_code; //this one may drop on nocarrier
if (site_code != 0) state->nxdn_location_site_code = site_code; //this one won't
if (sys_code != 0) state->nxdn_location_sys_code = sys_code;
sprintf (state->nxdn_location_category, "%s", category);
fprintf (stderr, "\n Location Information - Cat: %s - Sys Code: %d - Site Code %d ", category, sys_code, site_code);
}
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint32_t location_id = 0;
uint16_t cs_info = 0; //channel structure information
uint16_t svc_info = 0; //service information
uint32_t rst_info = 0; //restriction information
uint32_t ca_info = 0; //channel access information
uint8_t version_num = 0;
uint8_t adj_alloc = 0; //number of adjacent sites (on same system?)
uint16_t channel1 = 0;
uint16_t channel2 = 0;
long int freq1 = 0;
long int freq2 = 0;
location_id = (uint32_t)ConvertBitIntoBytes(&Message[8], 24);
cs_info = (uint16_t)ConvertBitIntoBytes(&Message[32], 16);
svc_info = (uint16_t)ConvertBitIntoBytes(&Message[48], 16);
rst_info = (uint32_t)ConvertBitIntoBytes(&Message[64], 24);
ca_info = (uint32_t)ConvertBitIntoBytes(&Message[88], 24);
version_num = (uint8_t)ConvertBitIntoBytes(&Message[112], 8);
adj_alloc = (uint8_t)ConvertBitIntoBytes(&Message[120], 4);
channel1 = (uint16_t)ConvertBitIntoBytes(&Message[124], 10);
channel2 = (uint16_t)ConvertBitIntoBytes(&Message[134], 10);
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "\n Location ID [%06X] CSC [%04X] SVC [%04X] RST [%06X] \n CA [%06X] V[%X] ADJ [%01X] ",
location_id, cs_info, svc_info, rst_info, ca_info, version_num, adj_alloc);
nxdn_location_id_handler(state, location_id);
if (channel1 != 0)
{
fprintf (stderr, "\n Control Channel 1 [%03X][%04d] ", channel1, channel1 );
freq1 = nxdn_channel_to_frequency (opts, channel1);
}
if (channel2 != 0)
{
fprintf (stderr, "\n Control Channel 2 [%03X][%04d] ", channel2, channel2 );
freq2 = nxdn_channel_to_frequency (opts, channel2);
}
fprintf (stderr, "%s", KNRM);
//place the freqs into the array for hunting CC or
//rotating CCs without user LCN list
if (freq1 != 0)
{
for (int i = 0; i <= state->lcn_freq_count; i++) //<= pretty sure is correct here since we need to go through atleast one rep
{
if (freq1 == state->trunk_lcn_freq[i]) goto SKIP;
}
//assign if freq1 not found in array
state->trunk_lcn_freq[state->lcn_freq_count] = freq1;
state->lcn_freq_count++;
//shim here for main CC freq?
state->p25_cc_freq = freq1;
}
if (freq2 != 0)
{
for (int i = 0; i <= state->lcn_freq_count; i++) //<= pretty sure is correct here since we need to go through atleast one rep
{
if (freq2 == state->trunk_lcn_freq[i]) goto SKIP;
}
//assign if freq2 not found in array
state->trunk_lcn_freq[state->lcn_freq_count] = freq2;
state->lcn_freq_count++;
}
SKIP: ; //do nothing
}
/*
* @brief : This function decodes the VCALL message
*
* @param opts : Option structure parameters pointer
*
* @param state : State structure parameters pointer
*
* @param Message : A 64 bit buffer containing the VCALL message to decode
*
* @return None
*
*/
void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
//uint32_t i;
uint8_t CCOption = 0;
uint8_t CallType = 0;
uint8_t VoiceCallOption = 0;
@ -273,13 +551,6 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
uint8_t TransmissionMode[32] = {0};
unsigned long long int FullMessage = 0;
/* Message[0..7] contains :
* - The "F1" and "F2" flags
* - The "Message Type" (already decoded before calling this function)
*
* So no need to decode it a second time
*/
/* Decode "CC Option" */
CCOption = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
state->NxdnElementsContent.CCOption = CCOption;
@ -334,7 +605,6 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
fprintf(stderr, "%s %s - ", DuplexMode, TransmissionMode);
/* Print Source ID and Destination ID (Talk Group or Unit ID) */
//fprintf (stderr, "%s", KGRN);
fprintf(stderr, "Src=%u - Dst/TG=%u ", SourceUnitID & 0xFFFF, DestinationID & 0xFFFF);
fprintf (stderr, "%s", KNRM);
@ -343,16 +613,13 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
fprintf (stderr, "\n %s", KYEL);
fprintf(stderr, "%s - ", NXDN_Cipher_Type_To_Str(CipherType));
//state->nxdn_cipher_type = CipherType;
}
/* Print the Key ID */
if(CipherType != 0)
{
fprintf(stderr, "Key ID %u - ", KeyID & 0xFF);
fprintf (stderr, "%s", KNRM);
//state->nxdn_key = (KeyID & 0xFF);
}
if (state->nxdn_cipher_type == 0x01 && state->R > 0) //scrambler key value
@ -362,14 +629,15 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
fprintf (stderr, "%s", KNRM);
}
//only grab if CRC is okay
if(state->NxdnElementsContent.VCallCrcIsGood)
{
if ( (SourceUnitID & 0xFFFF) > 0 )
{
state->nxdn_last_rid = SourceUnitID & 0xFFFF; //only grab if CRC is okay
state->nxdn_last_rid = SourceUnitID & 0xFFFF;
state->nxdn_last_tg = (DestinationID & 0xFFFF);
state->nxdn_key = (KeyID & 0xFF);
state->nxdn_cipher_type = CipherType; //will this set to zero if no enc?
state->nxdn_cipher_type = CipherType;
}
}
else
@ -390,32 +658,11 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
}
} /* End NXDN_decode_VCALL() */
/*
* @brief : This function decodes the VCALL_IV message
*
* @param opts : Option structure parameters pointer
*
* @param state : State structure parameters pointer
*
* @param Message : A 72 bit buffer containing the VCALL_IV message to decode
*
* @return None
*
*/
void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint32_t i;
/* Message[0..7] contains :
* - The "F1" and "F2" flags
* - The "Message Type" (already decoded before calling this function)
*
* So no need to decode it a second time
*
* Message[8..71] contains : The 64 bits IV
*/
state->payload_miN = 0; //zero out
state->payload_miN = 0; //zero out
/* Extract the IV from the VCALL_IV message */
for(i = 0; i < 8; i++)
{
@ -430,15 +677,6 @@ void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message)
} /* End NXDN_decode_VCALL_IV() */
/*
* @brief : This function decodes the "Call Type" and return the
* ASCII string corresponding to it.
*
* @param CallType : The call type parameter to decode
*
* @return An ASCII string of the "Call Type"
*
*/
char * NXDN_Call_Type_To_Str(uint8_t CallType)
{
char * Ptr = NULL;
@ -459,22 +697,6 @@ char * NXDN_Call_Type_To_Str(uint8_t CallType)
return Ptr;
} /* End NXDN_Call_Type_To_Str() */
/*
* @brief : This function decodes the "Voice Call Option" and return the
* ASCII string corresponding to it.
*
* @param VoiceCallOption : The call type parameter to decode
*
* @param Duplex : A 32 bytes ASCII buffer pointer where store
* the Duplex/Half duplex mode
*
* @param TransmissionMode : A 32 bytes ASCII buffer pointer where store
* the transmission mode (bit rate)
*
* @return An ASCII string of the "Voice Call Option"
*
*/
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode)
{
char * Ptr = NULL;
@ -496,15 +718,6 @@ void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, ui
strcpy((char *)TransmissionMode, Ptr);
} /* End NXDN_Voice_Call_Option_To_Str() */
/*
* @brief : This function decodes the "Cipher Type" and return the
* ASCII string corresponding to it.
*
* @param CipherType : The cipher type parameter to decode
*
* @return An ASCII string of the "Cipher Type"
*
*/
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType)
{
char * Ptr = NULL;

View File

@ -1,6 +1,6 @@
//NXDN frame handler
//Reworked portions from Osmocom OP25
//from Osmocom OP25 rx_sync.cc
//Reworked portions from Osmocom OP25 rx_sync.cc
/* -*- c++ -*- */
/*
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
@ -59,7 +59,6 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
memset (dbuf, 0, sizeof(dbuf));
//collect lich bits first, if they are good, then we can collect the rest of them
//otherwise we will either exit, or skip and then exit (depends on performance)
for (int i = 0; i < 8; i++)
{
lich_dibits[i] = dbuf[i] = getDibit(opts, state);
@ -150,8 +149,10 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
sacch = 1;
break;
default:
if (opts->payload == 1) fprintf(stderr, "unsupported NXDN lich type 0x%02X\n", lich);
//fprintf(stderr, " unsupported NXDN lich 0x%02X\n", lich);
if (opts->payload == 1) fprintf(stderr, " false sync or unsupported NXDN lich type 0x%02X\n", lich);
//reset the sacch field, we probably got a false sync and need to wipe or give a bad crc
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
voice = 0;
goto END;
break;
@ -239,21 +240,13 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
}
if (sacch)
{
state->nxdn_sacch_non_superframe = (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) ? true : false;
nxdn_deperm_sacch(opts, state, sacch_bits);
}
state->nxdn_sacch_non_superframe = (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) ? true : false;
if (sacch)nxdn_deperm_sacch(opts, state, sacch_bits);
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits);
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
if (facch & 2)
{
//don't run if duplicate of facch in first, may still run both if one has bad CRC
//hopefully can mitigate that later when we having the CRC function working properly for FACCH
if (facch_bits_a != facch_bits_b) nxdn_deperm_facch(opts, state, facch_bits_b); //don't think this check works correctly
}
if (voice) nxdn_voice (opts, state, voice, dbuf); //working great now with new simplified method!
if (facch & 2) nxdn_deperm_facch(opts, state, facch_bits_b);
if (voice) nxdn_voice (opts, state, voice, dbuf);
fprintf (stderr, "\n");
END: ; //do nothing

View File

@ -2,12 +2,15 @@
* p25_frequency.c
* P25 Channel to Frequency Calculator
*
* NXDN Channel to Frequency, Courtesy of IcomIcR20 and EricCottrell (base values)
*
* LWVMOBILE
* 2022-09 DSD-FME Florida Man Edition
* 2022-11 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//P25
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel)
{
@ -41,4 +44,44 @@ long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channe
return (0);
}
}
//NXDN Channel to Frequency, Courtesy of IcomIcR20 on RR Forums
long int nxdn_channel_to_frequency(dsd_opts * opts, uint16_t channel)
{
long int freq;
long int base = 0;
//the base freq value is per EricCottrell in the Understanding NXDN Trunking Forum Thread
//will need to do some research or tests to confirm these values are accurate
if ((channel > 0) && (channel <= 400))
{
if (opts->frame_nxdn48 == 1) base = 450000000;
else base = 451000000;
freq = base + (channel - 1) * 12500;
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
return (freq);
}
else if ((channel >= 401) && (channel <= 800))
{
if (opts->frame_nxdn48 == 1) base = 460000000;
else base = 461000000;
freq = base + (channel - 401) * 12500;
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
return (freq);
}
else if (channel > 800)
{
//write function to check the LCN import array for custom frequency values!
//return (freq);
}
else
{
fprintf(stderr, "\n This LCN is used for a non-standard frequency. No frequency can be reported");
return (0);
}
}