NXDN CAC/CCH Messages; Initial Trunking Support;
This commit is contained in:
parent
eefe1bac5e
commit
9f624e0b62
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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", "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue