mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 09:17:20 -05:00
NXDN: Add DCALL / SDCALL Header and Data Block Assembly; SDCALL_IV Function;
NXDN: Disable LICH "dividing" offbits check on sync detection, causes too many blips on still okay signal, particularly trunking;
This commit is contained in:
@@ -1291,6 +1291,7 @@ uint16_t crc12f(const uint8_t buf[], int len);
|
||||
uint16_t crc15(const uint8_t buf[], int len);
|
||||
uint16_t crc16cac(const uint8_t buf[], int len);
|
||||
uint8_t crc7_scch(uint8_t bits[], int len); //converted from op25 crc6
|
||||
uint32_t nxdn_message_crc32(uint8_t * input, int len);
|
||||
|
||||
//YSF Soft Decision Viterbi
|
||||
uint32_t ysf_soft_decision_viterbi(uint8_t * dbuf, int d_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes);
|
||||
@@ -1320,6 +1321,7 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
char * NXDN_Call_Type_To_Str(uint8_t CallType);
|
||||
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
|
||||
void NXDN_Data_Call_Option_To_Str(uint8_t DataCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
|
||||
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
|
||||
//added these
|
||||
void NXDN_decode_Prop(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
@@ -1331,6 +1333,10 @@ void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void nxdn_decode_dst_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void NXDN_decode_adj_site(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void nxdn_sdcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void nxdn_dcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
void nxdn_dcall_iv(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
|
||||
int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message);
|
||||
//Type-D SCCH Message Decoder
|
||||
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction);
|
||||
void NXDN_decode_VCALL_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||
@@ -1610,6 +1616,9 @@ int udp_socket_connectA(dsd_opts * opts, dsd_state * state);
|
||||
void udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
|
||||
void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
|
||||
|
||||
//15-bit Scrambler
|
||||
void pdu_scrambler_keystream_creation(uint8_t * ks, int lfsr, int len);
|
||||
|
||||
//RC4 function prototypes
|
||||
void rc4_voice_decrypt (int drop, uint8_t keylength, uint8_t messagelength, uint8_t key[], uint8_t cipher[], uint8_t plain[]);
|
||||
void rc4_block_output (int drop, int keylen, int meslen, uint8_t * key, uint8_t * output_blocks);
|
||||
@@ -1652,6 +1661,7 @@ void lfsr_64_to_128(uint8_t * iv);
|
||||
void LFSR128(dsd_state * state);
|
||||
void LFSR128n(dsd_state * state);
|
||||
void LFSR128d(dsd_state * state);
|
||||
void LFSR128npdu(dsd_state * state);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2026-01 DSD-FME Florida Man Edition
|
||||
* 2026-03 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
@@ -77,7 +77,7 @@ void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
|
||||
else if (MessageType == 0x08) fprintf(stderr, " TX_REL");
|
||||
else if (MessageType == 0x09) fprintf(stderr, " DCALL_HEADER");
|
||||
else if (MessageType == 0x0A) fprintf(stderr, " DCALL_REC_REQ");
|
||||
else if (MessageType == 0x0B) fprintf(stderr, " DCALL_UDATA");
|
||||
else if (MessageType == 0x0B) fprintf(stderr, " DCALL_DATA");
|
||||
else if (MessageType == 0x0C) fprintf(stderr, " DCALL_ACK");
|
||||
else if (MessageType == 0x0D) fprintf(stderr, " DCALL_ASSGN_DUP");
|
||||
else if (MessageType == 0x0E) fprintf(stderr, " DCALL_ASSGN");
|
||||
@@ -107,7 +107,7 @@ void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType)
|
||||
else if (MessageType == 0x36) fprintf(stderr, " REM_CON_E_REQ");
|
||||
else if (MessageType == 0x37) fprintf(stderr, " REM_CON_E_RESP");
|
||||
else if (MessageType == 0x38) fprintf(stderr, " SDCALL_REQ_HEADER");
|
||||
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_USERDATA");
|
||||
else if (MessageType == 0x39) fprintf(stderr, " SDCALL_REQ_DATA");
|
||||
else if (MessageType == 0x3A) fprintf(stderr, " SDCALL_IV");
|
||||
else if (MessageType == 0x3B) fprintf(stderr, " SDCALL_RESP");
|
||||
|
||||
@@ -270,6 +270,24 @@ uint16_t crc16cac(const uint8_t buf[], int len)
|
||||
return crc & 0xffff;
|
||||
}
|
||||
|
||||
uint32_t nxdn_message_crc32(uint8_t * input, int len)
|
||||
{
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
uint32_t poly = 0x04C11DB7;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
|
||||
if( ((crc >> 31) & 1) ^ (input[i] & 1) )
|
||||
crc = (crc << 1) ^ poly;
|
||||
else
|
||||
crc <<= 1;
|
||||
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint8_t crc7_scch(uint8_t bits[], int len)
|
||||
{
|
||||
uint8_t s[7];
|
||||
@@ -329,6 +347,58 @@ void LFSR128n(dsd_state * state)
|
||||
|
||||
}
|
||||
|
||||
void pdu_scrambler_keystream_creation(uint8_t * ks, int lfsr, int len)
|
||||
{
|
||||
int bit = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
ks[i] = lfsr & 0x1;
|
||||
bit = ( (lfsr >> 1) ^ (lfsr >> 0) ) & 1;
|
||||
lfsr = ( (lfsr >> 1 ) | (bit << 14) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LFSR128npdu(dsd_state * state)
|
||||
{
|
||||
//generate a 128-bit IV from a 64-bit IV for AES blocks
|
||||
unsigned long long int lfsr = state->payload_mi;
|
||||
|
||||
//start packing aes_ivR (for data in case of simultaneous voice and data call)
|
||||
state->aes_ivR[0] = (lfsr >> 56) & 0xFF;
|
||||
state->aes_ivR[1] = (lfsr >> 48) & 0xFF;
|
||||
state->aes_ivR[2] = (lfsr >> 40) & 0xFF;
|
||||
state->aes_ivR[3] = (lfsr >> 32) & 0xFF;
|
||||
state->aes_ivR[4] = (lfsr >> 24) & 0xFF;
|
||||
state->aes_ivR[5] = (lfsr >> 16) & 0xFF;
|
||||
state->aes_ivR[6] = (lfsr >> 8 ) & 0xFF;
|
||||
state->aes_ivR[7] = (lfsr >> 0 ) & 0xFF;
|
||||
|
||||
|
||||
int cnt = 0; int x = 64;
|
||||
unsigned long long int bit;
|
||||
//polynomial P(x) = 1 + X15 + X27 + X38 + X46 + X62 + X64
|
||||
for(cnt=0;cnt<64;cnt++)
|
||||
{
|
||||
//63,61,45,37,27,14
|
||||
// Polynomial is C(x) = x^64 + x^62 + x^46 + x^38 + x^27 + x^15 + 1
|
||||
bit = ((lfsr >> 63) ^ (lfsr >> 61) ^ (lfsr >> 45) ^ (lfsr >> 37) ^ (lfsr >> 26) ^ (lfsr >> 14)) & 0x1;
|
||||
lfsr = (lfsr << 1) | bit;
|
||||
|
||||
//continue packing aes_iv
|
||||
state->aes_ivR[x/8] = (state->aes_ivR[x/8] << 1) + bit;
|
||||
x++;
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " IV(128): ");
|
||||
for (x = 0; x < 16; x++)
|
||||
fprintf (stderr, "%02X", state->aes_ivR[x]);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
//viterbi bit and byte array sizes are max value for largest required (facch2/udch)
|
||||
uint8_t viterbi_bits[26*8];
|
||||
uint8_t viterbi_bytes[26];
|
||||
@@ -627,6 +697,8 @@ void nxdn_facch1(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t fra
|
||||
if (frame == 1)
|
||||
memcpy(facch1_storage, viterbi_bytes, sizeof(facch1_storage));
|
||||
|
||||
state->data_header_format[0] = 3;
|
||||
|
||||
if (crc == check && duplicate == 0) NXDN_Elements_Content_decode(opts, state, 1, viterbi_bits);
|
||||
// else if (opts->aggressive_framesync == 0) NXDN_Elements_Content_decode(opts, state, 0, viterbi_bits);
|
||||
|
||||
@@ -692,6 +764,7 @@ void nxdn_cac(dsd_opts * opts, dsd_state * state, uint8_t * bits)
|
||||
|
||||
if (crc == 0)
|
||||
{
|
||||
state->data_header_format[0] = 2;
|
||||
ran = (viterbi_bits[2] << 5) | (viterbi_bits[3] << 4) | (viterbi_bits[4] << 3) | (viterbi_bits[5] << 2) | (viterbi_bits[6] << 1) | viterbi_bits[7];
|
||||
state->nxdn_last_ran = ran;
|
||||
}
|
||||
@@ -760,6 +833,19 @@ void nxdn_cac(dsd_opts * opts, dsd_state * state, uint8_t * bits)
|
||||
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
||||
state->offset = 0;
|
||||
|
||||
//invalidate any active data call assembly and wipe storage
|
||||
//clear storage
|
||||
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
|
||||
//reset block number
|
||||
state->data_header_blocks[0] = 1;
|
||||
|
||||
//reset delivery format (type)
|
||||
state->data_header_format[0] = 0;
|
||||
|
||||
//reset header validity
|
||||
state->data_header_valid[0] = 0;
|
||||
|
||||
//debug notification
|
||||
// fprintf (stderr, " RESET CARRIER; ");
|
||||
|
||||
@@ -905,34 +991,36 @@ void idas_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8
|
||||
|
||||
if (crc[0] == check[0] && crc[1] == check[1])
|
||||
{
|
||||
state->data_header_format[0] = 1;
|
||||
if (type == 1) NXDN_Elements_Content_decode(opts, state, 1, f3_udch2);
|
||||
if (type == 0) {} //need handling for user data (text messages and AVL)
|
||||
if (type == 0) NXDN_Elements_Content_decode(opts, state, 1, f3_udch2);
|
||||
}
|
||||
// else TODO: invalidate UDCH2 data buffers
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
fprintf (stderr, "\n UDCH2 Data: " );
|
||||
for (int i = 0; i < 22; i++) //all but last crc portion
|
||||
{
|
||||
if (i == 10)
|
||||
{
|
||||
fprintf (stderr, " "); //space seperator?
|
||||
i = 12; //skip first crc portion
|
||||
}
|
||||
fprintf (stderr, "%02X", f3_udch2_bytes[i]);
|
||||
}
|
||||
// if (type == 0)
|
||||
// {
|
||||
// fprintf (stderr, "\n UDCH2 Data: " );
|
||||
// for (int i = 0; i < 22; i++) //all but last crc portion
|
||||
// {
|
||||
// if (i == 10)
|
||||
// {
|
||||
// fprintf (stderr, " "); //space seperator?
|
||||
// i = 12; //skip first crc portion
|
||||
// }
|
||||
// fprintf (stderr, "%02X", f3_udch2_bytes[i]);
|
||||
// }
|
||||
|
||||
fprintf (stderr, "\n UDCH2 Data: ASCII - " );
|
||||
for (int i = 0; i < 22; i++) //all but last crc portion
|
||||
{
|
||||
if (i == 10) i = 12; //skip first crc portion
|
||||
if (f3_udch2_bytes[i] <= 0x7E && f3_udch2_bytes[i] >=0x20)
|
||||
{
|
||||
fprintf (stderr, "%c", f3_udch2_bytes[i]);
|
||||
}
|
||||
else fprintf (stderr, " ");
|
||||
}
|
||||
}
|
||||
// fprintf (stderr, "\n UDCH2 Data: ASCII - " );
|
||||
// for (int i = 0; i < 22; i++) //all but last crc portion
|
||||
// {
|
||||
// if (i == 10) i = 12; //skip first crc portion
|
||||
// if (f3_udch2_bytes[i] <= 0x7E && f3_udch2_bytes[i] >=0x20)
|
||||
// {
|
||||
// fprintf (stderr, "%c", f3_udch2_bytes[i]);
|
||||
// }
|
||||
// else fprintf (stderr, " ");
|
||||
// }
|
||||
// }
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
@@ -1359,27 +1447,29 @@ void nxdn_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_
|
||||
|
||||
if (crc == check)
|
||||
{
|
||||
state->data_header_format[0] = 1;
|
||||
if (type == 1) NXDN_Elements_Content_decode(opts, state, 1, f2u_message_buffer);
|
||||
if (type == 0) {} //need handling for user data (text messages and AVL)
|
||||
if (type == 0) NXDN_Elements_Content_decode(opts, state, 1, f2u_message_buffer);
|
||||
}
|
||||
// else TODO: invalidate UDCH data buffers
|
||||
|
||||
if (type == 0 && crc == check)
|
||||
{
|
||||
fprintf (stderr, "\n UDCH Data: " );
|
||||
for (int i = 0; i < 24; i++) //all but last crc portion
|
||||
fprintf (stderr, "%02X", viterbi_bytes[i]);
|
||||
// if (type == 0 && crc == check)
|
||||
// {
|
||||
// fprintf (stderr, "\n UDCH Data: " );
|
||||
// for (int i = 0; i < 24; i++) //all but last crc portion
|
||||
// fprintf (stderr, "%02X", viterbi_bytes[i]);
|
||||
|
||||
fprintf (stderr, "\n UDCH Data: ASCII - " );
|
||||
for (int i = 0; i < 24; i++) //remove crc portion
|
||||
{
|
||||
if (viterbi_bytes[i] <= 0x7E && viterbi_bytes[i] >=0x20)
|
||||
{
|
||||
fprintf (stderr, "%c", viterbi_bytes[i]);
|
||||
}
|
||||
else fprintf (stderr, " ");
|
||||
}
|
||||
// fprintf (stderr, "\n UDCH Data: ASCII - " );
|
||||
// for (int i = 0; i < 24; i++) //remove crc portion
|
||||
// {
|
||||
// if (viterbi_bytes[i] <= 0x7E && viterbi_bytes[i] >=0x20)
|
||||
// {
|
||||
// fprintf (stderr, "%c", viterbi_bytes[i]);
|
||||
// }
|
||||
// else fprintf (stderr, " ");
|
||||
// }
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Originally found at - https://github.com/LouisErigHerve/dsd
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2026-01 DSD-FME Florida Man Edition
|
||||
* 2026-03 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
@@ -81,17 +81,30 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t Cr
|
||||
switch(MessageType)
|
||||
{
|
||||
|
||||
/*
|
||||
//Note: CAC Message with same Message Type -- This is a private call request and rejection (TODO: Seperate handling depending on CAC, FACCH< Sacch, etc)
|
||||
20:56:15 Sync: NXDN96 RCCH Data RAN 01 CAC VCALL (VCALL_REQ)
|
||||
Private Call - Half Duplex 9600bps/EHR (02) - Src=211 - Dst/TG=1603
|
||||
20:56:15 Sync: NXDN96 RCCH Data RAN 01 CAC DISC (VCALL_REJECTION)
|
||||
Private Call - Disconnect - Src=1603 - Dst/TG=211
|
||||
//SDCALL Header
|
||||
case 0x38:
|
||||
nxdn_sdcall_header(opts, state, ElementsContent);
|
||||
break;
|
||||
|
||||
*/
|
||||
//Debug: Disable DUP messages if they cause random issues with Type-C trunking (i.e. changing SRC ang TGT IDs, hopping in the middle of calls, etc)
|
||||
//SDCALL Data Blocks
|
||||
case 0x39:
|
||||
nxdn_dcall_data(opts, state, state->data_header_format[0], ElementsContent); //TODO: type field as delivery format (FACCH1, UDCH, or CAC)
|
||||
break;
|
||||
|
||||
//observed new messages in #318, should also be noted that F1 and F2 are both set on these messages
|
||||
//SDCALL_IV
|
||||
case 0x3A:
|
||||
nxdn_dcall_iv(opts, state, 2, ElementsContent);
|
||||
break;
|
||||
|
||||
//DCALL Header
|
||||
case 0x09:
|
||||
nxdn_dcall_header(opts, state, ElementsContent);
|
||||
break;
|
||||
|
||||
//DCALL Data Blocks
|
||||
case 0x0B:
|
||||
nxdn_dcall_data(opts, state, state->data_header_format[0], ElementsContent); //TODO: type field as delivery format (FACCH1, UDCH, or CAC)
|
||||
break;
|
||||
|
||||
//VCALL and TX_REL custom to certain radios, but they have different elements in them
|
||||
case 0xE1:
|
||||
@@ -157,6 +170,24 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t Cr
|
||||
case 0x08: //TX_REL
|
||||
sprintf (state->call_string[0], "%s", "");
|
||||
sprintf (state->nxdn_call_type, "%s", "");
|
||||
//invalidate any active data call assembly and wipe storage
|
||||
//clear storage
|
||||
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
|
||||
//reset block number
|
||||
state->data_header_blocks[0] = 1;
|
||||
|
||||
//reset delivery format (type)
|
||||
state->data_header_format[0] = 0;
|
||||
|
||||
//reset header validity
|
||||
state->data_header_valid[0] = 0;
|
||||
|
||||
//reset encryption variables (for pdu)
|
||||
state->payload_algid = 0;
|
||||
state->payload_keyid = 0;
|
||||
state->payload_mi = 0;
|
||||
|
||||
case 0x01: //VCALL
|
||||
NXDN_decode_VCALL(opts, state, ElementsContent);
|
||||
break;
|
||||
@@ -169,6 +200,24 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t Cr
|
||||
sprintf (state->call_string[0], "%s", "");
|
||||
sprintf (state->nxdn_call_type, "%s", "");
|
||||
|
||||
//invalidate any active data call assembly and wipe storage
|
||||
//clear storage
|
||||
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
|
||||
//reset block number
|
||||
state->data_header_blocks[0] = 1;
|
||||
|
||||
//reset delivery format (type)
|
||||
state->data_header_format[0] = 0;
|
||||
|
||||
//reset header validity
|
||||
state->data_header_valid[0] = 0;
|
||||
|
||||
//reset encryption variables (for pdu)
|
||||
state->payload_algid = 0;
|
||||
state->payload_keyid = 0;
|
||||
state->payload_mi = 0;
|
||||
|
||||
//tune back to CC here - save about 1-2 seconds
|
||||
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
|
||||
{
|
||||
@@ -1115,8 +1164,12 @@ void nxdn_decode_dst_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
fprintf (stderr, "\n Option: %02X; Start: %d; End %d; Characters: %d or Sequence: %02X;", option, start, end, num_chars, option & 0x3F);
|
||||
|
||||
//This message has a CRC, but it appears that it is carried out across
|
||||
//multiple segments if this is a multi-part message, but CRC type is not
|
||||
//indicated, so will just rely on the overall CRC from each message prior
|
||||
//multiple segments (string part only) if this is a multi-part message
|
||||
|
||||
//CRC32 on this with Denny's dst_info sample, works, but its a single segment message
|
||||
// uint32_t crc_ext = convert_bits_into_output(Message+16+(num_chars*8), 32);
|
||||
// uint32_t crc_chk = nxdn_message_crc32(Message+16, num_chars*8);
|
||||
// fprintf (stderr, " CRC: %08X / %08X", crc_ext, crc_chk);
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
@@ -2389,6 +2442,470 @@ void NXDN_decode_ALIAS_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Messag
|
||||
|
||||
} /* End NXDN_decode_ALIAS_ARIB() */
|
||||
|
||||
void nxdn_dcall_iv(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message)
|
||||
{
|
||||
UNUSED(opts);
|
||||
|
||||
uint8_t idas = 0;
|
||||
if (strcmp (state->nxdn_location_category, "Type-D") == 0) idas = 1;
|
||||
|
||||
uint8_t iv[16]; memset(iv, 0, sizeof(iv));
|
||||
|
||||
if (idas)
|
||||
{
|
||||
state->payload_mi = (unsigned long long int)ConvertBitIntoBytes(Message+8, 22);
|
||||
pack_bit_array_into_byte_array(Message+8, iv, 3);
|
||||
//TODO: Figure out how IDAS 22-bit expansion works, use taps on 22-bit config, then go from 64-128?
|
||||
//I really don't expect this will ever be something that comes up, given how Type-D is not prevelant
|
||||
}
|
||||
else
|
||||
{
|
||||
state->payload_mi = (unsigned long long int)ConvertBitIntoBytes(Message+8, 64);
|
||||
pack_bit_array_into_byte_array(Message+8, iv, 8);
|
||||
if (state->nxdn_cipher_type == 3)
|
||||
LFSR128npdu(state);
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
if (type == 1)
|
||||
fprintf (stderr, "\n DCALL_IV: %016llX", state->payload_mi);
|
||||
else fprintf (stderr, "\n SDCALL_IV: %016llX", state->payload_mi);
|
||||
|
||||
}
|
||||
|
||||
void nxdn_sdcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
{
|
||||
UNUSED(opts);
|
||||
|
||||
uint8_t idas = 0;
|
||||
if (strcmp (state->nxdn_location_category, "Type-D") == 0) idas = 1;
|
||||
|
||||
uint8_t cc_option = (uint8_t)ConvertBitIntoBytes(Message+8, 8); UNUSED(cc_option);
|
||||
uint8_t call_type = (uint8_t)ConvertBitIntoBytes(Message+16, 3);
|
||||
uint8_t dcall_opt = (uint8_t)ConvertBitIntoBytes(Message+19, 5);
|
||||
|
||||
uint16_t source = (uint16_t)ConvertBitIntoBytes(Message+24, 16);
|
||||
uint16_t target = (uint16_t)ConvertBitIntoBytes(Message+40, 16);
|
||||
uint8_t cipher = (uint8_t)ConvertBitIntoBytes(Message+56, 2);
|
||||
uint8_t key_id = (uint8_t)ConvertBitIntoBytes(Message+58, 6);
|
||||
|
||||
//encryption variable assignment for PDU
|
||||
state->payload_algid = cipher;
|
||||
state->payload_keyid = key_id;
|
||||
|
||||
//idas channel split
|
||||
uint16_t source_ch = 0;
|
||||
uint16_t target_ch = 0;
|
||||
|
||||
if (idas)
|
||||
{
|
||||
source_ch = (source >> 11) & 0x1F;
|
||||
target_ch = (target >> 11) & 0x1F;
|
||||
source = source & 0x7FF;
|
||||
target = target & 0x7FF;
|
||||
}
|
||||
|
||||
//packet information and its sub elements (bytes 0, and 1 only)
|
||||
uint16_t pkt_info = (uint16_t)ConvertBitIntoBytes(Message+64, 16);
|
||||
uint8_t confirmed_delivery = Message[64];
|
||||
uint8_t spare1 = Message[65];
|
||||
uint8_t selective_retry = Message[66];
|
||||
uint8_t spare2 = Message[67];
|
||||
uint8_t block_count = (uint8_t)ConvertBitIntoBytes(Message+68, 4);
|
||||
uint8_t pad_bytes = (uint8_t)ConvertBitIntoBytes(Message+72, 5);
|
||||
uint8_t start_frag = Message[77];
|
||||
uint8_t circulate = Message[78];
|
||||
|
||||
//NOTE: Location Info Element is only on UPCH (CAC) and not on FACCH1 message
|
||||
//it will be treated as an optional element and will not be decoded.
|
||||
|
||||
uint8_t duplex_string[32]; memset(duplex_string, 0, sizeof(duplex_string));
|
||||
uint8_t dcall_string[32]; memset(dcall_string, 0, sizeof(dcall_string));
|
||||
NXDN_Data_Call_Option_To_Str(dcall_opt, duplex_string, dcall_string);
|
||||
|
||||
fprintf (stderr, "\n %s", KCYN);
|
||||
fprintf (stderr, "SD Data Call Header (%04X) ", pkt_info);
|
||||
if (idas == 0)
|
||||
fprintf (stderr, "Source: %d; Target: %d; ", source, target);
|
||||
else
|
||||
fprintf (stderr, "Source: %d-%d; Target: %d-%d; ", source_ch, source, target_ch, target);
|
||||
fprintf (stderr, "%s %s %s ", NXDN_Call_Type_To_Str(call_type), dcall_string, duplex_string);
|
||||
|
||||
//TODO: Decode cc_option
|
||||
|
||||
//next line
|
||||
fprintf (stderr, "\n ");
|
||||
|
||||
fprintf (stderr, "Blocks: %d; Padding: %d; ", block_count, pad_bytes);
|
||||
|
||||
if (cipher)
|
||||
{
|
||||
fprintf (stderr, "ENC; ");
|
||||
fprintf (stderr, "Cipher: %X; ", cipher);
|
||||
fprintf (stderr, "Key ID; %02X; ", key_id);
|
||||
}
|
||||
|
||||
if (spare1)
|
||||
fprintf (stderr, "S1; ");
|
||||
|
||||
if (spare2)
|
||||
fprintf (stderr, "S2; ");
|
||||
|
||||
if (start_frag)
|
||||
fprintf (stderr, "Starting Fragment; ");
|
||||
|
||||
if (circulate)
|
||||
fprintf (stderr, "Circulate; ");
|
||||
|
||||
if (confirmed_delivery)
|
||||
fprintf (stderr, "Confirmed Delivery; ");
|
||||
|
||||
if (selective_retry)
|
||||
fprintf (stderr, "Selective Retry; ");
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//clear storage
|
||||
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
|
||||
//set header block expected value
|
||||
if (block_count > 0)
|
||||
state->data_header_blocks[0] = block_count;
|
||||
else state->data_header_blocks[0] = 1;
|
||||
|
||||
//set this is a valid header
|
||||
state->data_header_valid[0] = 1;
|
||||
|
||||
}
|
||||
|
||||
void nxdn_dcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message)
|
||||
{
|
||||
UNUSED(opts);
|
||||
|
||||
uint8_t idas = 0;
|
||||
if (strcmp (state->nxdn_location_category, "Type-D") == 0) idas = 1;
|
||||
|
||||
uint8_t cc_option = (uint8_t)ConvertBitIntoBytes(Message+8, 8); UNUSED(cc_option);
|
||||
uint8_t call_type = (uint8_t)ConvertBitIntoBytes(Message+16, 3);
|
||||
uint8_t dcall_opt = (uint8_t)ConvertBitIntoBytes(Message+19, 5);
|
||||
|
||||
uint16_t source = (uint16_t)ConvertBitIntoBytes(Message+24, 16);
|
||||
uint16_t target = (uint16_t)ConvertBitIntoBytes(Message+40, 16);
|
||||
uint8_t cipher = (uint8_t)ConvertBitIntoBytes(Message+56, 2);
|
||||
uint8_t key_id = (uint8_t)ConvertBitIntoBytes(Message+58, 6);
|
||||
|
||||
//idas channel split
|
||||
uint16_t source_ch = 0;
|
||||
uint16_t target_ch = 0;
|
||||
|
||||
if (idas)
|
||||
{
|
||||
source_ch = (source >> 11) & 0x1F;
|
||||
target_ch = (target >> 11) & 0x1F;
|
||||
source = source & 0x7FF;
|
||||
target = target & 0x7FF;
|
||||
}
|
||||
|
||||
//packet information and its sub elements
|
||||
uint32_t pkt_info = (uint32_t)ConvertBitIntoBytes(Message+64, 24);
|
||||
uint8_t confirmed_delivery = Message[64];
|
||||
uint8_t spare1 = Message[65];
|
||||
uint8_t selective_retry = Message[66];
|
||||
uint8_t spare2 = Message[67];
|
||||
uint8_t block_count = (uint8_t)ConvertBitIntoBytes(Message+68, 4);
|
||||
uint8_t pad_bytes = (uint8_t)ConvertBitIntoBytes(Message+72, 5);
|
||||
uint8_t start_frag = Message[77];
|
||||
uint8_t circulate = Message[78];
|
||||
uint16_t tx_frag_count = (uint16_t)ConvertBitIntoBytes(Message+79, 9);
|
||||
|
||||
//check for optional IV
|
||||
uint8_t iv[16]; memset(iv, 0, sizeof(iv));
|
||||
uint8_t iv_presence = (uint8_t)ConvertBitIntoBytes(Message+88, 8);
|
||||
if (cipher > 1 && iv_presence != 0)
|
||||
{
|
||||
if (idas)
|
||||
pack_bit_array_into_byte_array(Message+88, iv, 3);
|
||||
else
|
||||
pack_bit_array_into_byte_array(Message+88, iv, 8);
|
||||
|
||||
if (idas)
|
||||
{
|
||||
//TODO: Idas expansion
|
||||
state->payload_mi = (unsigned long long int)ConvertBitIntoBytes(Message+88, 22);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->payload_mi = (unsigned long long int)ConvertBitIntoBytes(Message+88, 64);
|
||||
}
|
||||
}
|
||||
|
||||
//encryption variable assignment for PDU
|
||||
state->payload_algid = cipher;
|
||||
state->payload_keyid = key_id;
|
||||
|
||||
uint8_t duplex_string[32]; memset(duplex_string, 0, sizeof(duplex_string));
|
||||
uint8_t dcall_string[32]; memset(dcall_string, 0, sizeof(dcall_string));
|
||||
NXDN_Data_Call_Option_To_Str(dcall_opt, duplex_string, dcall_string);
|
||||
|
||||
fprintf (stderr, "\n %s", KCYN);
|
||||
fprintf (stderr, "Data Call Header (%04X) ", pkt_info);
|
||||
if (idas == 0)
|
||||
fprintf (stderr, "Source: %d; Target: %d; ", source, target);
|
||||
else
|
||||
fprintf (stderr, "Source: %d-%d; Target: %d-%d; ", source_ch, source, target_ch, target);
|
||||
fprintf (stderr, "%s %s %s ", NXDN_Call_Type_To_Str(call_type), dcall_string, duplex_string);
|
||||
|
||||
//TODO: Decode cc_option
|
||||
|
||||
//next line
|
||||
fprintf (stderr, "\n ");
|
||||
|
||||
fprintf (stderr, "Blocks: %d; Padding: %d; TX Frag: %d; ", block_count, pad_bytes, tx_frag_count);
|
||||
|
||||
if (cipher)
|
||||
{
|
||||
fprintf (stderr, "ENC; ");
|
||||
fprintf (stderr, "Cipher: %X; ", cipher);
|
||||
fprintf (stderr, "Key ID; %02X; ", key_id);
|
||||
if (cipher > 1 && iv_presence)
|
||||
{
|
||||
fprintf (stderr, "IV: ");
|
||||
if (idas)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
fprintf (stderr, "%02X", iv[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
fprintf (stderr, "%02X", iv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (spare1)
|
||||
fprintf (stderr, "S1; ");
|
||||
|
||||
if (spare2)
|
||||
fprintf (stderr, "S2; ");
|
||||
|
||||
if (start_frag)
|
||||
fprintf (stderr, "Starting Fragment; ");
|
||||
|
||||
if (circulate)
|
||||
fprintf (stderr, "Circulate; ");
|
||||
|
||||
if (confirmed_delivery)
|
||||
fprintf (stderr, "Confirmed Delivery; ");
|
||||
|
||||
if (selective_retry)
|
||||
fprintf (stderr, "Selective Retry; ");
|
||||
|
||||
//AES IV expansion
|
||||
if (cipher == 3 && iv_presence != 0)
|
||||
LFSR128npdu(state);
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//clear storage
|
||||
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
|
||||
//set header block expected value
|
||||
if (block_count > 0)
|
||||
state->data_header_blocks[0] = block_count;
|
||||
else state->data_header_blocks[0] = 1;
|
||||
|
||||
//set this is a valid header
|
||||
state->data_header_valid[0] = 1;
|
||||
|
||||
}
|
||||
|
||||
int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Message)
|
||||
{
|
||||
UNUSED(opts);
|
||||
|
||||
//packet frame number and block number, observation shows these seem to be the same value (observed Type-D)
|
||||
//but the manual does not quite explain these values indicate super packet of up to 16 big packets, and 1 packet is up to 16 blocks?
|
||||
uint8_t pf_num = (uint8_t)ConvertBitIntoBytes(Message+8, 4);
|
||||
uint8_t blk_num = (uint8_t)ConvertBitIntoBytes(Message+12, 4);
|
||||
|
||||
fprintf (stderr, "\n %s", KCYN);
|
||||
fprintf (stderr, "Data Call (%d/%d); ", pf_num, blk_num);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//TODO: Will need to have a len of valid bytes of dcall data at this point, that depends on carrier type (cac, facch1, udch, udch2, etc)
|
||||
//NOTE: Its possible this format only works with UDCH, or UDCH2 if using Type-D
|
||||
//TODO: Copy / Append data in blocks to superframe for decode on last one received
|
||||
//TODO: CRC Check on completed PDU? Is that available?
|
||||
|
||||
int byte_len = 20; //relevant number of user data bytes in UDCH
|
||||
uint8_t idas = 0;
|
||||
if (strcmp (state->nxdn_location_category, "Type-D") == 0) idas = 1;
|
||||
if (idas)
|
||||
byte_len = 18; //relevant number of user data bytes in UDCH2
|
||||
|
||||
//CAC type byte_len overrides on SDCALL (UPCH)
|
||||
if (type == 2)
|
||||
byte_len = 14;
|
||||
|
||||
//FACCH1 type byte_len overrides on SDCALL
|
||||
if (type == 3)
|
||||
byte_len = 8;
|
||||
|
||||
//Set a pointer value to where this block fits into the PDU superframe structure (bits, not bytes)
|
||||
int ptr = byte_len * 8 * (state->data_header_blocks[0] - blk_num);
|
||||
int total_bytes = state->data_header_blocks[0]*byte_len;
|
||||
|
||||
if (type == 2) //reported blocks is 2, but data blocks consists of 2,1,0 in sample obtained, doesn't occur on IDAS, but on Type-C UPCH
|
||||
total_bytes = (state->data_header_blocks[0]+1)*byte_len;
|
||||
|
||||
//sanity check and warning
|
||||
if (state->data_header_valid[0] == 0)
|
||||
{
|
||||
fprintf (stderr, "Missing or Invalid Header; ");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (blk_num > state->data_header_blocks[0])
|
||||
{
|
||||
fprintf (stderr, "Block Num Exceeds Header Reported (%d/%d); ", state->data_header_blocks[0], blk_num);
|
||||
state->data_header_valid[0] = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Debug
|
||||
// fprintf (stderr, "Type: %d; Byte Len: %02d; Total: %03d; Ptr: %04d;", state->data_header_format[0], byte_len, total_bytes, ptr);
|
||||
|
||||
//storage (bits, not bytes)
|
||||
memcpy(state->dmr_pdu_sf[0]+ptr, Message+16, byte_len*8*sizeof(uint8_t));
|
||||
|
||||
//WIP: completed, evaluate, dump, zero out completed results
|
||||
if (blk_num == 0)
|
||||
{
|
||||
|
||||
//WIP: KS creation and application, if applicable
|
||||
//TODO: May need to use state->RR for simultaneous data and voice call where both are potentially encrypted?
|
||||
uint8_t ks[3000]; memset(ks, 0, sizeof(ks));
|
||||
|
||||
//TODO: check for key available, if loader
|
||||
// if (state->payload_algid == 2)
|
||||
// {
|
||||
// if (state->keyloader == 1)
|
||||
// {
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
if (state->payload_algid != 0)
|
||||
{
|
||||
fprintf (stderr, "\n Encrypted Data; Cipher: %d; Key ID: %02X;", state->payload_algid, state->payload_keyid);
|
||||
if (state->payload_algid > 1)
|
||||
fprintf (stderr, " IV: %016llX;", state->payload_mi);
|
||||
}
|
||||
|
||||
//scrambler
|
||||
if (state->payload_algid == 1 && state->R != 0)
|
||||
{
|
||||
fprintf (stderr, " Key: %05lld;", state->R);
|
||||
pdu_scrambler_keystream_creation(ks, state->R, total_bytes*8);
|
||||
}
|
||||
//DES
|
||||
else if (state->payload_algid == 2 && state->R != 0)
|
||||
{
|
||||
fprintf (stderr, " Key: %016llX;", state->R);
|
||||
int nblocks = total_bytes/8;
|
||||
if (total_bytes%8)
|
||||
nblocks++;
|
||||
uint8_t ks_bytes[nblocks*8]; memset(ks_bytes, 0, sizeof(ks_bytes));
|
||||
des_multi_keystream_output (state->payload_mi, state->R, ks_bytes, 1, nblocks);
|
||||
unpack_byte_array_into_bit_array(ks_bytes, ks, nblocks*8);
|
||||
}
|
||||
//AES
|
||||
else if (state->payload_algid == 3 && state->aes_key_loaded[1] == 1)
|
||||
{
|
||||
// fprintf (stderr, " KS: %016llX;", state->K4); //stub
|
||||
int nblocks = total_bytes/16;
|
||||
if (total_bytes%16)
|
||||
nblocks++;
|
||||
uint8_t ks_bytes[nblocks*16]; memset(ks_bytes, 0, sizeof(ks_bytes));
|
||||
aes_ofb_keystream_output (state->aes_ivR, state->aes_key, ks_bytes, 2, nblocks);
|
||||
unpack_byte_array_into_bit_array(ks_bytes, ks, nblocks*16);
|
||||
}
|
||||
|
||||
//Debug Data Dump (before KS)
|
||||
// fprintf (stderr, "\n DATA: ");
|
||||
// for (int i = 0; i < total_bytes; i++)
|
||||
// fprintf (stderr, "%02X", (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0]+(i*8), 8));
|
||||
|
||||
//apply KS
|
||||
for (int i = 0; i < total_bytes*8; i++)
|
||||
state->dmr_pdu_sf[0][i] ^= ks[i];
|
||||
|
||||
//Message CRC calculated using the entire User Data is included in User Data
|
||||
//area in the User Data format used to send the last block, so the CRC is encrypted (...genius)
|
||||
|
||||
ptr = (total_bytes * 8) - 32;
|
||||
uint32_t crc_ext = convert_bits_into_output(state->dmr_pdu_sf[0]+ptr, 32);
|
||||
uint32_t crc_chk = nxdn_message_crc32(state->dmr_pdu_sf[0], ptr);
|
||||
|
||||
//Debug Data Dump
|
||||
fprintf (stderr, "\n DATA: ");
|
||||
for (int i = 0; i < total_bytes; i++)
|
||||
fprintf (stderr, "%02X", (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0]+(i*8), 8));
|
||||
|
||||
//debug
|
||||
// fprintf (stderr, " CRC DATA LEN: %03d;", ptr);
|
||||
// fprintf (stderr, " CRC: %08X / %08X;", crc_ext, crc_chk);
|
||||
|
||||
if (crc_ext == crc_chk)
|
||||
{
|
||||
|
||||
if ((uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0], 8) == 0x06)
|
||||
{
|
||||
//String Data Dump for nmea if first byte is 0x06
|
||||
fprintf (stderr, "\n NMEA: ");
|
||||
for (int i = 1; i < total_bytes; i++)
|
||||
fprintf (stderr, "%c", (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0]+(i*8), 8));
|
||||
}
|
||||
// else
|
||||
// {
|
||||
|
||||
// //String Data Dump (not suitable unless this is text)
|
||||
// fprintf (stderr, "\n String: ");
|
||||
// for (int i = 0; i < total_bytes; i++)
|
||||
// fprintf (stderr, "%c", (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0]+(i*8), 8));
|
||||
// }
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, " CRC: %08X / %08X;", crc_ext, crc_chk);
|
||||
fprintf (stderr, " (CRC ERR) ");
|
||||
}
|
||||
|
||||
//clear storage
|
||||
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
|
||||
|
||||
//reset block number
|
||||
state->data_header_blocks[0] = 1;
|
||||
|
||||
//reset delivery format (type)
|
||||
state->data_header_format[0] = 0;
|
||||
|
||||
//reset header validity
|
||||
state->data_header_valid[0] = 0;
|
||||
|
||||
//reset encryption variables
|
||||
state->payload_algid = 0;
|
||||
state->payload_keyid = 0;
|
||||
state->payload_mi = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
char * NXDN_Call_Type_To_Str(uint8_t CallType)
|
||||
{
|
||||
char * Ptr = NULL;
|
||||
@@ -2409,6 +2926,40 @@ char * NXDN_Call_Type_To_Str(uint8_t CallType)
|
||||
return Ptr;
|
||||
} /* End NXDN_Call_Type_To_Str() */
|
||||
|
||||
void NXDN_Data_Call_Option_To_Str(uint8_t DataCallOption, uint8_t * Duplex, uint8_t * TransmissionMode)
|
||||
{
|
||||
char * Ptr = NULL;
|
||||
|
||||
Duplex[0] = 0;
|
||||
TransmissionMode[0] = 0;
|
||||
|
||||
if(DataCallOption & 0x10) strcpy((char *)Duplex, "Duplex"); //SU is in Simplex
|
||||
else strcpy((char *)Duplex, "Half Duplex");
|
||||
|
||||
switch(DataCallOption & 0xF)
|
||||
{ //added all options, getting a random one on an NXDN96 System, need to know what it is
|
||||
case 0: Ptr = "4800bps"; break;
|
||||
case 1: Ptr = "Reserved 1"; break;
|
||||
case 2: Ptr = "9600bps"; break;
|
||||
case 3: Ptr = "Reserved 3"; break;
|
||||
case 4: Ptr = "Reserved 4"; break;
|
||||
case 5: Ptr = "Reserved 5"; break;
|
||||
case 6: Ptr = "Reserved 6"; break;
|
||||
case 7: Ptr = "Reserved 7"; break;
|
||||
case 8: Ptr = "4800bps S:1"; break; //spare bit enabled
|
||||
case 9: Ptr = "Reserved 9; S:1"; break; //spare bit enabled
|
||||
case 10: Ptr = "9600bps S:1"; break; //spare bit enabled
|
||||
case 11: Ptr = "9600bps S:1"; break; //spare bit enabled
|
||||
case 12: Ptr = "Reserved C; S1;"; break; //spare bit enabled
|
||||
case 13: Ptr = "Reserved D; S1"; break; //spare bit enabled
|
||||
case 14: Ptr = "Reserved E; S1"; break; //spare bit enabled
|
||||
case 15: Ptr = "Reserved F: S1"; break; //spare bit enabled
|
||||
default: Ptr = "Unk;"; break; //should never get here
|
||||
}
|
||||
|
||||
strcpy((char *)TransmissionMode, Ptr);
|
||||
} /* End NXDN_Voice_Call_Option_To_Str() */
|
||||
|
||||
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode)
|
||||
{
|
||||
char * Ptr = NULL;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2026-01 DSD-FME Florida Man Edition
|
||||
* 2026-03 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
#define LICH_ERR_THRESHOLD 7 //Threshold for lich dibit "dividing" errors (8 is perfect, 7 is 1 bit error)
|
||||
// #define NXDN_DEBUG_LICH //print LICH debug info on err on payload == 1
|
||||
#define NXDN_LICH_OFFBITS //use the offbits to help determine sync status (disable if bad signal / bad sample)
|
||||
// #define NXDN_LICH_OFFBITS //use the offbits to help determine sync status (disable if bad signal / bad sample)
|
||||
|
||||
//NXDN Type Enablement (handled by cmake -DTYPEC=OFF , -DTYPED=OFF, -DDCR=OFF)
|
||||
// #define JPNDCR_DECODE //Include Japanese DCR Decoding
|
||||
|
||||
Reference in New Issue
Block a user