NXDN: Soft Decision Viterbi;

NXDN: Tweak to LICH Dibit "Dividing" Error Threshold (1 to 2);

NXDN: Change MessageType to 8-bit and Decoding to Include the F1 and F2 Flag; Make Tweaks for DCR/ARIB Messages to compensate;

NXDN: Frame Sync Tweaks to help reduce false sync patterns; Make Inverted NXDN user option; cmake enabled defines for Type-C, Type-D, and DCR decoding (all enabled by default);

NXDN: Decode DST_ID_INFO Message;

NXDN: Rework Code on Standard Caller Alias and add a 16bit alias variants for ASCII, SJIS, BIG5, or UTF16 alias decoding (configure via cmake, default is ASCII);

NXDN: Seperate PROP_FORM 0x3F from Alias via checkdown;

YSF: Soft Decision Viterbi;

YSF: Fix BUMBLEBEETUNA text string on partial decode;

P25p2: Fix Harris GPS MAC message to fixed len, or do not decode (observed bugfix);

P25p2: Add MFID90 Unknown 0x8F and 0xBF MAC Message;

DOCS: Update complete_usage_options; Remove M17 Encoding Section;
This commit is contained in:
lwvmobile
2026-01-30 17:52:07 -05:00
parent a26a680fd6
commit 56b491ed3e
18 changed files with 5395 additions and 2429 deletions

View File

@@ -39,6 +39,55 @@ if (LZ)
add_definitions(-DLIMAZULUTWEAKS)
endif ()
#use cmake option -DASCII=ON to enable ASCII only Alias Support in NXDN
option(ASCII
"Build with ASCII only Alias Support in NXDN Enabled" ON)
if (ASCII)
add_definitions(-DASCII_DECODE)
endif ()
#use cmake option -DASCII=OFF -DBIG5=ON to enable ASCII and BIG5 Alias Support in NXDN
option(BIG5
"Build with BIG5 and ASCII Alias Support in NXDN Enabled" OFF)
if (BIG5)
add_definitions(-DBIG5_DECODE)
endif ()
#use cmake option -DASCII=OFF -DSJIS=ON to enable ASCII and SJIS Alias Support in NXDN
option(SJIS
"Build with SJIS and ASCII Alias Support in NXDN Enabled" OFF)
if (SJIS)
add_definitions(-DSJIS_DECODE)
endif ()
#use cmake option -DASCII=OFF -DUTF16=ON to enable ASCII and UTF16BE Alias Support in NXDN
option(UTF16
"Build with UTF16BE and ASCII Alias Support in NXDN Enabled" OFF)
if (UTF16)
add_definitions(-DUTF16_DECODE)
endif ()
#use cmake option -DTYPEC=OFF to disable NXDN Type-C and Conventional Decoding (enabled by default)
option(TYPEC
"Build with NXDN Type-C and Conventional Decoding Enabled" ON)
if (TYPEC)
add_definitions(-DTYPE_C_DECODE)
endif ()
#use cmake option -DTYPED=OFF to disable NXDN Type-D / IDAS Decoding (enabled by default)
option(TYPED
"Build with NXDN Type-D / IDAS Decoding Enabled" ON)
if (TYPED)
add_definitions(-DTYPE_D_DECODE)
endif ()
#use cmake option -DDCR=OFF to disable Japanese DCR (NXDN Variant) Decoding (enabled by default)
option(DCR
"Build with Japanese DCR (NXDN Variant) Decoding Enabled" ON)
if (DCR)
add_definitions(-DJPNDCR_DECODE)
endif ()
#use cmake option -DSID=ON to enable P25p1 Soft ID decoding -- Experimental
option(SID
"Build with P25p1 LSD/Soft ID Enabled" OFF)

View File

@@ -18,7 +18,7 @@
-f2 P25P2 (may need to specify wacn/sys/nac manually if traffic channel)
-fx X2-TDMA
-fy YSF
-fz M17 Audio
-fz M17 Stream
```
## Conventional Frequency Scanner
@@ -311,70 +311,3 @@ On the remote side where dsd-fme is, use the TCP input:
`dsd-fme -i tcp:192.168.7.8:1234 -N`
Note: As always, when using rtl_fm, play with `-l` and `-g` to find a good squelch and gain values for your environment - you will know if the squelch is the right value when `input.wav` does not increase in size in the absence of useful signal on the monitored frequency.
## Using the Experimental M17 Encoder (and Decoder)
You can use the experimental M17 Voice Stream and Packet encoder with the following examples.
Encode/Decode Local Feedback Voice Stream (Single Session):
`dsd-fme -fZ -i pulse -o pulse -M M17:3:SP5WWP:BROADCAST:48000`
Using the `-8` option (raw audio monitor) will instead send encoded raw audio to output instead of decoded audio. For example,
To send a voice stream out of stdout into SOCAT TCP to a second/remote DSD-FME session to decode:
Encode (Upper Z):
`dsd-fme -fZ -8 -i pulse -M M17:9:LWVMOBILE:DSD-FME:48000 -o - | socat stdio tcp-listen:7355,forever,reuseaddr`
Decode (Lower z):
`dsd-fme -fz -i tcp:192.168.7.8:7355`
NOTE: When using tcp input for M17 decoding, DSD-FME will keep the TCP connection alive with constant retries, so no need to restart the decoding session on connection failure.
SMS Packet Encoder (Text Only):
`dsd-fme -fP -8 -S "This is a sample message sent over M17 Packet Data using DSD-FME to encode it."`
SMS Packet Encoder (over SOCAT TCP):
`dsd-fme -fP -8 -S "This is a sample message sent over M17 Packet Data using DSD-FME to encode it and SOCAT to send it over TCP." -o - | socat stdio tcp-listen:7355,forever,reuseaddr`
Many combinations of input and output can be used as well, and using features like raw audio saving with `-6 m17str.wav` will save a wav file of your encoded audio to be replayed later with `-i m17str.wav`
Also note, features function similarly, like -Z for payload, and input options include pulse, oss, stdin, rtl, tcp, etc, using those as the mic in for voice encoding. With the right setup of input/output/input using SOCAT TCP pipes, it is conceivable to use DSD-FME to convert analog audio from NFM into M17 audio, or even change another digital format supported by DSD-FME into M17 audio, if desired (purely for research and fun purposes). Ncurses terminal will function with internal encoder/decoder and wav file creation, with toggle encode/tx with `\` key, but will not function with stdout properly.
The `-M` switch is used to specify user M17 variables, the string is in the format of M17:CAN:SRC:DST:INPUT_RATE.
CAN 1-15; SRC and DST have to be no more than 9 UPPER base40 characters.
BASE40: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.'
Input Rate Default is 48000; Use Multiples of 8000 up to 48000.
Values not entered into the M17: string are set to default values. M17:7:DSD-FME:DSD-FME:48000
Fun Example Transmuting OTA Analog Things into M17:
Start up in sequence.
Terminal #1 (RTL Input):
`dsd-fme -fZ -8 -M M17:1:ANALOG:M17:48000 -i rtl:0:162.425M -o - | socat stdio tcp-listen:7310,forever,reuseaddr`
Terminal #2:
`dsd-fme -fz -i tcp:127.0.0.1:7310`
Fun Example Transmuting OTA Digital Things into M17:
Start up all three of these in sequence.
Terminal #1 (connecting to SDR++):
`dsd-fme -fi -i tcp:127.0.0.1:7355 -o - | socat stdio tcp-listen:7300,forever,reuseaddr`
Terminal #2:
`dsd-fme -fZ -8 -M M17:2:NXDN:M17:8000 -i tcp:127.0.0.1:7300 -o - | socat stdio tcp-listen:7301,forever,reuseaddr`
Terminal #3:
`dsd-fme -fz -i tcp:127.0.0.1:7301`
Note: Transmuting only carries over voice when audio is output, and will not carry any ID values, or any other data payloads.
This mode is only meant for educational or fun purposes, please do not retransmit stuff that doesn't belong to you.
Information on this section will be updated as features are added or modified, etc. If you need help with a particular function and can't find a suitable answer, always open a new issue here, or make contact on the radio reference forum for additional examples, guidance, ideas, help, etc.

View File

@@ -1,4 +1,4 @@
Build Version: AW 2025-209-g14edf32
Build Version: AW 2026-1-g99f4228
MBElib Version: 1.3.4
CODEC2 Support Enabled
@@ -103,6 +103,7 @@ Decoder options:
-xx Expect non-inverted X2-TDMA signal
-xr Expect inverted DMR signal
-xd Expect inverted ICOM dPMR signal
-xn Expect inverted NXDN/DCR/IDAS signal
* denotes frame types that cannot be auto-detected.
** Phase 2 Single Frequency may require user to manually set WACN/SYSID/CC parameters if MAC_SIGNAL not present.

View File

@@ -354,6 +354,7 @@ typedef struct
int uvquality;
int inverted_x2tdma;
int inverted_dmr;
int inverted_nxdn;
int mod_threshold;
int ssize;
int msize;
@@ -1262,25 +1263,25 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state);
uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c);
void ncursesClose ();
//new NXDN Functions start here!
//NXDN Functions Start Here
void nxdn_frame (dsd_opts * opts, dsd_state * state);
void nxdn_pn95_dibit_scrambler(dsd_state * state, uint8_t * dibits, int len);
//nxdn deinterleaving/depuncturing functions
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type);
//type-d 'idas' deinterleaving/depuncturing functions
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction);
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type);
//DCR Mode
void nxdn_deperm_sacch2(dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
void nxdn_deperm_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t bits[144], uint8_t lich);
//MT and Voice
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
//Osmocom OP25 12 Rate Trellis Decoder (for NXDN, M17, YSF, etc)
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len);
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t * dbuf);
//All-in-One NXDN Soft Decision Viterbi Function based on libM17
uint32_t nxdn_soft_decision_viterbi(uint8_t * bits, const uint16_t * interleave, uint8_t * puncture, int d_len, int p_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes);
//NXDN Conventional and Type-C
void nxdn_facch1(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void nxdn_sacch(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void nxdn_cac(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void nxdn_facch2_udch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t type);
//type-d 'idas'
void idas_scch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t direction);
void idas_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t type);
//Japanese DCR
void dcr_sacch(dsd_opts * opts, dsd_state * state, uint8_t * bits);
void dcr_pich_tch(dsd_opts * opts, dsd_state * state, uint8_t * bits, uint8_t lich);
//OP25 NXDN CRC functions
int load_i(const uint8_t val[], int len);
@@ -1290,12 +1291,8 @@ 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
/* NXDN Convolution functions */
void CNXDNConvolution_start(void);
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
void CNXDNConvolution_init();
//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);
//libM17 viterbi decoder
@@ -1311,6 +1308,9 @@ uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len);
void viterbi_reset(void);
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2);
//libm17 based soft decision viterbi for variable len and new punctures
void slice_symbols_to_len(uint16_t * out, const float * inp, int len);
//keeping these
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
@@ -1321,17 +1321,21 @@ 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);
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
//added these
void NXDN_decode_Prop(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_Alias16(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void nxdn_decode_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);
//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);
void NXDN_decode_ALIAS_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Message);
uint32_t sjis_char_to_unicode(uint16_t sjis);
uint32_t big5_char_to_unicode(uint16_t big5);
int utf8_encode(int c, char *buf);
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);

View File

@@ -16,7 +16,7 @@
// Software Foundation, Inc., 51 Franklin Street, Boston, MA
// 02110-1301, USA.
static const uint8_t PERM_12_5[] = {
static const uint16_t PERM_12_5[] = {
0,12,24,36,48,
1,13,25,37,49,
2,14,26,38,50,
@@ -31,7 +31,7 @@ static const uint8_t PERM_12_5[] = {
11,23,35,47,59
};
static const uint8_t PERM_16_9[] = {
static const uint16_t PERM_16_9[] = {
0, 16, 32, 48, 64, 80, 96, 112, 128,
1, 17, 33, 49, 65, 81, 97, 113, 129,
2, 18, 34, 50, 66, 82, 98, 114, 130,

View File

@@ -187,7 +187,7 @@ dmr_filter(short sample)
short
nxdn_filter(short sample)
{
return dsd_input_filter(sample, 4);
return dsd_input_filter(sample, 2);
}
short

View File

@@ -1141,8 +1141,56 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
}
//NXDN
else if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
// //NXDN (previous sync, revert if issues / complaints arise)
// else if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
// {
// strncpy (synctest10, (synctest_p - 9), 10); //FSW only
// if (
// (strcmp (synctest10, "3131331131") == 0 ) //this seems to be the most common 'correct' pattern on Type-C
// || (strcmp (synctest10, "3331331131") == 0 ) //this one hits on new sync but gives a bad lich code
// || (strcmp (synctest10, "3131331111") == 0 )
// || (strcmp (synctest10, "3331331111") == 0 )
// || (strcmp (synctest10, "3131311131") == 0 ) //First few FSW on NXDN48 Type-C seems to hit this for some reason
// )
// {
// if (state->lastsynctype == 28)
// {
// state->offset = synctest_pos;
// state->max = ((state->max) + lmax) / 2;
// state->min = ((state->min) + lmin) / 2;
// state->last_cc_sync_time = time(NULL);
// return (28);
// }
// state->lastsynctype = 28;
// }
// else if (
// (strcmp (synctest10, "1313113313") == 0 )
// || (strcmp (synctest10, "1113113313") == 0 )
// || (strcmp (synctest10, "1313113333") == 0 )
// || (strcmp (synctest10, "1113113333") == 0 )
// || (strcmp (synctest10, "1313133313") == 0 )
// )
// {
// if (state->lastsynctype == 29)
// {
// state->offset = synctest_pos;
// state->max = ((state->max) + lmax) / 2;
// state->min = ((state->min) + lmin) / 2;
// state->last_cc_sync_time = time(NULL);
// return (29);
// }
// state->lastsynctype = 29;
// }
// }
//NXDN (positive sync only)
else if (opts->inverted_nxdn == 0 && ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1)) )
{
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
if (
@@ -1155,38 +1203,44 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 28)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
state->last_cc_sync_time = time(NULL);
return (28);
}
state->lastsynctype = 28;
}
else if (
}
(strcmp (synctest10, "1313113313") == 0 )
|| (strcmp (synctest10, "1113113313") == 0 )
|| (strcmp (synctest10, "1313113333") == 0 )
|| (strcmp (synctest10, "1113113333") == 0 )
|| (strcmp (synctest10, "1313133313") == 0 )
//NXDN (inverted sync only)
else if (opts->inverted_nxdn == 1 && ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1)) )
{
strncpy (synctest10, (synctest_p - 9), 10); //FSW only
if (
(strcmp (synctest10, "1313113313") == 0 )
|| (strcmp (synctest10, "1113113313") == 0 )
|| (strcmp (synctest10, "1313113333") == 0 )
|| (strcmp (synctest10, "1113113333") == 0 )
|| (strcmp (synctest10, "1313133313") == 0 )
)
)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (state->lastsynctype == 29)
{
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
state->last_cc_sync_time = time(NULL);
return (29);
}
state->lastsynctype = 29;
}
}
//Provoice Conventional -- Some False Positives due to shortened frame sync pattern, so use squelch if possible

View File

@@ -678,6 +678,7 @@
opts->uvquality = 3;
opts->inverted_x2tdma = 1; // most transmitter + scanner + sound card combinations show inverted signals for this
opts->inverted_dmr = 0; // most transmitter + scanner + sound card combinations show non-inverted signals for this
opts->inverted_nxdn = 0; //only seek pos by default to reduce false sync pattern detections
opts->mod_threshold = 26;
opts->ssize = 128; //36 default, max is 128, much cleaner data decodes on Phase 2 cqpsk at max
opts->msize = 1024; //15 default, max is 1024, much cleaner data decodes on Phase 2 cqpsk at max
@@ -1489,6 +1490,7 @@
printf (" -xx Expect non-inverted X2-TDMA signal\n");
printf (" -xr Expect inverted DMR signal\n");
printf (" -xd Expect inverted ICOM dPMR signal\n");
printf (" -xn Expect inverted NXDN/DCR/IDAS signal\n");
printf ("\n");
printf (" * denotes frame types that cannot be auto-detected.\n");
printf (" ** Phase 2 Single Frequency may require user to manually set WACN/SYSID/CC parameters if MAC_SIGNAL not present.\n");
@@ -1882,7 +1884,7 @@
init_audio_filters(&state); //audio filters
init_rrc_filter_memory(); //initialize input filtering
InitAllFecFunction();
CNXDNConvolution_init();
exitflag = 0;
@@ -1928,10 +1930,10 @@
fprintf (stderr, "TG Hold set to %d \n", state.tg_hold);
break;
//experimental audio monitoring
//raw audio monitoring
case '8':
opts.monitor_input_audio = 1;
fprintf (stderr,"Experimental Raw Analog Source Monitoring Enabled (Pulse Audio Only!)\n");
fprintf (stderr,"Raw Analog Source Monitoring Enabled.\n");
break;
//rc4 enforcement on DMR (due to missing the PI header)
@@ -2757,6 +2759,7 @@
sprintf (opts.output_name, "NXDN48");
fprintf (stderr,"Setting symbol rate to 2400 / second\n");
fprintf (stderr,"Decoding only NXDN 4800 baud frames.\n");
fprintf(stderr, "Notice: NXDN autodetect polarity disabled. \n Use -xn option if Inverted Signal expected.\n");
}
else if (optarg[0] == 'y')
{
@@ -2892,6 +2895,7 @@
// opts.setmod_bw = 12000; //causing issues
sprintf (opts.output_name, "NXDN96");
fprintf (stderr,"Decoding only NXDN 9600 baud frames.\n");
fprintf(stderr, "Notice: NXDN autodetect polarity disabled. \n Use -xn option if Inverted Signal expected.\n");
}
else if (optarg[0] == 'r')
{
@@ -3077,6 +3081,11 @@
opts.inverted_dpmr = 1;
fprintf (stderr, "Expecting inverted ICOM dPMR signals.\n");
}
else if (optarg[0] == 'n')
{
opts.inverted_nxdn = 1;
fprintf (stderr, "Expecting inverted NXDN/DCR/IDAS signals.\n");
}
break;
case 'r':

View File

@@ -8,84 +8,6 @@
#include "dsd.h"
static const int PARITY[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};
// trellis_1_2 encode: source is in bits, result in bits
void trellis_encode(uint8_t result[], const uint8_t source[], int result_len, int reg)
{
for (int i=0; i<result_len; i+=2) {
reg = (reg << 1) | source[i>>1];
result[i] = PARITY[reg & 0x19];
result[i+1] = PARITY[reg & 0x17];
}
}
// simplified trellis 2:1 decode; source and result in bits
// assumes that encoding was done with NTEST trailing zero bits
// result_len should be set to the actual number of data bits
// in the original unencoded message (excl. these trailing bits)
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len)
{
int reg = 0;
int min_d;
int min_bt;
static const int NTEST = 4;
static const int NTESTC = 1 << NTEST;
uint8_t bt[NTEST];
uint8_t tt[NTEST*2];
int dstats[4];
int sum;
for (int p=0; p < 4; p++)
dstats[p] = 0;
for (int p=0; p < result_len; p++) {
for (int i=0; i<NTESTC; i++) {
bt[0] = (i&8)>>3;
bt[1] = (i&4)>>2;
bt[2] = (i&2)>>1;
bt[3] = (i&1);
trellis_encode(tt, bt, NTEST*2, reg);
sum=0;
for (int j=0; j<NTEST*2; j++) {
sum += tt[j] ^ source[p*2+j];
}
if (i == 0 || sum < min_d) {
min_d = sum;
min_bt = bt[0];
}
}
result[p] = min_bt;
reg = (reg << 1) | min_bt;
dstats[(min_d > 3) ? 3 : min_d] += 1;
}
//debug output
// fprintf (stderr, "\n stats\t%d %d %d %d\n", dstats[0], dstats[1], dstats[2], dstats[3]);
}
//Original Copyright/License
/* -*- c++ -*- */
/*
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
*
* This file is part of OP25
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
//Ripped from libM17
#define K 5 //constraint length
#define NUM_STATES (1 << (K - 1)) //number of states
@@ -94,7 +16,7 @@ static uint32_t prevMetrics[NUM_STATES];
static uint32_t currMetrics[NUM_STATES];
static uint32_t prevMetricsData[NUM_STATES];
static uint32_t currMetricsData[NUM_STATES];
static uint16_t viterbi_history[244];
static uint16_t viterbi_history[400]; //CAC needs more, expand to 400 for testing
/**
* @brief Decode unpunctured convolutionally encoded data.
@@ -106,8 +28,9 @@ static uint16_t viterbi_history[244];
*/
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
{
if(len > 244*2)
fprintf(stderr, "Input size exceeds max history\n");
//variable in len
// if(len > 244*2)
// fprintf(stderr, "Input size exceeds max history\n");
viterbi_reset();
@@ -142,11 +65,12 @@ uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
*/
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len)
{
if(in_len > 244*2)
fprintf(stderr, "Input size exceeds max history\n");
//variable len now
// if(in_len > 244*2)
// fprintf(stderr, "Input size exceeds max history\n");
uint16_t umsg[244*2]; //unpunctured message
uint8_t p=0; //puncturer matrix entry
uint16_t umsg[400*2]; //unpunctured message
uint16_t p=0; //puncturer matrix entry
uint16_t u=0; //bits count - unpunctured message
uint16_t i=0; //bits read from the input message
@@ -168,7 +92,7 @@ uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_
}
//debug
// fprintf (stderr, " p: %d, u: %d; p_len: %d; len: %d;", p, u, p_len, (u-in_len)*0x7FFF);
// fprintf (stderr, " p: %d, u: %d; p_len: %d; ", p, u, p_len); //
return viterbi_decode(out, umsg, u) - (u-in_len)*0x7FFF;
}
@@ -249,7 +173,7 @@ uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len)
uint8_t state = 0;
size_t bitPos = len+4;
memset(out, 0, (len-1)/8+1);
// memset(out, 0, (len-1)/8+1); //upstream bugfix, but arrays passed here are already memset / zeroed out
while(pos > 0)
{
@@ -646,6 +570,57 @@ void slice_symbols(uint16_t out[2*SYM_PER_PLD], const float inp[SYM_PER_PLD])
}
}
/**
* @brief Slice payload symbols into soft dibits.
* Input (RRC filtered baseband sampled at symbol centers)
* should be already normalized to {-3, -1, +1 +3}.
* @param out Soft valued dibits (type-4).
* @param inp Array of len floats (1 sample per symbol).
* @param len legnth value to run this at
*/
void slice_symbols_to_len(uint16_t * out, const float * inp, int len)
{
for(int i=0; i<len; i++)
{
//bit 0
if(inp[i]>=symbol_list[3])
{
out[i*2+1]=0xFFFF;
}
else if(inp[i]>=symbol_list[2])
{
out[i*2+1]=-(float)0xFFFF/(symbol_list[3]-symbol_list[2])*symbol_list[2]+inp[i]*(float)0xFFFF/(symbol_list[3]-symbol_list[2]);
}
else if(inp[i]>=symbol_list[1])
{
out[i*2+1]=0x0000;
}
else if(inp[i]>=symbol_list[0])
{
out[i*2+1]=(float)0xFFFF/(symbol_list[1]-symbol_list[0])*symbol_list[1]-inp[i]*(float)0xFFFF/(symbol_list[1]-symbol_list[0]);
}
else
{
out[i*2+1]=0xFFFF;
}
//bit 1
if(inp[i]>=symbol_list[2])
{
out[i*2]=0x0000;
}
else if(inp[i]>=symbol_list[1])
{
out[i*2]=0x7FFF-inp[i]*(float)0xFFFF/(symbol_list[2]-symbol_list[1]);
}
else
{
out[i*2]=0xFFFF;
}
}
}
/**
* @brief Soft logic NOT.
*
@@ -680,7 +655,7 @@ void randomize_soft_bits(uint16_t inp[SYM_PER_PLD*2])
}
}
//interleaver pattern
//interleaver pattern for M17
const uint16_t intrl_seq[SYM_PER_PLD*2]=
{
0, 137, 90, 227, 180, 317, 270, 39, 360, 129, 82, 219, 172, 309, 262, 31,
@@ -719,3 +694,122 @@ void reorder_soft_bits(uint16_t outp[SYM_PER_PLD*2], const uint16_t inp[SYM_PER_
for(uint16_t i=0; i<SYM_PER_PLD*2; i++)
outp[i]=inp[intrl_seq[i]];
}
//NXDN all-in-one function to go from dibits to valid output, return viterbi error
uint32_t nxdn_soft_decision_viterbi(uint8_t * bits, const uint16_t * interleave, uint8_t * puncture, int d_len, int p_len, int num_bytes, int offset, uint8_t * viterbi_bits, uint8_t * viterbi_bytes)
{
uint32_t error = 0;
uint16_t soft_bit[1000];
uint8_t temp_bits[500];
memset(temp_bits, 0, sizeof(temp_bits));
memset(soft_bit, 0, sizeof(soft_bit));
//deinterleaved bits
uint8_t deinterleaved_bits[500];
memset(deinterleaved_bits, 0, sizeof(deinterleaved_bits));
//deinterleaving the bits
for (int i = 0; i < d_len; i++)
deinterleaved_bits[interleave[i]] = bits[i];
//converting back to dibits
uint8_t dbuf[500];
memset(dbuf, 0, sizeof(dbuf));
for (int i = 0; i < d_len/2; i++)
dbuf[i] = (deinterleaved_bits[(i*2)+0] << 1) | deinterleaved_bits[(i*2)+1];
float sbuf[500];
memset(sbuf, 0.0f, sizeof(sbuf));
//convert dbuf into a symbol array
for (int i = 0; i < d_len/2; i++)
{
if (dbuf[i] == 0) sbuf[i] = +1.0f;
else if (dbuf[i] == 1) sbuf[i] = +3.0f;
else if (dbuf[i] == 2) sbuf[i] = -1.0f;
else if (dbuf[i] == 3) sbuf[i] = -3.0f;
else sbuf[i] = +0.0f;
}
//slice symbols to soft dibits
slice_symbols_to_len(soft_bit, sbuf, d_len/2);
//viterbi
error = viterbi_decode_punctured(viterbi_bytes, soft_bit, puncture, d_len, p_len);
//debug
// fprintf (stderr, " Ve: %1.1f; ", (float)error/(float)0xFFFF);
//debug
// for (int i = 0; i < num_bytes+1; i++)
// fprintf(stderr, "%02X", viterbi_bytes[i]);
//load viterbi_bytes into bits
unpack_byte_array_into_bit_array(viterbi_bytes, temp_bits, num_bytes+1);
//rearrange bits to get correct offset
for (int i = 0; i < num_bytes*8; i++)
viterbi_bits[i] = temp_bits[i+offset];
// memset(viterbi_bytes, 0, sizeof(viterbi_bytes));
for (int i = 0; i < num_bytes; i++)
viterbi_bytes[i] = 0;
pack_bit_array_into_byte_array(viterbi_bits, viterbi_bytes, num_bytes);
return error;
}
//ysf fake puncture
uint8_t ysf_puncture[4] = {1,1,1,1};
//YSF all-in-one function to go from dibits to valid output, return viterbi error
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)
{
uint32_t error = 0;
uint16_t soft_bit[1000];
uint8_t temp_bits[500];
memset(temp_bits, 0, sizeof(temp_bits));
memset(soft_bit, 0, sizeof(soft_bit));
float sbuf[500];
memset(sbuf, 0.0f, sizeof(sbuf));
//convert dbuf into a symbol array
for (int i = 0; i < d_len/2; i++)
{
if (dbuf[i] == 0) sbuf[i] = +1.0f;
else if (dbuf[i] == 1) sbuf[i] = +3.0f;
else if (dbuf[i] == 2) sbuf[i] = -1.0f;
else if (dbuf[i] == 3) sbuf[i] = -3.0f;
else sbuf[i] = +0.0f;
}
//slice symbols to soft dibits
slice_symbols_to_len(soft_bit, sbuf, d_len/2);
//viterbi
error = viterbi_decode_punctured(viterbi_bytes, soft_bit, ysf_puncture, d_len, 4);
//debug
// fprintf (stderr, " Ve: %1.1f; ", (float)error/(float)0xFFFF);
//debug
// for (int i = 0; i < num_bytes+1; i++)
// fprintf(stderr, "%02X", viterbi_bytes[i]);
//load viterbi_bytes into bits
unpack_byte_array_into_bit_array(viterbi_bytes, temp_bits, num_bytes+1);
//rearrange bits to get correct offset
for (int i = 0; i < num_bytes*8; i++)
viterbi_bits[i] = temp_bits[i+offset];
// memset(viterbi_bytes, 0, sizeof(viterbi_bytes));
for (int i = 0; i < num_bytes; i++)
viterbi_bytes[i] = 0;
pack_bit_array_into_byte_array(viterbi_bits, viterbi_bytes, num_bytes);
return error;
}

View File

@@ -230,6 +230,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
opts->inverted_dpmr = 1;
opts->inverted_x2tdma = 1;
opts->inverted_ysf = 1;
opts->inverted_nxdn = 1;
}
else
{
@@ -237,6 +238,7 @@ uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c)
opts->inverted_dpmr = 0;
opts->inverted_x2tdma = 0;
opts->inverted_ysf = 0;
opts->inverted_nxdn = 0;
}
}

View File

@@ -1,173 +0,0 @@
/*
* Copyright (C) 2009-2016,2018 by Jonathan Naylor G4KLX
*
* Copyright (C) 2018 by Edouard Griffiths F4EXB:
* - Cosmetic changes to integrate with DSDcc
*
* Copyright (C) 2018 by Louis HERVE F4HUZ:
* - Transform C++ lib into C lib to integrate with DSD
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Include ------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
/* Define -------------------------------------------------------------------*/
/* Global variables ---------------------------------------------------------*/
static const uint8_t CNXDNConvolution_BIT_MASK_TABLE[8] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | CNXDNConvolution_BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
#define READ_BIT1(p,i) (p[(i)>>3] & CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
static const uint8_t CNXDNConvolution_BRANCH_TABLE1[8] = {0U, 0U, 0U, 0U, 2U, 2U, 2U, 2U};
static const uint8_t CNXDNConvolution_BRANCH_TABLE2[8] = {0U, 2U, 2U, 0U, 0U, 2U, 2U, 0U};
static const unsigned int CNXDNConvolution_NUM_OF_STATES_D2 = 8U;
static const uint32_t CNXDNConvolution_M = 4U;
static const unsigned int CNXDNConvolution_K = 5U;
//NOTE:
uint16_t m_metrics1[16];
uint16_t m_metrics2[16];
uint64_t m_decisions[8*300];
uint16_t * m_oldMetrics = NULL;
uint16_t * m_newMetrics = NULL;
uint64_t * m_dp = NULL;
/* Functions ----------------------------------------------------------------*/
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1)
{
uint8_t i = 0;
uint8_t j = 0;
uint8_t decision0 = 0;
uint8_t decision1 = 0;
uint16_t metric = 0;
uint16_t m0 = 0;
uint16_t m1 = 0;
uint16_t * tmp = NULL;
*m_dp = 0U;
for (i = 0U; i < CNXDNConvolution_NUM_OF_STATES_D2; i++)
{
j = i * 2U;
metric = abs(CNXDNConvolution_BRANCH_TABLE1[i] - s0) + abs(CNXDNConvolution_BRANCH_TABLE2[i] - s1);
m0 = m_oldMetrics[i] + metric;
m1 = m_oldMetrics[i + CNXDNConvolution_NUM_OF_STATES_D2] + (CNXDNConvolution_M - metric);
decision0 = (m0 >= m1) ? 1U : 0U;
m_newMetrics[j + 0U] = decision0 != 0U ? m1 : m0;
m0 = m_oldMetrics[i] + (CNXDNConvolution_M - metric);
m1 = m_oldMetrics[i + CNXDNConvolution_NUM_OF_STATES_D2] + metric;
decision1 = (m0 >= m1) ? 1U : 0U;
m_newMetrics[j + 1U] = decision1 != 0U ? m1 : m0;
*m_dp |= ((uint64_t)(decision1) << (j + 1U)) | ((uint64_t)(decision0) << (j + 0U));
}
++m_dp;
//assert((m_dp - m_decisions) <= 300);
tmp = m_oldMetrics;
m_oldMetrics = m_newMetrics;
m_newMetrics = tmp;
}
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits)
{
//assert(out != 0);
uint32_t state = 0U;
uint32_t i = 0;
uint8_t bit = 0;
while (nBits-- > 0)
{
--m_dp;
i = state >> (9 - CNXDNConvolution_K);
bit = (uint8_t)(*m_dp >> i) & 1;
state = (bit << 7) | (state >> 1);
WRITE_BIT1(out, nBits, bit != 0U);
}
}
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits)
{
//assert(in != 0);
//assert(out != 0);
//assert(nBits > 0U);
uint8_t g1 = 0U;
uint8_t g2 = 0U;
uint8_t d = 0U;
uint8_t d1 = 0U;
uint8_t d2 = 0U;
uint8_t d3 = 0U;
uint8_t d4 = 0U;
uint32_t k = 0U;
unsigned int i = 0U;
for (i = 0U; i < nBits; i++)
{
d = READ_BIT1(in, i) ? 1U : 0U;
g1 = (d + d3 + d4) & 1;
g2 = (d + d1 + d2 + d4) & 1;
d4 = d3;
d3 = d2;
d2 = d1;
d1 = d;
WRITE_BIT1(out, k, g1 != 0U);
k++;
WRITE_BIT1(out, k, g2 != 0U);
k++;
}
}
void CNXDNConvolution_start(void)
{
m_oldMetrics = m_metrics1;
m_newMetrics = m_metrics2;
m_dp = m_decisions;
}
void CNXDNConvolution_init()
{
memset(m_metrics1, 0x0, 16*sizeof(uint16_t));
memset(m_metrics2, 0x0, 16*sizeof(uint16_t));
memset(m_decisions,0x0, 8*300*sizeof(uint64_t));
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,29 @@
/*
============================================================================
Name : nxdn_element.c (formerly nxdn_lib)
Author :
Version : 1.0
Date : 2018 December 26
Copyright : No copyright
Description : NXDN decoding source lib - modified from nxdn_lib
Origin : Originally found at - https://github.com/LouisErigHerve/dsd
============================================================================
*/
/*-------------------------------------------------------------------------------
* nxdn_element.c
* NXDN Message Decoding
*
* Reworked portions from NXDN decoding source lib - modified from nxdn_lib
* Originally found at - https://github.com/LouisErigHerve/dsd
*
* LWVMOBILE
* 2026-01 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
//below items are in cmake as -DASCII=ON (default), or -DBIG5=ON , -DSJIS=ON , or -DUTF16=ON
// #define ASCII_DECODE //if using older ASCII only Alias Decoder, and not new one.
// #define SJIS_DECODE //if using ASCII/SJIS decoder, and not BIG5 decoder
// #define BIG5_DECODE //if using ASCII/BIG5 decoder, and not SJIS decoder
// #define UTF16_DECODE //if using straight UTF16 with Alias16 decoder (untested/not observed)
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state)
{
uint8_t SACCH[72]; //72
uint8_t sacch_bytes[9];
//below sizes are matching largest message carrier (facch2) when
//sending to content element decoder, we don't want to go address a memory range out of bounds
//so this is a safeguard to prevent doing so. This is same as viterbi bytes and bits sizes.
uint8_t SACCH[26*8]; //72
uint8_t sacch_bytes[26]; //9
uint32_t i;
uint8_t CrcCorrect = 1;
@@ -58,28 +66,14 @@ void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state)
} /* End NXDN_SACCH_Full_decode() */
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
uint8_t CrcCorrect, uint8_t * ElementsContent)
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t CrcCorrect, uint8_t * ElementsContent)
{
uint8_t MessageType;
/* Get the "Message Type" field */
MessageType = (ElementsContent[2] & 1) << 5;
MessageType |= (ElementsContent[3] & 1) << 4;
MessageType |= (ElementsContent[4] & 1) << 3;
MessageType |= (ElementsContent[5] & 1) << 2;
MessageType |= (ElementsContent[6] & 1) << 1;
MessageType |= (ElementsContent[7] & 1) << 0;
//Including Flags on bit 0 and bit 1 for more precise usage below
uint8_t MessageType = (uint8_t)convert_bits_into_output(ElementsContent+0, 8);
nxdn_message_type (opts, state, MessageType);
/* Save the "F1" and "F2" flags */
state->NxdnElementsContent.F1 = ElementsContent[0];
state->NxdnElementsContent.F2 = ElementsContent[1];
/* Save the "Message Type" field */
state->NxdnElementsContent.MessageType = MessageType;
/* Set the CRC state */
state->NxdnElementsContent.VCallCrcIsGood = CrcCorrect;
@@ -100,13 +94,13 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
//observed new messages in #318, should also be noted that F1 and F2 are both set on these messages
//VCALL and TX_REL custom to certain radios, but they have different elements in them
case 0x21:
case 0x28:
case 0xE1:
case 0xE8:
NXDN_decode_VCALL_ARIB(opts, state, ElementsContent);
break;
//Shift-JIS Talker Alias
case 0x27:
case 0xE7:
NXDN_decode_ALIAS_ARIB(opts, state, ElementsContent);
break;
@@ -126,9 +120,9 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
NXDN_decode_VCALL_ASSGN(opts, state, ElementsContent);
break;
//Alias 0x3F
//PROP_FORM 0x3F
case 0x3F:
NXDN_decode_Alias(opts, state, ElementsContent);
NXDN_decode_Prop(opts, state, ElementsContent);
break;
//SRV_INFO
@@ -143,6 +137,11 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
NXDN_decode_cch_info(opts, state, ElementsContent);
break;
//DST_ID_INFO
case 0x17:
nxdn_decode_dst_info(opts, state, ElementsContent);
break;
//SITE_INFO
case 0x18:
NXDN_decode_site_info(opts, state, ElementsContent);
@@ -165,14 +164,11 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
//DISC
case 0x11:
NXDN_decode_VCALL(opts, state, ElementsContent);
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
memset (state->generic_talker_alias[0], 0, sizeof(state->generic_talker_alias[0]));
sprintf (state->generic_talker_alias[0], "%s", "");
sprintf (state->call_string[0], "%s", "");
sprintf (state->nxdn_call_type, "%s", "");
// #ifdef LIMAZULUTWEAKS
// ; //do nothing -- testing errors on CAC messages when returning quickly from RTCH
// #else
//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)
{
@@ -749,95 +745,224 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
} /* End NXDN_decode_VCALL_ASSGN() */
//Multi Encoding Alias Decoder (ASCII, and SJIS or BIG5 or UTF16BE)
void NXDN_decode_Alias16(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint8_t mfid = (uint8_t)ConvertBitIntoBytes(&Message[8], 8);
uint16_t unk = (uint16_t)ConvertBitIntoBytes(&Message[16], 16);
uint8_t seg_num = (uint8_t)ConvertBitIntoBytes(&Message[32], 4);
uint8_t seg_len = (uint8_t)ConvertBitIntoBytes(&Message[36], 4);
uint8_t seg_bytes[12]; memset(seg_bytes, 0, sizeof(seg_bytes));
int seg_byte_num = 4;
for (int i = 0; i < seg_byte_num; i++)
seg_bytes[i] = (uint8_t)ConvertBitIntoBytes(&Message[(i*8)+40], 8);
if (opts->payload == 1)
{
fprintf (stderr, "\n Multi Segment Alias %d/%d; ", seg_num, seg_len);
for (int i = 0; i < seg_byte_num; i++)
fprintf (stderr, "%02X", seg_bytes[i]);
}
else fprintf (stderr, " (%d/%d) ", seg_num, seg_len);
//copy to PDU superframe
memcpy(state->dmr_pdu_sf[0]+(seg_num-1)*seg_byte_num, seg_bytes, seg_byte_num*sizeof(uint8_t));
int len = seg_byte_num * seg_len;
//TODO: Appears to have embedded 2 byte crc, populated up to 12 bits
//not a match (may need bitwise, or may need entire messages put together and not just alias)
// uint16_t crc_chk = crc12f(state->dmr_pdu_sf[0], 12);
//extract CRC
uint16_t crc_ext = (state->dmr_pdu_sf[0][len-2] << 8) | (state->dmr_pdu_sf[0][len-1] << 0);
//remove CRC bytes
if (len > 2)
len -= 2;
if (seg_num == seg_len)
{
if (opts->payload == 1)
{
fprintf (stderr, "\n Completed Alias Encoded: ");
for (int i = 0; i < (seg_len*seg_byte_num); i++)
fprintf (stderr, "%02X", state->dmr_pdu_sf[0][i]);
}
//Alias String
char alias[500]; memset(alias, 0, sizeof(alias));
int ptr = 0; //pointer to position in alias buffer
int k = 0; //pointer to position in completed message
uint16_t c16b = 0; //16-bit character encoded in BIG5 or SJIS
uint8_t utf8 = 0; //UTF-8 Character
uint32_t uni = 0; //Unicode Character returned from BIG5, or SJIS decoder
//SHIFT-JIS or BIG5 to UNICODE Conversion
setlocale(LC_ALL, ""); //needed when encoded alias contains CJK
if (opts->payload == 1)
fprintf (stderr, "\n MFID: %02X; Unk: %04X; Encoded Len: %d; CRC: %03X; Alias: ", mfid, unk, len, crc_ext);
for (int i = 0; i < len; i++)
{
uni = 0;
utf8 = state->dmr_pdu_sf[0][k];
c16b = (state->dmr_pdu_sf[0][k+0] << 8) | state->dmr_pdu_sf[0][k+1];
if (utf8 >= 0x20 && utf8 < 0x80)
{
uni = sjis_char_to_unicode(utf8);
fprintf (stderr, "%lc", uni);
k++;
}
else if (c16b != 0)
{
#ifdef BIG5_DECODE //BIG5 Tradional Chinese
uni = big5_char_to_unicode(c16b);
#elif SJIS_DECODE //SJIS for Japanese
uni = sjis_char_to_unicode(c16b);
#elif UTF16_DECODE //try as UTF16BE (untested through Unicode to UTF8 decoder)
uni = c16b; //UTF16BE
// uni = (state->dmr_pdu_sf[0][k+1] << 8) | state->dmr_pdu_sf[0][k+0]; //UTF16LE (for test only)
#endif
fprintf (stderr, "%lc", uni);
k+=2;
}
else if (utf8 == 0 && c16b == 0)
break;
//Encode Unicode to UTF-8 if not a pass-through ASCII control character or 0
if (uni >= 0x0020)
ptr += utf8_encode(uni, alias+ptr);
}
//terminate string failsafe
alias[ptr++] = 0x00;
alias[ptr++] = 0x00;
sprintf (state->generic_talker_alias[0], "%s", alias);
sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s; ", alias);
//reset PDU superframe afterwards
memset(state->dmr_pdu_sf[0], 0, sizeof(state->dmr_pdu_sf[0]));
}
} /* End NXDN_decode_Alias16 */
void NXDN_decode_Prop(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
fprintf (stderr, "%s", KYEL);
//Octet 1 is MFID value
uint8_t mfid = (uint8_t)convert_bits_into_output(Message+8, 8);
//check this message to see if it is an alias, or other unknown PROP_FORM
uint32_t alias_check = (uint16_t)convert_bits_into_output(Message+16, 16);
if (mfid == 0x68 && alias_check == 0x8204)
{
fprintf(stderr, " ALIAS");
#ifdef BIG5_DECODE
NXDN_decode_Alias16(opts, state, Message); //ASCII and 16-bit SJIS or BIG5 (depends on cmake define)
#elif SJIS_DECODE
NXDN_decode_Alias16(opts, state, Message); //ASCII and 16-bit SJIS or BIG5 (depends on cmake define)
#elif UTF16_DECODE
NXDN_decode_Alias16(opts, state, Message); //ASCII and 16-bit UTF16BE (untested) (depends on cmake define)
#elif ASCII_DECODE
NXDN_decode_Alias(opts, state, Message); //ASCII only version
#else
NXDN_decode_Alias(opts, state, Message); //ASCII only version (fallback if none selected by cmake)
#endif
}
else
{
fprintf(stderr, " PROP_FORM");
fprintf (stderr, "\n");
fprintf (stderr, " MFID: %02X; Message: ", mfid);
for (int i = 2; i < 9; i++)
fprintf (stderr, "%02X", (uint8_t)convert_bits_into_output(Message+(i*8), 8));
}
fprintf (stderr, "%s", KNRM);
}
//This version works well with ASCII, but no support for other encodings
void NXDN_decode_Alias(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
UNUSED(opts);
uint8_t Alias1 = 0x20;
uint8_t Alias2 = 0x20;
uint8_t Alias3 = 0x20;
uint8_t Alias4 = 0x20;
uint8_t blocknumber = 0;
uint8_t total = 0;
uint8_t unk1 = 0;
uint8_t unk2 = 0;
uint8_t CrcCorrect = 0;
//alias can also hit on a facch1 -- do we still need this checkdown?
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
// uint8_t mfid = (uint8_t)ConvertBitIntoBytes(&Message[8], 8); //PROP_FORM MFID value
// uint8_t unk1 = (uint8_t)ConvertBitIntoBytes(&Message[16], 8); //82
// uint8_t unk2 = (uint8_t)ConvertBitIntoBytes(&Message[24], 8); //04
//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
total = (uint8_t)ConvertBitIntoBytes(&Message[36], 4) & 0x7; //second value seems to be total number of blocks? or len of alias in this segment?
unk1 = (uint8_t)ConvertBitIntoBytes(&Message[8], 8); //unknown values in first two bytes after the opcode
unk2 = (uint8_t)ConvertBitIntoBytes(&Message[16], 8); //could be related to the format of these (Iso8? etc)
UNUSED4(unk1, unk2, total, blocknumber);
Alias1 = (uint8_t)ConvertBitIntoBytes(&Message[40], 8);
Alias2 = (uint8_t)ConvertBitIntoBytes(&Message[48], 8);
Alias3 = (uint8_t)ConvertBitIntoBytes(&Message[56], 8);
Alias4 = (uint8_t)ConvertBitIntoBytes(&Message[64], 8);
char str_a[120]; char str_b[50];
//TODO: Revisit the debug here and see if anythign comes out of it,
//maybe go on the assumptiont his works very similar to DMR talker alias
//debug/test
// fprintf (stderr, " U1: %02X U2: %02X;", unk1, unk2); //these always appear to be the same two byte values, even across different systems and languages
// fprintf (stderr, " A:%d/%d; ", blocknumber, total); //this is accurate info here
//sanity check to prevent OOB array assignment
if (blocknumber > 0 && blocknumber < 4) //last 'block' may have been assigning garbage name values -- I'm honestly not sure block'4' contains Alias data, but other data or something
{
//assign to index -1, since block number conveyed here is 1,2,3,4, and index values are 0,1,2,3
//only assign if within valid range of ascii characters (not including diacritical extended alphabet)
//else assign "space" ascii character
//since we are zeroing out the blocks on tx_rel and other conditions, better to just set nothing to bad Alias bytes
//tends to zero out otherwise already good blocks set in a previous repitition.
if (Alias1 > 0x19 && Alias1 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][0], "%c", Alias1);
// else sprintf (state->nxdn_alias_block_segment[blocknumber-1][0], "%c", 0x20); //space
if (Alias2 > 0x19 && Alias2 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][1], "%c", Alias2);
// else sprintf (state->nxdn_alias_block_segment[blocknumber-1][1], "%c", 0x20); //space
if (Alias3 > 0x19 && Alias3 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][2], "%c", Alias3);
// else sprintf (state->nxdn_alias_block_segment[blocknumber-1][2], "%c", 0x20); //space
if (Alias4 > 0x19 && Alias4 < 0x7F) sprintf (state->nxdn_alias_block_segment[blocknumber-1][3], "%c", Alias4);
// else sprintf (state->nxdn_alias_block_segment[blocknumber-1][3], "%c", 0x20); //space
sprintf (str_a, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
state->nxdn_alias_block_segment[0][0], state->nxdn_alias_block_segment[0][1], state->nxdn_alias_block_segment[0][2], state->nxdn_alias_block_segment[0][3],
state->nxdn_alias_block_segment[1][0], state->nxdn_alias_block_segment[1][1], state->nxdn_alias_block_segment[1][2], state->nxdn_alias_block_segment[1][3],
state->nxdn_alias_block_segment[2][0], state->nxdn_alias_block_segment[2][1], state->nxdn_alias_block_segment[2][2], state->nxdn_alias_block_segment[2][3],
state->nxdn_alias_block_segment[3][0], state->nxdn_alias_block_segment[3][1], state->nxdn_alias_block_segment[3][2], state->nxdn_alias_block_segment[3][3]);
//juggle strings here so we don't get compiler warnings on assignment size
memcpy (str_b, str_a, 48); str_b[49] = '\0';
sprintf (state->generic_talker_alias[0], "%s", str_b);
sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s; ", str_b);
}
uint8_t blocknumber = (uint8_t)ConvertBitIntoBytes(&Message[32], 4) & 0x7;
uint8_t total = (uint8_t)ConvertBitIntoBytes(&Message[36], 4) & 0x7;
//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, " "); //spacer
fprintf (stderr, " (%d/%d) ", blocknumber, total);
blocknumber--;
total--;
int ptr = blocknumber*4;
for (int i = 0; i < 4; i++)
state->generic_talker_alias[0][ptr++] = (char)ConvertBitIntoBytes(&Message[40+(i*8)], 8);
//on block 4, the last two (14, and 15) will not be an alias value, but a CRC value (seemingly)
state->generic_talker_alias[0][14] = '\0';
state->generic_talker_alias[0][15] = '\0';
//comb alias so far, if characters greater than ASCII 0x7F, this may be a UTF16 encoded alias
//May be more beneficial in long run to use the ARIB alias with SJIS and/or UTF16 to UTF8 in it.
//NOTE: This will not prevent 'splicing' issues if signal issue occurs and resumes with another alias
//this alias string is cleared on TX_REL, TX_REL_EXT, noCarrier, and few other items
for (int i = 0; i < 14; i++)
{
for (int j = 0; j < 4; j++)
{
fprintf (stderr, "%s", state->nxdn_alias_block_segment[i][j]);
}
//substitute with a space if early terminator (partial decode) or greater than end of ASCII
if (state->generic_talker_alias[0][i] < 0x20 || state->generic_talker_alias[0][i] > 0x7F)
state->generic_talker_alias[0][i] = 0x20;
}
fprintf (stderr, " ");
//walk backwards, remove any ending spaces with a terminator
for (int i = 13; i >= 1; i--)
{
if (state->generic_talker_alias[0][i] == 0x20)
state->generic_talker_alias[0][i] = '\0';
if (state->generic_talker_alias[0][i-1] != 0x20)
break;
}
sprintf (state->event_history_s[0].Event_History_Items[0].alias, "%s; ", state->generic_talker_alias[0]);
setlocale(LC_ALL, "");
// if (blocknumber == total)
{
if (opts->payload == 1)
fprintf (stderr, "\n Talker Alias: %s", state->generic_talker_alias[0]);
else fprintf (stderr, "%s", state->generic_talker_alias[0]);
}
}
else fprintf (stderr, " CRC ERR "); //redundant print? or okay?
}
@@ -939,6 +1064,64 @@ void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
fprintf (stderr, "%s", KNRM);
}
void nxdn_decode_dst_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
char station_id_string[26]; //FACCH2 maxes out at 26 (largest message carrier)
memset(station_id_string, 0, sizeof(station_id_string));
uint8_t start = Message[8];
uint8_t end = Message[9];
uint8_t option = convert_bits_into_output(Message+8, 8);
uint8_t num_chars = convert_bits_into_output(Message+10, 6);
//arbitrary value to stop on if not the first (not sure why first and not last? probably should be last)
if (!start)
num_chars = 24;
for (int i = 0; i < num_chars+1; i++)
{
uint8_t c = convert_bits_into_output(Message+16+(i*8), 8);
if (c >= 0x20 && c <= 0x7F) //in range of ASCII characters
station_id_string[i] = c;
}
station_id_string[25] = '\0';
fprintf (stderr, "%s", KYEL);
fprintf (stderr, "\n Station Identification Information - ");
if (start && end)
fprintf (stderr, "Full ");
else if (start)
fprintf (stderr, "First ");
else if (end)
fprintf (stderr, "Last ");
else fprintf (stderr, "Next ");
fprintf (stderr, "ID: %s ", station_id_string);
//push as a data event string
if (start && end)
{
char event_string[55]; memset(event_string, 0, sizeof(event_string));
sprintf (event_string, "NXDN Digital Station ID: %s", station_id_string);
watchdog_event_datacall (opts, state, 65520, 0, event_string, 0);
}
//debug
if (opts->payload == 1)
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
fprintf (stderr, "%s", KNRM);
}
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
{
uint32_t location_id = 0;
@@ -955,7 +1138,8 @@ void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message)
nxdn_location_id_handler (state, location_id, 0);
//run the srv info
nxdn_srv_info_handler (state, svc_info);
if (opts->payload == 1)
nxdn_srv_info_handler (state, svc_info);
//run the rst info, if not zero
if (rst_info) nxdn_rst_info_handler (state, rst_info);
@@ -1024,7 +1208,8 @@ void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message
nxdn_location_id_handler(state, location_id, 0);
//run the srv info
nxdn_srv_info_handler (state, svc_info);
if (opts->payload == 1)
nxdn_srv_info_handler (state, svc_info);
//run the rst info, if not zero
if (rst_info) nxdn_rst_info_handler (state, rst_info);
@@ -1410,8 +1595,8 @@ void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message)
state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0;
state->gi[0] = -1;
memset (state->generic_talker_alias[0], 0, sizeof(state->generic_talker_alias[0]));
sprintf (state->generic_talker_alias[0], "%s", "");
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
}
//set enc bit here so we can tell playSynthesizedVoice whether or not to play enc traffic
@@ -2085,8 +2270,8 @@ void NXDN_decode_VCALL_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Messag
state->nxdn_last_rid = 0;
state->nxdn_last_tg = 0;
state->gi[0] = -1;
memset (state->generic_talker_alias[0], 0, sizeof(state->generic_talker_alias[0]));
sprintf (state->generic_talker_alias[0], "%s", "");
memset (state->nxdn_alias_block_segment, 0, sizeof(state->nxdn_alias_block_segment));
}
//set enc bit here so we can tell playSynthesizedVoice whether or not to play enc traffic
@@ -2170,7 +2355,7 @@ void NXDN_decode_ALIAS_ARIB(dsd_opts * opts, dsd_state * state, uint8_t * Messag
utf8 = state->dmr_pdu_sf[0][k];
sjis = (state->dmr_pdu_sf[0][k] << 8) | state->dmr_pdu_sf[0][k+1];
if (utf8 >= 0x20 && utf8 < 0x7F)
if (utf8 >= 0x20 && utf8 < 0x80)
{
c16 = sjis_char_to_unicode(utf8);
fprintf (stderr, "%lc", c16);

View File

@@ -1,40 +1,32 @@
//NXDN frame handler
//Reworked portions from Osmocom OP25 rx_sync.cc
/* -*- c++ -*- */
/*
/*-------------------------------------------------------------------------------
* nxdn_frame.c
* NXDN Frame Handling
*
* Reworked portions from Osmocom OP25 rx_sync.cc
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
*
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
* LWVMOBILE
* 2026-01 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include "nxdn_const.h"
// #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 LICH_ERR_THRESHOLD 2 //Threshold for lich dibit "dividing" errors
// #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)
//NXDN Type Enablement (handled by cmake -DTYPEC=OFF , -DTYPED=OFF, -DDCR=OFF)
// #define JPNDCR_DECODE //Include Japanese DCR Decoding
// #define TYPE_D_DECODE //Include Type-D Decoding
// #define TYPE_C_DECODE //Include Type-C and Conventional Decoding (Basically the "normal" NXDN stuff)
void nxdn_frame (dsd_opts * opts, dsd_state * state)
{
// length is implicitly 192, with frame sync in first 10 dibits
uint8_t dbuf[182];
uint8_t lich;
uint8_t answer[32];
uint8_t sacch_answer[32];
uint8_t lich = 0;
int lich_parity_received;
int lich_parity_computed;
int voice = 0;
@@ -71,12 +63,9 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//nxdn bit buffer, for easy assignment handling
int nxdn_bit_buffer[364];
int nxdn_dibit_buffer[182];
//init all arrays
memset (dbuf, 0, sizeof(dbuf));
memset (answer, 0, sizeof(answer));
memset (sacch_answer, 0, sizeof(sacch_answer));
memset (lich_dibits, 0, sizeof(lich_dibits));
memset (sacch_bits, 0, sizeof(sacch_bits));
memset (facch_bits_b, 0, sizeof(facch_bits_b));
@@ -85,15 +74,18 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
memset (facch2_bits, 0, sizeof(facch2_bits));
memset (nxdn_bit_buffer, 0, sizeof(nxdn_bit_buffer));
memset (nxdn_dibit_buffer, 0, sizeof(nxdn_dibit_buffer));
//collect lich bits first, if they are good, then we can collect the rest of them
for (int i = 0; i < 8; i++) lich_dibits[i] = dbuf[i] = getDibit(opts, state);
nxdn_pn95_dibit_scrambler (state, lich_dibits, 8);
lich = 0;
for (int i=0; i<8; i++) lich |= (lich_dibits[i] >> 1) << (7-i);
//divide lich_dibits to get the lich code w/ parity bit on LSB
for (int i = 0; i < 8; i++)
{
lich <<= 1;
lich |= (lich_dibits[i] >> 1) & 1;
}
//debug lich as a 16-bit value (with encoding "dividing")
uint8_t lich_bits[16]; memset(lich_bits, 0, sizeof(lich_bits));
@@ -111,14 +103,13 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
for (int i=0; i<8; i++)
lich_off_hex += lich_bits[(i*2)+1];
#ifdef NXDN_LICH_OFFBITS
if (lich_off_hex < 7) //allow up to 1 bit error
if (lich_off_hex < LICH_ERR_THRESHOLD)
{
#ifdef NXDN_DEBUG_LICH
if (opts->payload == 1)
fprintf(stderr, " Lich Off Bit Fill Error: %d / 8; \n", lich_off_hex);
#endif
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
goto END;
}
#endif
@@ -127,9 +118,11 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
lich_parity_computed = ((lich_full >> 7) + (lich_full >> 6) + (lich_full >> 5) + (lich_full >> 4)) & 1;
lich = lich_full >> 1;
#ifdef JPNDCR_DECODE
//special cases on DCR where parity is computed over 7 bits, and not 4 bits
if (lich == 0x08 || lich == 0x4A || lich == 0x48 || lich == 0x46)
lich_parity_computed = ((lich_full >> 7) + (lich_full >> 6) + (lich_full >> 5) + (lich_full >> 4) + (lich_full >> 3) + (lich_full >> 2) + (lich_full >> 1)) & 1;
#endif
if (lich_parity_received != lich_parity_computed)
{
@@ -137,8 +130,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
if (opts->payload == 1)
fprintf(stderr, " Lich Parity Error %02X / %04X\n", lich_full, lich_bits_hex);
#endif
state->lastsynctype = -1;
goto END;
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
}
voice = 0;
@@ -156,150 +148,168 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
fprintf(stderr, " Simplex/Inbound NXDN lich on trunking system - type 0x%02X\n", lich);
#endif
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
goto END;
}
switch(lich) { //cases without breaks continue to flow downwards until they hit the break
case 0x01: // CAC types
case 0x05:
cac = 1;
break;
case 0x28: //facch2 types
case 0x29:
// case 0x48: //removing from here, moving to DCR as pich_tch
case 0x49:
facch2 = 1;
break;
case 0x2e: //udch types
case 0x2f:
case 0x4e:
case 0x4f:
udch = 1;
break;
case 0x32: //facch in 1, vch in 2
case 0x33:
case 0x52:
case 0x53:
voice = 2;
facch = 1;
sacch = 1;
break;
case 0x34: //vch in 1, facch in 2
case 0x35:
case 0x54:
case 0x55: //disabled for testing, IDAS system randomly triggers this one, probably due to poor signal
voice = 1;
facch = 2;
sacch = 1;
break;
case 0x36: //vch in both
case 0x37:
case 0x56:
case 0x57:
voice = 3;
facch = 0;
sacch = 1;
break;
case 0x20: //facch in both
case 0x21:
case 0x30:
case 0x31:
case 0x40:
case 0x41:
case 0x50:
case 0x51:
voice = 0;
facch = 3;
sacch = 1;
break;
case 0x38: //sacch only (NULL?)
case 0x39:
sacch = 1;
break;
switch(lich)
{
//DCR Voice
case 0x46:
voice = 3;
sacch2 = 1;
break;
#ifdef TYPE_C_DECODE
case 0x01: // CAC types
case 0x05:
cac = 1;
break;
case 0x28: //facch2 types
case 0x29:
// case 0x48: //removing from here, moving to DCR as pich_tch
case 0x49:
facch2 = 1;
break;
case 0x2e: //udch types
case 0x2f:
case 0x4e:
case 0x4f:
udch = 1;
break;
case 0x32: //facch in 1, vch in 2
case 0x33:
case 0x52:
case 0x53:
voice = 2;
facch = 1;
sacch = 1;
break;
case 0x34: //vch in 1, facch in 2
case 0x35:
case 0x54:
case 0x55:
voice = 1;
facch = 2;
sacch = 1;
break;
case 0x36: //vch in both
case 0x37:
case 0x56:
case 0x57:
voice = 3;
facch = 0;
sacch = 1;
break;
case 0x20: //facch in both
case 0x21:
case 0x30:
case 0x31:
case 0x40:
case 0x41:
case 0x50:
case 0x51:
voice = 0;
facch = 3;
sacch = 1;
break;
case 0x38: //sacch only (NULL?)
case 0x39:
sacch = 1;
break;
#endif
//DCR SB0, Data or End Frame
case 0x08: //SB0 w/ CSM 9 digit BCD found here
sacch2 = 1;
pich_tch = 1; //observed 2nd PICH is zero fill
break;
case 0x48:
pich_tch = 3; //may be 1, or 2 TCH (observed possibly both)
case 0x4A:
sacch2 = 1;
break;
#ifdef JPNDCR_DECODE
//DCR Voice
case 0x46:
voice = 3;
sacch2 = 1;
break;
//NXDN "Type-D" or "IDAS" Specific Lich Codes
case 0x76: //normal vch voice (in one and two)
case 0x77:
idas = 1;
scch = 1;
voice = 3;
break;
// case 0x74: //False Positive on DCR, keep disabled, or revert this line
case 0x75: //vch in 1, facch1 in 2 (facch 2 steal)
idas = 1;
scch = 1;
voice = 1;
facch = 2;
break;
case 0x72: //facch in 1, vch in 2 (facch 1 steal)
case 0x73:
idas = 1;
scch = 1;
voice = 2;
facch = 1;
break;
case 0x70: //facch steal in vch 1 and vch 2 (during voice only)
case 0x71:
idas = 1;
scch = 1;
facch = 3;
break;
case 0x6E: //udch2
case 0x6F:
idas = 1;
scch = 1;
udch2 = 1;
break;
case 0x68:
case 0x69: //facch3
idas = 1;
scch = 1;
facch3 = 1;
break;
case 0x62:
case 0x63: //facch1 in 1, null data and post field in 2
idas = 1;
scch = 1;
facch = 1;
break;
case 0x60:
case 0x61: //facch1 in both (non vch)
idas = 1;
scch = 1;
facch = 3;
break;
//DCR SB0, Data or End Frame
case 0x08: //SB0 w/ CSM 9 digit BCD found here
sacch2 = 1;
pich_tch = 1; //observed 2nd PICH is zero fill
break;
case 0x48:
pich_tch = 3; //may be 1, or 2 TCH (observed possibly both)
case 0x4A:
sacch2 = 1;
break;
#endif
default:
#ifdef NXDN_DEBUG_LICH
if (opts->payload == 1)
fprintf(stderr, " false sync or unsupported NXDN lich type L: %02X / LH: %04X\n", lich, lich_bits_hex);
#endif
//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, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
voice = 0;
goto END;
break;
#ifdef TYPE_D_DECODE
//NXDN "Type-D" or "IDAS" Specific Lich Codes
case 0x76: //normal vch voice (in one and two)
case 0x77:
idas = 1;
scch = 1;
voice = 3;
break;
// case 0x74: //False Positive on DCR, keep disabled, or revert this line
case 0x75: //vch in 1, facch1 in 2 (facch 2 steal)
idas = 1;
scch = 1;
voice = 1;
facch = 2;
break;
case 0x72: //facch in 1, vch in 2 (facch 1 steal)
case 0x73:
idas = 1;
scch = 1;
voice = 2;
facch = 1;
break;
case 0x70: //facch steal in vch 1 and vch 2 (during voice only)
case 0x71:
idas = 1;
scch = 1;
facch = 3;
break;
case 0x6E: //udch2
case 0x6F:
idas = 1;
scch = 1;
udch2 = 1;
break;
case 0x68:
case 0x69: //facch3
idas = 1;
scch = 1;
facch3 = 1;
break;
case 0x62:
case 0x63: //facch1 in 1, null data and post field in 2
idas = 1;
scch = 1;
facch = 1;
break;
case 0x60:
case 0x61: //facch1 in both (non vch)
idas = 1;
scch = 1;
facch = 3;
break;
#endif
default:
#ifdef NXDN_DEBUG_LICH
if (opts->payload == 1)
fprintf(stderr, " false sync or unsupported NXDN lich type L: %02X / LH: %04X\n", lich, lich_bits_hex);
#endif
//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, 1, sizeof(state->nxdn_sacch_frame_segment));
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
voice = 0;
break;
} // end of switch(lich)
// //collect remaining dibits at this point (before bad frame skip)
// for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
// dbuf[i+8] = getDibit(opts, state);
//go to end if bad returns from earlier
if (state->lastsynctype == -1)
goto END;
//collect remaining dibits at this point (after bad frame skip)
for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
dbuf[i+8] = getDibit(opts, state);
//enable these after good lich parity and known lich value
state->carrier = 1;
state->last_cc_sync_time = time(NULL);
@@ -340,13 +350,6 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
#endif
}
//now that we have a good LICH, we can collect all of our dibits
//and push them to the proper places for decoding (if LICH calls for that type)
for (int i = 0; i < 174; i++) //192total-10FSW-8lich = 174
{
dbuf[i+8] = getDibit(opts, state);
}
nxdn_pn95_dibit_scrambler (state, dbuf, 182);
//seperate our dbuf (dibit_buffer) into individual bit array
@@ -358,9 +361,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//sacch or scch bits
for (int i = 0; i < 60; i++)
{
sacch_bits[i] = nxdn_bit_buffer[i+16];
}
//facch
for (int i = 0; i < 144; i++)
@@ -371,21 +372,15 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//cac
for (int i = 0; i < 300; i++)
{
cac_bits[i] = nxdn_bit_buffer[i+16];
}
//udch or facch2
for (int i = 0; i < 348; i++)
{
facch2_bits[i] = nxdn_bit_buffer[i+16];
}
//udch2 or facch3
for (int i = 0; i < 288; i++)
{
facch3_bits[i] = nxdn_bit_buffer[i+16+60];
}
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
//sacch needs extra handling depending on superframe or non-superframe variety
@@ -551,32 +546,30 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
else state->nxdn_sacch_non_superframe = FALSE;
//TODO Later: Add Direction and/or LICH to all decoding functions
if (scch) nxdn_deperm_scch (opts, state, sacch_bits, direction);
if (udch2) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 0);
if (facch3) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 1);
//Type-D "IDAS"
if (scch) idas_scch(opts, state, sacch_bits, direction);
if (udch2) idas_facch3_udch2(opts, state, facch3_bits, 0);
if (facch3) idas_facch3_udch2(opts, state, facch3_bits, 1);
if (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
//NXDN Conventional and Type-C
if (sacch) nxdn_sacch(opts, state, sacch_bits);
if (cac) nxdn_cac(opts, state, cac_bits);
//TODO: Redo duplicate facch check inside decoder? storage for facch message?
if (facch & 1) nxdn_facch1(opts, state, facch_bits_a);
if (facch & 2) nxdn_facch1(opts, state, facch_bits_b);
//Seperated UDCH user data from facch2 data
if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0);
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1);
if (udch) nxdn_facch2_udch(opts, state, facch2_bits, 0);
if (facch2) nxdn_facch2_udch(opts, state, facch2_bits, 1);
//DCR
if (sacch2) nxdn_deperm_sacch2(opts, state, sacch_bits);
if (pich_tch & 1) nxdn_deperm_pich_tch(opts, state, facch_bits_a, lich);
if (pich_tch & 2) nxdn_deperm_pich_tch(opts, state, facch_bits_b, lich);
//only run facch in second slot if its not equal to the first one
//ideally, this would work better AFTER decoding/FEC
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
if (facch & 2)
{
if (memcmp (facch_bits_a, facch_bits_b, 144) != 0)
nxdn_deperm_facch(opts, state, facch_bits_b);
}
//Japanese DCR
if (sacch2) dcr_sacch(opts, state, sacch_bits);
if (pich_tch & 1) dcr_pich_tch(opts, state, facch_bits_a, lich);
if (pich_tch & 2) dcr_pich_tch(opts, state, facch_bits_b, lich);
//EHR only (TODO: Make an EFR mode? never observed)
if (voice)
{
//restore MBE file open here
@@ -632,7 +625,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
//if rejected sync, reset carrier and synctype as well
if (state->lastsynctype == -1)
{
state->carrier = 0;
state->carrier = 0; //this is why the "Sync: no sync" doesn't show in terminal if we have a bad sync (not an issue, just not consistent)
state->synctype = -1;
}
}

View File

@@ -53,7 +53,7 @@ const int nZ[36] = { 5, 3, 4, 2, 3, 1,
13, 2, 12, 1, 11, 0
};
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182])
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t * dbuf)
{
int i;
int start, stop;

View File

@@ -1629,9 +1629,53 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
}
//MFID90 Group Regroup Add Command
if (MAC[1+len_a] == 0x81 && MAC[2+len_a] == 0x90) //needs MAC message len update, may work same as explicit enc regroup?
if (MAC[1+len_a] == 0x81 && MAC[2+len_a] == 0x90)
{
fprintf (stderr, "\n MFID90 Group Regroup Add Command ");
uint8_t len = MAC[3+len_a];
len_b = len;
}
//MFID90 Unknown Message <--was causing an issue on ABQ system w/ false GPS, SCCB and RFSS_STS_BCST MAC messages
if (MAC[1+len_a] == 0x8F && MAC[2+len_a] == 0x90)
{
uint8_t len = MAC[3+len_a]; //observed len values are 0x04, 0x08, and 0x0B (12)
//constraints on message len
if (len > 12)
len = 12;
// else if (len < 4)
// len = 4;
fprintf (stderr, "%s",KCYN);
fprintf (stderr, "\n MFID 90 (Moto); Opcode: %02X; ", MAC[1+len_a] & 0x3F); //mask, or not?
for (int i = 4; i < len+1; i++)
fprintf (stderr, "%02X", MAC[i+len_a]);
fprintf (stderr, " %s",KNRM);
len_b = len;
}
//MFID90 Unknown Message 2 (usually in second message, unknown, maybe just filler?)
if (MAC[1+len_a] == 0xBF && MAC[2+len_a] == 0x90)
{
uint8_t len = MAC[3+len_a]; //observed len values are 0x03
//constraints on message len
if (len > 3)
len = 3;
else if (len < 3)
len = 3;
fprintf (stderr, "%s",KCYN);
fprintf (stderr, "\n MFID 90 (Moto); Opcode: %02X; ", MAC[1+len_a] & 0x3F); //mask, or not?
for (int i = 4; i < len+1; i++)
fprintf (stderr, "%02X", MAC[i+len_a]);
fprintf (stderr, " %s",KNRM);
len_b = len;
}
//the len on these indicate they are always a single messages, foregoing the +len_a index pointer
@@ -1775,6 +1819,12 @@ void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned lon
int unk2 = MAC[len_a+2]; //This field is observed as 0xAA, unknown if this is an opcode, or other MFID
int mfid = MAC[len_a+3]; //This is where the 0xA4 (Harris) Identifier is found in this message, as opposed to +2
int len = MAC[len_a+4] & 0x3F;; //0x11 or 17 dec sounds reasonable, but cannot verify
//bugfix observed on random errant second MAC message on Phase 2
//although can't 100% confirm, pretty sure these all have same len value
if (len != 0x11)
goto END_PDU;
fprintf (stderr, "\n MFID %02X (Harris); Len: %d; Opcode: %02X/%02X;", mfid, len, unk1, unk2);
//convert bytes to bits, may move this up top

File diff suppressed because it is too large Load Diff

205
src/ysf.c
View File

@@ -228,7 +228,12 @@ void ysf_dch_decode (dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint
}
if (fn == ft) //last frame
fprintf (stderr, " %s", state->event_history_s[0].Event_History_Items[0].text_message);
{
char text_string[15]; memset (text_string, 0, sizeof(text_string));
sprintf (text_string, "%s", "BUMBLEBEETUNA");
if (strncmp(text_string, state->event_history_s[0].Event_History_Items[0].event_string, 13) != 0)
fprintf (stderr, " %s", state->event_history_s[0].Event_History_Items[0].text_message);
}
break;
@@ -377,15 +382,14 @@ static inline uint16_t crc16ysf(const uint8_t buf[], int len)
int ysf_conv_dch2 (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint8_t ft, uint8_t cm, uint8_t input[])
{
int i, j, k, err;
uint8_t s0, s1;
uint8_t trellis_buf[100];
int i, j, err;
uint8_t viterbi_bits[100];
uint8_t temp[210];
uint8_t m_data[100];
uint8_t viterbi_bytes[100];
uint8_t bits[210];
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (viterbi_bits, 0, sizeof(viterbi_bits));
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
memset (bits, 0, sizeof(bits));
err = 0;
@@ -398,56 +402,27 @@ int ysf_conv_dch2 (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, u
}
}
k = 0;
//convert dibits to bits
for (i = 0; i < 100; i++)
{
bits[k++] = (buf[i] >> 1) & 1;
bits[k++] = (buf[i] >> 0) & 1;
}
uint32_t v_error =
ysf_soft_decision_viterbi(buf, 200, 13, 8, viterbi_bits, viterbi_bytes);
//setup for the convolutional decoder
for (i = 0; i < 200; i++)
temp[i] = bits[i] << 1;
//debug
if (opts->payload == 1)
fprintf (stderr, " D2-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
CNXDNConvolution_start();
for (i = 0; i < 100; i++)
{
s0 = temp[(2*i)+0];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 96);
//96/8 = 12, last 4 (96-100) are trailing zeroes
for(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;
}
uint16_t crc = crc16ysf(trellis_buf, 96);
uint16_t crc = crc16ysf(viterbi_bits, 96);
if (crc != 0) err = -2; // crc failure
for (i = 0; i < 80; i++)
trellis_buf[i] = trellis_buf[i] ^ pn95[i];
viterbi_bits[i] = viterbi_bits[i] ^ pn95[i];
//reload after de-whitening
memset (m_data, 0, sizeof (m_data));
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
for (i = 0; i < 12; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
viterbi_bytes[i] = (uint8_t)ConvertBitIntoBytes(&viterbi_bits[i*8], 8);
//decode the callsign, etc, found in the DCH when no errors
if (err == 0)
ysf_dch_decode2 (state, bn, bt, fn, ft, cm, trellis_buf);
ysf_dch_decode2 (state, bn, bt, fn, ft, cm, viterbi_bits);
else
{
fprintf (stderr, "%s", KRED);
@@ -460,25 +435,24 @@ int ysf_conv_dch2 (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, u
fprintf (stderr, "\n ");
fprintf (stderr, "DCH2: ");
for (i = 0; i < 12; i++)
fprintf (stderr, "[%02X]", m_data[i]);
fprintf (stderr, "[%02X]", viterbi_bytes[i]);
}
return err;
}
//modified version of nxdn_deperm_facch1 -- this one for Full Rate, Type 1 CC, Headers and Terminators DCH (180 dibit version)
//Soft Decision Full Rate, Type 1 CC, Headers and Terminators DCH (180 dibit version)
int ysf_conv_dch (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint8_t ft, uint8_t cm, uint8_t input[])
{
int i, j, k, err;
uint8_t s0, s1;
uint8_t trellis_buf[190];
int i, j, err;
uint8_t viterbi_bits[190];
uint8_t temp[370];
uint8_t m_data[100];
uint8_t viterbi_bytes[100];
uint8_t bits[370];
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (viterbi_bits, 0, sizeof(viterbi_bits));
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
memset (bits, 0, sizeof(bits));
err = 0;
@@ -491,56 +465,27 @@ int ysf_conv_dch (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, ui
}
}
k = 0;
//convert dibits to bits
for (i = 0; i < 180; i++)
{
bits[k++] = (buf[i] >> 1) & 1;
bits[k++] = (buf[i] >> 0) & 1;
}
uint32_t v_error =
ysf_soft_decision_viterbi(buf, 360, 23, 8, viterbi_bits, viterbi_bytes);
//setup for the convolutional decoder
for (i = 0; i < 360; i++)
temp[i] = bits[i] << 1;
//debug
if (opts->payload == 1)
fprintf (stderr, " D1-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
CNXDNConvolution_start();
for (i = 0; i < 180; i++)
{
s0 = temp[(2*i)+0];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 176);
//176/8 = 22, last 4 (176-180) are trailing zeroes
for(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;
}
uint16_t crc = crc16ysf(trellis_buf, 176);
uint16_t crc = crc16ysf(viterbi_bits, 176);
if (crc != 0) err = -2; // crc failure
for (i = 0; i < 160; i++)
trellis_buf[i] = trellis_buf[i] ^ pn95[i];
viterbi_bits[i] = viterbi_bits[i] ^ pn95[i];
//reload after de-whitening
memset (m_data, 0, sizeof (m_data));
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
for (i = 0; i < 22; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
viterbi_bytes[i] = (uint8_t)ConvertBitIntoBytes(&viterbi_bits[i*8], 8);
//decode the callsign, etc, found in the DCH when no errors
if (err == 0)
ysf_dch_decode (state, bn, bt, fn, ft, cm, trellis_buf);
ysf_dch_decode (state, bn, bt, fn, ft, cm, viterbi_bits);
else
{
fprintf (stderr, "%s", KRED);
@@ -553,25 +498,24 @@ int ysf_conv_dch (dsd_opts * opts, dsd_state * state, uint8_t bn, uint8_t bt, ui
fprintf (stderr, "\n ");
fprintf (stderr, "DCH1: ");
for (i = 0; i < 22; i++)
fprintf (stderr, "[%02X]", m_data[i]);
fprintf (stderr, "[%02X]", viterbi_bytes[i]);
}
return err;
}
//modified version of nxdn_deperm_facch1
int ysf_conv_fich (uint8_t input[], uint8_t dest[32])
//FICH Soft Decision Viterbi
int ysf_conv_fich (dsd_opts * opts, uint8_t input[], uint8_t dest[32])
{
int i, j, k, err;
uint8_t s0, s1;
uint8_t trellis_buf[100];
int i, j, err;
uint8_t viterbi_bits[100];
uint8_t temp[210];
uint8_t m_data[100];
uint8_t viterbi_bytes[100];
uint8_t bits[210];
memset (trellis_buf, 0, sizeof(trellis_buf));
memset (viterbi_bits, 0, sizeof(viterbi_bits));
memset (temp, 0, sizeof (temp));
memset (m_data, 0, sizeof (m_data));
memset (viterbi_bytes, 0, sizeof (viterbi_bytes));
memset (bits, 0, sizeof(bits));
err = 0;
@@ -584,41 +528,12 @@ int ysf_conv_fich (uint8_t input[], uint8_t dest[32])
}
}
k = 0;
//convert dibits to bits
for (i = 0; i < 100; i++)
{
bits[k++] = (buf[i] >> 1) & 1;
bits[k++] = (buf[i] >> 0) & 1;
}
uint32_t v_error =
ysf_soft_decision_viterbi(buf, 200, 13, 8, viterbi_bits, viterbi_bytes);
//setup for the convolutional decoder
for (i = 0; i < 200; i++) //192
temp[i] = bits[i] << 1;
CNXDNConvolution_start();
for (i = 0; i < 100; i++)
{
s0 = temp[(2*i)+0];
s1 = temp[(2*i)+1];
CNXDNConvolution_decode(s0, s1);
}
CNXDNConvolution_chainback(m_data, 96);
//96/8 = 12, last 4 (96-100) are trailing zeroes
for(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;
}
//debug
if (opts->payload == 1)
fprintf (stderr, " F-Ve: %1.1f; ", (float)v_error/(float)0xFFFF);
uint8_t fich_bits[12*4];
uint8_t temp_b[24];
@@ -631,31 +546,27 @@ int ysf_conv_fich (uint8_t input[], uint8_t dest[32])
g[i] = FALSE;
for (j = 0; j < 24; j++)
temp_b[j] = (char) trellis_buf[(i*24)+j];
temp_b[j] = (char) viterbi_bits[(i*24)+j];
g[i] = Golay_24_12_decode(temp_b);
if(g[i] == FALSE) err = -1;
for (j = 0; j < 24; j++)
trellis_buf[(i*24)+j] = (uint8_t) temp_b[j];
viterbi_bits[(i*24)+j] = (uint8_t) temp_b[j];
}
//load corrected bits
for (i = 0; i < 12; i++)
{
fich_bits[(12*0)+i] = trellis_buf[i+0];
fich_bits[(12*1)+i] = trellis_buf[i+24];
fich_bits[(12*2)+i] = trellis_buf[i+48];
fich_bits[(12*3)+i] = trellis_buf[i+72];
fich_bits[(12*0)+i] = viterbi_bits[i+0];
fich_bits[(12*1)+i] = viterbi_bits[i+24];
fich_bits[(12*2)+i] = viterbi_bits[i+48];
fich_bits[(12*3)+i] = viterbi_bits[i+72];
}
uint16_t crc = crc16ysf(fich_bits, 48);
if (crc != 0) err = -2; // crc failure
memset (m_data, 0, sizeof (m_data));
for (i = 0; i < 12; i++)
m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8);
memcpy(dest, fich_bits, 32); //copy minus the crc16
return err;
}
@@ -808,7 +719,7 @@ void processYSF(dsd_opts * opts, dsd_state * state)
fichrawdibits[i] = getDibit(opts, state);
//from nxdn_deperm_facch1 w/ nxdn convolutional decoder
err = ysf_conv_fich (fichrawdibits, fich_decode);
err = ysf_conv_fich (opts, fichrawdibits, fich_decode);
//if errors decoding fich, then just treat it like the last frame that came in
if (err == 0)