From 1d05f3d8518846c820ed2d72a360babe3c4c8296 Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Mon, 10 Jul 2023 19:13:16 -0400 Subject: [PATCH] M17 More Work and Fixes; #152 --- include/dsd.h | 21 +- src/dsd_audio.c | 41 ++++ src/dsd_dibit.c | 4 +- src/dsd_frame.c | 11 +- src/dsd_frame_sync.c | 58 +++++- src/dsd_main.c | 23 +++ src/dsd_ncurses.c | 113 ++++++++++- src/m17.c | 452 ++++++++++++++++++++++++++++++++++++------- 8 files changed, 643 insertions(+), 80 deletions(-) diff --git a/include/dsd.h b/include/dsd.h index e64d457..fa6bf35 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -736,6 +736,19 @@ typedef struct uint8_t m17_lsf[360]; uint8_t m17_str_dt; //stream contents + unsigned long long int m17_dst; + unsigned long long int m17_src; + + char m17_dst_csd[20]; + char m17_src_csd[20]; + + char m17_src_str[50]; + char m17_dst_str[50]; + + uint8_t m17_meta[14]; //packed meta + uint8_t m17_enc; //enc type + uint8_t m17_enc_st; //scrambler or data subtye + //Codec2 #ifdef USE_CODEC2 struct CODEC2 *codec2_3200; //M17 fullrate @@ -751,6 +764,11 @@ typedef struct //The inverse of LSF also seems to be Stream Frame Type, only going to work on Stream mode for now #define M17_LSF "11113313" #define M17_STR "33331131" +//alternating with last symbol opposite of first symbol of LSF +#define M17_PRE "31313131" +#define M17_PIV "13131313" +#define M17_PRE_LSF "3131313133331131" //Preamble + LSF +#define M17_PIV_LSF "1313131311113313" //Preamble + LSF // #define M17_BRT "31331111" // #define M17_PKT "13113333" @@ -920,7 +938,8 @@ void processX2TDMAdata (dsd_opts * opts, dsd_state * state); void processX2TDMAvoice (dsd_opts * opts, dsd_state * state); void processDSTAR_HD (dsd_opts * opts, dsd_state * state); void processYSF(dsd_opts * opts, dsd_state * state); //YSF -void processM17(dsd_opts * opts, dsd_state * state); //M17 +void processM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR) +void processM17LSF(dsd_opts * opts, dsd_state * state); //M17 (LSF) void processP2(dsd_opts * opts, dsd_state * state); //P2 void processTSBK(dsd_opts * opts, dsd_state * state); //P25 Trunking Single Block void processMPDU(dsd_opts * opts, dsd_state * state); //P25 Multi Block PDU (SAP 0x61 FMT 0x15 or 0x17 for Trunking Blocks) diff --git a/src/dsd_audio.c b/src/dsd_audio.c index 6d91a2f..c4e98f5 100644 --- a/src/dsd_audio.c +++ b/src/dsd_audio.c @@ -640,6 +640,47 @@ openAudioInDevice (dsd_opts * opts) } } + //test .rrc files with hardset wav file settings + else if (strncmp(extension, ".rrc", 3) == 0) + { + //debug + fprintf (stderr, "Opening M17 .rrc headless wav file\n"); + + opts->audio_in_type = 2; + opts->audio_in_file_info = calloc(1, sizeof(SF_INFO)); + opts->audio_in_file_info->samplerate = 48000; + opts->audio_in_file_info->channels = 1; + opts->audio_in_file_info->seekable = 0; + opts->audio_in_file_info->format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE; + opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info); + + if(opts->audio_in_file == NULL) + { + fprintf(stderr, "Error, couldn't open %s with libsndfile: %s\n", opts->audio_in_dev, sf_strerror(NULL)); + exit(1); + } + } + + //TODO: test .sym files as symbol capture .bin files + else if (strncmp(extension, ".sym", 3) == 0) + { + struct stat stat_buf; + if (stat(opts->audio_in_dev, &stat_buf) != 0) + { + fprintf (stderr,"Error, couldn't open bin file %s\n", opts->audio_in_dev); + exit(1); + } + if (S_ISREG(stat_buf.st_mode)) + { + opts->symbolfile = fopen(opts->audio_in_dev, "r"); + opts->audio_in_type = 4; //symbol capture bin files + } + else + { + opts->audio_in_type = 0; + } + } + else if (strncmp(extension, ".bin", 3) == 0) { struct stat stat_buf; diff --git a/src/dsd_dibit.c b/src/dsd_dibit.c index c4e92dd..ae71f2e 100644 --- a/src/dsd_dibit.c +++ b/src/dsd_dibit.c @@ -268,7 +268,7 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol) else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) || (state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) || (state->synctype == 17) || (state->synctype == 29) || (state->synctype == 31) || - (state->synctype == 36) ) + (state->synctype == 36) || (state->synctype == 99) ) { // 1 -P25p1 @@ -281,6 +281,7 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol) // 29 -NXDN (inverted FSW) // 31 -YSF // 36 -P25p2 + // 99 -M17 Preamble int valid; int dibit; @@ -346,6 +347,7 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol) // 28 +NXND (FSW) // 30 +YSF // 35 +p25p2 + // 98 +M17 Preamble int valid; int dibit; diff --git a/src/dsd_frame.c b/src/dsd_frame.c index 87ff2f1..9aafc2c 100644 --- a/src/dsd_frame.c +++ b/src/dsd_frame.c @@ -297,11 +297,14 @@ processFrame (dsd_opts * opts, dsd_state * state) return; } //M17 - else if ((state->synctype == 16) || (state->synctype == 9) || (state->synctype == 17) || (state->synctype == 8) ) + else if ((state->synctype == 16) || (state->synctype == 9) || (state->synctype == 17) || (state->synctype == 8) || (state->synctype == 99) || (state->synctype == 98)) { - if (state->synctype == 16 || state->synctype == 17) - processM17(opts, state); - else skipDibit(opts, state, 184); //8 and 9 will need seperate handling for LSF + if (state->synctype == 98 || state->synctype == 99) //preamble only + skipDibit(opts, state, 184); //skip dibits to prime the demodulator + else if (state->synctype == 16 || state->synctype == 17) + processM17STR(opts, state); + else + processM17LSF(opts, state); return; } //P25 P2 diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c index 1356eb1..1d76752 100644 --- a/src/dsd_frame_sync.c +++ b/src/dsd_frame_sync.c @@ -216,7 +216,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state) char synctest20[21]; //YSF char synctest21[22]; //P25 S-OEMI (SACCH) char synctest48[49]; //EDACS - char synctest8[9]; //M17 + char synctest8[9]; //M17 + char synctest16[17]; //M17 Preamble char modulation[8]; char *synctest_p; char synctest_buf[10240]; //what actually is assigned to this, can't find its use anywhere? @@ -604,11 +605,42 @@ getFrameSync (dsd_opts * opts, dsd_state * state) } //end YSF sync - //M17 Sync -- Just Voice and LSF for now + //M17 Sync -- Just STR and LSF for now + strncpy(synctest16, (synctest_p - 15), 16); strncpy(synctest8, (synctest_p - 7), 8); if(opts->frame_m17 == 1) { - if (strcmp(synctest8, M17_STR) == 0) + //preambles will skip dibits in an attempt to prime the + //demodulator but not attempt any decoding + if (strcmp(synctest8, M17_PRE) == 0) + { + if (opts->inverted_m17 == 0) + { + printFrameSync (opts, state, "+M17 PREAMBLE", synctest_pos + 1, modulation); + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + state->lastsynctype = 98; + fprintf (stderr, "\n"); + return (98); + } + } + else if (strcmp(synctest8, M17_PIV) == 0) + { + if (opts->inverted_m17 == 1) + { + printFrameSync (opts, state, "-M17 PREAMBLE", synctest_pos + 1, modulation); + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + state->lastsynctype = 99; + fprintf (stderr, "\n"); + return (99); + } + } + else if (strcmp(synctest8, M17_STR) == 0) { if (opts->inverted_m17 == 0) { @@ -617,8 +649,11 @@ 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 == 16) + if (state->lastsynctype == 16 || state->lastsynctype == 8) + { + state->lastsynctype = 16; return (16); + } state->lastsynctype = 16; fprintf (stderr, "\n"); } @@ -629,8 +664,11 @@ 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 == 9) + if (state->lastsynctype == 99) + { + state->lastsynctype = 9; return (9); + } state->lastsynctype = 9; fprintf (stderr, "\n"); } @@ -644,8 +682,11 @@ 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 == 17) + if (state->lastsynctype == 17 || state->lastsynctype == 9) + { + state->lastsynctype = 17; return (17); + } state->lastsynctype = 17; fprintf (stderr, "\n"); } @@ -656,8 +697,11 @@ 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 == 8) + if (state->lastsynctype == 98) + { + state->lastsynctype = 8; return (8); + } state->lastsynctype = 8; fprintf (stderr, "\n"); } diff --git a/src/dsd_main.c b/src/dsd_main.c index b1e6a9d..fa8fd5d 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -474,6 +474,17 @@ noCarrier (dsd_opts * opts, dsd_state * state) memset (state->m17_lsf, 1, sizeof(state->m17_lsf)); state->m17_str_dt = 9; + state->m17_dst = 0; + state->m17_src = 0; + memset(state->m17_dst_csd, 0, sizeof(state->m17_dst_csd)); + memset(state->m17_src_csd, 0, sizeof(state->m17_src_csd)); + sprintf (state->m17_dst_str, "%s", ""); + sprintf (state->m17_src_str, "%s", ""); + + state->m17_enc = 0; + state->m17_enc_st = 0; + memset(state->m17_meta, 0, sizeof(state->m17_meta)); + } //nocarrier void @@ -1058,6 +1069,18 @@ initState (dsd_state * state) //M17 Storage memset (state->m17_lsf, 1, sizeof(state->m17_lsf)); state->m17_str_dt = 9; + + state->m17_dst = 0; + state->m17_src = 0; + memset(state->m17_dst_csd, 0, sizeof(state->m17_dst_csd)); + memset(state->m17_src_csd, 0, sizeof(state->m17_src_csd)); + sprintf (state->m17_dst_str, "%s", ""); + sprintf (state->m17_src_str, "%s", ""); + + state->m17_enc = 0; + state->m17_enc_st = 0; + memset(state->m17_meta, 0, sizeof(state->m17_meta)); + #ifdef USE_CODEC2 state->codec2_3200 = codec2_create(CODEC2_MODE_3200); diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c index 6cb3fc3..66177cb 100644 --- a/src/dsd_ncurses.c +++ b/src/dsd_ncurses.c @@ -302,7 +302,7 @@ char * getTimeC(time_t t) //get pretty hh:mm:ss timestamp //testing a few things, going to put this into ncursesMenu #define WIDTH 36 -#define HEIGHT 23 +#define HEIGHT 25 int startx = 0; int starty = 0; @@ -1250,6 +1250,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 1; + opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; @@ -1280,6 +1281,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 0; opts->frame_provoice = 1; opts->frame_ysf = 0; + opts->frame_m17 = 0; opts->mod_c4fm = 0; opts->mod_qpsk = 0; opts->mod_gfsk = 1; @@ -1308,6 +1310,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; + opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; @@ -1346,6 +1349,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; + opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; @@ -1383,6 +1387,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 1; + opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; @@ -1410,6 +1415,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; + opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; @@ -1437,6 +1443,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 1; opts->frame_provoice = 0; opts->frame_ysf = 0; + opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; @@ -1467,6 +1474,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; + opts->frame_m17 = 0; opts->mod_c4fm = 1; //opts->unmute_encrypted_p25 = 0; // opts->mod_qpsk = 0; @@ -1494,6 +1502,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; + opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; @@ -1522,6 +1531,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; + opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; @@ -1549,6 +1559,34 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 1; + opts->frame_m17 = 0; + opts->mod_c4fm = 1; + opts->mod_qpsk = 0; + opts->mod_gfsk = 0; + state->rf_mod = 0; + } + if (choice == 99) //TODO: Slot this into the menu choices, and maybe rearrange them into the correct order as well + { + //Decode M17 + // resetState (state); //use sparingly, may cause memory leak + state->samplesPerSymbol = 10; + state->symbolCenter = 4; + sprintf (opts->output_name, "M17"); + opts->dmr_mono = 0; + opts->dmr_stereo = 0; //this value is the end user option + state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling + opts->pulse_digi_rate_out = 8000; + opts->pulse_digi_out_channels = 1; + opts->frame_dstar = 0; + opts->frame_x2tdma = 0; + opts->frame_p25p1 = 0; + opts->frame_nxdn48 = 0; + opts->frame_nxdn96 = 0; + opts->frame_dmr = 0; + opts->frame_dpmr = 0; + opts->frame_provoice = 0; + opts->frame_ysf = 0; + opts->frame_m17 = 1; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; @@ -1563,6 +1601,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->inverted_dpmr = 1; opts->inverted_x2tdma = 1; opts->inverted_ysf = 1; + opts->inverted_m17 = 1; } else { @@ -1570,6 +1609,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->inverted_dpmr = 0; opts->inverted_x2tdma = 0; opts->inverted_ysf = 0; + opts->inverted_m17 = 0; } } @@ -2424,6 +2464,75 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } } + //M17 + if (lls == 8 || lls == 9 || lls == 16 || lls == 17) + { + + printw ("| "); + printw ("M17 - "); + + //insert data type and frame information + if (state->m17_str_dt == 0) printw("Reserved"); + if (state->m17_str_dt == 1) printw("Data "); + if (state->m17_str_dt == 2) printw("Voice (3200) "); + if (state->m17_str_dt == 3) printw("Voice (1600) + Data"); + printw (" "); + + printw ("\n"); + printw ("| "); + + printw ("DST: "); + if (state->m17_dst == 0xFFFFFFFFFFFF) + printw("BROADCAST "); + else if (state->m17_dst != 0 && state->m17_dst >= 0xEE6B28000000) + printw("RESERVED (%012llx) ", state->m17_dst); + else + { + // for (i = 0; i < 9; i++) + // printw ("%c", state->m17_dst_csd[i]); + printw("%s", state->m17_dst_str); + } + + printw ("\n"); + printw ("| "); + + printw ("SRC: "); + if (state->m17_src != 0 && state->m17_src >= 0xEE6B28000000) + printw("RESERVED (%012llx)", state->m17_src); + else + { + // for (i = 0; i < 9; i++) + // printw ("%c", state->m17_src_csd[i]); + printw("%s", state->m17_src_str); + } + + printw ("\n"); + printw ("| "); + + + //fill in any extra info, like Meta (IV, etc) + if (state->m17_enc == 2) + { + printw (" Scrambler - Type: %d", state->m17_enc_st); + } + + if (state->m17_enc == 1) + { + printw (" AES-CTR - IV: "); + //display packed meta as IV + for (i = 0; i < 14; i++) + printw ("%02X", state->m17_meta); + } + + if (state->m17_enc == 3) + { + printw (" Reserved Enc - Type: %d", state->m17_enc_st); + } + + printw ("\n"); + + } + //YSF if (lls == 30 || lls == 31) { @@ -3438,6 +3547,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) opts->inverted_dpmr = 1; opts->inverted_x2tdma = 1; opts->inverted_ysf = 1; + opts->inverted_m17 = 1; } else { @@ -3445,6 +3555,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) opts->inverted_dpmr = 0; opts->inverted_x2tdma = 0; opts->inverted_ysf = 0; + opts->inverted_m17 = 0; } } diff --git a/src/m17.c b/src/m17.c index 89f451c..f9f939b 100644 --- a/src/m17.c +++ b/src/m17.c @@ -2,8 +2,8 @@ * m17.c * M17 Decoder (WIP) * - * M17_SCRAMBLER (m17_scramble) Bit Array from SDR++ - * Base40 CharMap from m17-tools + * m17_scramble Bit Array from SDR++ + * CRC16 from M17-Implementations (thanks again, sp5wwp) * * LWVMOBILE * 2023-07 DSD-FME Florida Man Edition @@ -37,32 +37,97 @@ uint8_t m17_scramble[369] = { 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1 }; -char b40[41] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/."; +char b40[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/."; -/* -2.5.4 -LSF CRC -M17 uses a non-standard version of 16-bit CRC with polynomial x16 + x14 + x12 + x11 + x8 + -x5 + x4 + x2 + 1 or 0×5935 and initial value of 0×FFFF. This polynomial allows for detecting all -errors up to hamming distance of 5 with payloads up to 241 bits, which is less than the amount -of data in each frame. -*/ +uint8_t p1[62] = { +1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, +1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, +0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1 +}; -uint16_t crc16m17(const uint8_t buf[], int len) +//from M17_Implementations -- sp5wwp -- should have just looked here to begin with +//this setup looks very similar to the OP25 variant of crc16, but with a few differences (uses packed bytes) +uint16_t crc16m17(const uint8_t *in, const uint16_t len) { + uint32_t crc = 0xFFFF; //init val uint16_t poly = 0x5935; - uint16_t crc = 0xFFFF; - for(int i=0; i= 0xEE6B28000000) + fprintf (stderr, " DST: RESERVED %012llx", dst); + else + { + fprintf (stderr, " DST: "); + for (i = 0; i < 9; i++) + { + c = b40[dst % 40]; + state->m17_dst_csd[i] = c; + fprintf (stderr, "%c", c); + dst = dst / 40; + } + //assign completed CSD to a more useful string instead + sprintf (state->m17_dst_str, "%c%c%c%c%c%c%c%c%c\0", + state->m17_dst_csd[0], state->m17_dst_csd[1], state->m17_dst_csd[2], state->m17_dst_csd[3], + state->m17_dst_csd[4], state->m17_dst_csd[5], state->m17_dst_csd[6], state->m17_dst_csd[7], state->m17_dst_csd[8]); + + //debug + // fprintf (stderr, "DT: %s", state->m17_dst_str); + } + + if (src == 0xFFFFFFFFFFFF) + fprintf (stderr, " SRC: UNKNOWN FFFFFFFFFFFF"); + else if (src == 0) + fprintf (stderr, " SRC: RESERVED %012llx", src); + else if (src >= 0xEE6B28000000) + fprintf (stderr, " SRC: RESERVED %012llx", src); + else + { + fprintf (stderr, " SRC: "); + for (i = 0; i < 9; i++) + { + c = b40[src % 40]; + state->m17_src_csd[i] = c; + fprintf (stderr, "%c", c); + src = src / 40; + } + //assign completed CSD to a more useful string instead + sprintf (state->m17_src_str, "%c%c%c%c%c%c%c%c%c\0", + state->m17_src_csd[0], state->m17_src_csd[1], state->m17_src_csd[2], state->m17_src_csd[3], + state->m17_src_csd[4], state->m17_src_csd[5], state->m17_src_csd[6], state->m17_src_csd[7], state->m17_src_csd[8]); + + //debug + // fprintf (stderr, "ST: %s", state->m17_src_str); + } + + //debug + // fprintf (stderr, " DST: %012llX SRC: %012llX", state->m17_dst, state->m17_src); + +} + +void M17decodeLSF(dsd_state * state) { int i; @@ -78,30 +143,56 @@ void M17decodeLSF(dsd_state * state, dsd_opts * opts) uint8_t lsf_cn = (lsf_type >> 7) & 0xF; uint8_t lsf_rs = (lsf_type >> 11) & 0x1F; - //store this so we can reference it for playing voice and/or decoding data + //store this so we can reference it for playing voice and/or decoding data, dst/src etc state->m17_str_dt = lsf_dt; + state->m17_dst = lsf_dst; + state->m17_src = lsf_src; fprintf (stderr, "\n"); - //debug - // fprintf (stderr, " LSF TYPE: %04X", lsf_type); - // if (lsf_ps == 0) fprintf (stderr, " PACKET - "); - // if (lsf_ps == 1) fprintf (stderr, " STREAM - "); + //packet or stream + if (lsf_ps == 0) fprintf (stderr, " P-"); + if (lsf_ps == 1) fprintf (stderr, " S-"); - //TODO: Base40 Callsigns fprintf (stderr, " CAN: %d", lsf_cn); - fprintf (stderr, " DST: %012llX SRC: %012llX", lsf_dst, lsf_src); + M17decodeCSD(state, lsf_dst, lsf_src); + + if (lsf_et != 0) fprintf (stderr, " ENC"); if (lsf_dt == 0) fprintf (stderr, " Reserved"); if (lsf_dt == 1) fprintf (stderr, " Data"); if (lsf_dt == 2) fprintf (stderr, " Voice (3200bps)"); if (lsf_dt == 3) fprintf (stderr, " Voice + Data"); - if (lsf_et != 0) fprintf (stderr, " ENC"); - if (lsf_et == 1) - fprintf (stderr, " Scrambler - %d", lsf_es); - if (lsf_et == 2) fprintf (stderr, " AES-CTR"); + if (lsf_rs != 0) fprintf (stderr, " RS: %02X", lsf_rs); + if (lsf_et == 1) fprintf (stderr, " AES-CTR"); + if (lsf_et == 2) fprintf (stderr, " Scrambler - %d", lsf_es); + + state->m17_enc = lsf_et; + state->m17_enc_st = lsf_es; + + //pack meta bits into 14 bytes + for (i = 0; i < 14; i++) + state->m17_meta[i] = (uint8_t)ConvertBitIntoBytes(&state->m17_lsf[(i*8)+112], 8); + + //Examine the Meta data + if (lsf_et == 0 && state->m17_meta[0] != 0) //not sure if this applies universally, or just to text data byte 0 for null data + { + fprintf (stderr, " Meta:"); + if (lsf_es == 0) fprintf (stderr, " Text Data"); + if (lsf_es == 1) fprintf (stderr, " GNSS Pos"); + if (lsf_es == 2) fprintf (stderr, " Extended CSD"); + if (lsf_es == 3) fprintf (stderr, " Reserved"); + } + + if (lsf_et == 1) + { + fprintf (stderr, " IV: "); + for (i = 0; i < 14; i++) + fprintf (stderr, "%02X", state->m17_meta[i]); + } + } int M17processLICH(dsd_state * state, dsd_opts * opts, uint8_t lich_bits[96]) @@ -114,6 +205,10 @@ int M17processLICH(dsd_state * state, dsd_opts * opts, uint8_t lich_bits[96]) uint8_t temp[96]; bool g[4]; + //for testing bit arrangement vs the crc code + uint8_t bs[240]; + memset(bs, 1, sizeof(bs)); + uint16_t crc_cmp = 0; uint16_t crc_ext = 0; uint8_t crc_err = 0; @@ -145,8 +240,7 @@ int M17processLICH(dsd_state * state, dsd_opts * opts, uint8_t lich_bits[96]) fprintf (stderr, "LC: %d/6 ", lich_counter+1); else fprintf (stderr, "LICH G24 ERR"); - //this is what 2.rrc is supposed to be -- according to m17-demod -- code fixed now and reflects this - //SRC: AB2CDE, DEST: BROADCAST, STR:V/V, ENC:NONE, CAN:10, NONCE: 0000000000000000000000000000, CRC: 99af + if (err = 0 && lich_reserve != 0) fprintf(stderr, " LRS: %d", lich_reserve); //transfer to storage for (i = 0; i < 40; i++) @@ -159,15 +253,23 @@ int M17processLICH(dsd_state * state, dsd_opts * opts, uint8_t lich_bits[96]) fprintf (stderr, "[%02X]", (uint8_t)ConvertBitIntoBytes(&lich_decoded[i*8], 8)); } + uint8_t lsf_packed[30]; + memset (lsf_packed, 0, sizeof(lsf_packed)); + if (lich_counter == 5) { - crc_cmp = crc16m17 (state->m17_lsf, 240); + + //need to pack bytes for the sw5wwp variant of the crc (might as well, may be useful in the future) + for (i = 0; i < 30; i++) + lsf_packed[i] = (uint8_t)ConvertBitIntoBytes(&state->m17_lsf[i*8], 8); + + crc_cmp = crc16m17(lsf_packed, 28); crc_ext = (uint16_t)ConvertBitIntoBytes(&state->m17_lsf[224], 16); if (crc_cmp != crc_ext) crc_err = 1; if (crc_err == 0) - M17decodeLSF(state, opts); + M17decodeLSF(state); if (opts->payload == 1) { @@ -175,22 +277,29 @@ int M17processLICH(dsd_state * state, dsd_opts * opts, uint8_t lich_bits[96]) for (i = 0; i < 30; i++) { if (i == 15) fprintf (stderr, "\n "); - fprintf (stderr, "[%02X]", (uint8_t)ConvertBitIntoBytes(&state->m17_lsf[i*8], 8)); + fprintf (stderr, "[%02X]", lsf_packed[i]); } } - // if (crc_err != 0) - // fprintf (stderr, " %04X - %04X (CRC ERR)", crc_cmp, crc_ext); - if (crc_err == 1) fprintf (stderr, " LSF CRC ERR"); + memset (state->m17_lsf, 1, sizeof(state->m17_lsf)); + + //debug + // fprintf (stderr, " E-%04X; C-%04X (CRC CHK)", crc_ext, crc_cmp); + + if (crc_err == 1) fprintf (stderr, " EMB LSF CRC ERR"); } return err; } -void M17processCodec2_3200(dsd_opts * opts, dsd_state * state, uint8_t payload[128]) +void M17processCodec2_1600(dsd_opts * opts, dsd_state * state, uint8_t payload[128]) { + + //If payload is the same from the stream in this mode, I suppose we could use this area to parse + //or send off the remaining data to be handled or decoded , or just print out the raw data bytes + int i; - unsigned char voice1[8]; + unsigned char voice1[8]; //codec2_bytes_per_frame unsigned char voice2[8]; for (i = 0; i < 8; i++) @@ -199,8 +308,86 @@ void M17processCodec2_3200(dsd_opts * opts, dsd_state * state, uint8_t payload[1 voice2[i] = (unsigned char)ConvertBitIntoBytes(&payload[i*8+64], 8); } - //TODO: Either Make seperate 3200 and 1600 handling, or make this handle both - //depends on how many bits 1600 needs and if its split the same as this + //TODO: Add some decryption methods + if (state->m17_enc != 0) + { + //process scrambler or AES-CTR decryption + //(no AES-CTR right now, Scrambler should be easy enough) + } + + if (opts->payload == 1) + { + fprintf (stderr, "\n CODEC2: "); + for (i = 0; i < 8; i++) + fprintf (stderr, "%02X", voice1[i]); + fprintf (stderr, " (1600)"); + + fprintf (stderr, "\n LSDATA: "); //low speed data + for (i = 0; i < 8; i++) + fprintf (stderr, "%02X", voice2[i]); + } + + #ifdef USE_CODEC2 + size_t nsam; + nsam = 320; //codec2_samples_per_frame(state->codec2_1600); + + short samp1[nsam]; + // short samp2[nsam]; + memset (samp1, 0, sizeof(samp1)); + // memset (samp2, 0, sizeof(samp2)); + + + codec2_decode(state->codec2_1600, samp1, voice1); + // codec2_decode(state->codec2_1600, samp2, voice2); + + + if (opts->audio_out_type == 0 && state->m17_enc == 0) + { + pa_simple_write(opts->pulse_digi_dev_out, samp1, nsam*2, NULL); + // pa_simple_write(opts->pulse_digi_dev_out, samp2, nsam*2, NULL); + } + + if (opts->audio_out_type == 5 && state->m17_enc == 0) + { + write (opts->audio_out_fd, samp1, nsam*2); + // write (opts->audio_out_fd, samp2, nsam*2); + } + + //WIP: Wav file saving -- still need a way to open/close/label wav files similar to call history + if(opts->wav_out_f != NULL && state->m17_enc == 0) + { + sf_write_short(opts->wav_out_f, samp1, nsam); + // sf_write_short(opts->wav_out_f, samp2, nsam); + } + + //TODO: Codec2 Raw file saving + // if(mbe_out_dir) + // { + + // } + + #endif + +} + +void M17processCodec2_3200(dsd_opts * opts, dsd_state * state, uint8_t payload[128]) +{ + int i; + unsigned char voice1[8]; //codec2_bytes_per_frame + unsigned char voice2[8]; + + for (i = 0; i < 8; i++) + { + voice1[i] = (unsigned char)ConvertBitIntoBytes(&payload[i*8+0], 8); + voice2[i] = (unsigned char)ConvertBitIntoBytes(&payload[i*8+64], 8); + } + + //TODO: Add some decryption methods + if (state->m17_enc != 0) + { + //process scrambler or AES-CTR decryption + //(no AES-CTR right now, Scrambler should be easy enough) + } if (opts->payload == 1) { @@ -215,10 +402,9 @@ void M17processCodec2_3200(dsd_opts * opts, dsd_state * state, uint8_t payload[1 fprintf (stderr, " (3200)"); } - //TODO: Rework parts of this (we already know nsam = 160 on 3200 and 320 on 1600, etc) #ifdef USE_CODEC2 size_t nsam; - nsam = codec2_samples_per_frame(state->codec2_3200); + nsam = 160; //codec2_samples_per_frame(state->codec2_3200); short samp1[nsam]; short samp2[nsam]; @@ -230,20 +416,30 @@ void M17processCodec2_3200(dsd_opts * opts, dsd_state * state, uint8_t payload[1 codec2_decode(state->codec2_3200, samp2, voice2); - if (opts->audio_out_type == 0) + if (opts->audio_out_type == 0 && state->m17_enc == 0) { pa_simple_write(opts->pulse_digi_dev_out, samp1, nsam*2, NULL); pa_simple_write(opts->pulse_digi_dev_out, samp2, nsam*2, NULL); } - if (opts->audio_out_type == 5) + if (opts->audio_out_type == 5 && state->m17_enc == 0) { write (opts->audio_out_fd, samp1, nsam*2); write (opts->audio_out_fd, samp2, nsam*2); } - //TODO: Wav file saving + //WIP: Wav file saving -- still need a way to open/close/label wav files similar to call history + if(opts->wav_out_f != NULL && state->m17_enc == 0) + { + sf_write_short(opts->wav_out_f, samp1, nsam); + sf_write_short(opts->wav_out_f, samp2, nsam); + } + //TODO: Codec2 Raw file saving + // if(mbe_out_dir) + // { + + // } #endif @@ -251,13 +447,6 @@ void M17processCodec2_3200(dsd_opts * opts, dsd_state * state, uint8_t payload[1 void M17prepareStream(dsd_opts * opts, dsd_state * state, uint8_t m17_bits[368]) { - //this function will prepare Stream frames - - // Scheme P2 is for frames (excluding LICH chunks, which are coded differently). This takes 296 - // encoded bits and selects 272 of them. Every 12th bit is being punctured out, leaving 272 bits. - // The full matrix shall have 12 entries with 11 being ones. - - // P2 = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] int i, j, k, x; uint8_t m17_punc[310]; //25 * 11 = 275 @@ -342,21 +531,22 @@ void M17prepareStream(dsd_opts * opts, dsd_state * state, uint8_t m17_bits[368]) if (state->m17_str_dt == 2) M17processCodec2_3200(opts, state, payload); - else if (state->m17_str_dt == 3) fprintf (stderr, " V+D;"); //format not available yet in manual + else if (state->m17_str_dt == 3) + M17processCodec2_1600(opts, state, payload); else if (state->m17_str_dt == 1) fprintf (stderr, " DATA;"); else if (state->m17_str_dt == 0) fprintf (stderr, " RES;"); - else fprintf (stderr, " UNK;"); + // else fprintf (stderr, " UNK;"); - if (opts->payload == 1 && state->m17_str_dt != 2) + if (opts->payload == 1 && state->m17_str_dt < 2) { fprintf (stderr, "\n STREAM: "); for (i = 0; i < 18; i++) - fprintf (stderr, "[%02X]", (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8)); + fprintf (stderr, "[%02X]", (uint8_t)ConvertBitIntoBytes(&trellis_buf[i*8], 8)); } } -void processM17(dsd_opts * opts, dsd_state * state) +void processM17STR(dsd_opts * opts, dsd_state * state) { int i, x; @@ -380,12 +570,6 @@ void processM17(dsd_opts * opts, dsd_state * state) memset (m17_bits, 0, sizeof(m17_bits)); memset (lich_bits, 0, sizeof(lich_bits)); - - /* - Within the Physical Layer, the 368 Type 4 bits are randomized and combined with the 16-bit - Stream Sync Burst, which results in a complete frame of 384 bits (384 bits / 9600bps = 40 ms). - */ - //load dibits into dibit buffer for (i = 0; i < 184; i++) dbuf[i] = (uint8_t) getDibit(opts, state); @@ -423,4 +607,140 @@ void processM17(dsd_opts * opts, dsd_state * state) //ending linebreak fprintf (stderr, "\n"); -} //end processM17 +} //end processM17STR + +void processM17LSF(dsd_opts * opts, dsd_state * state) +{ + + //NOTE1: Pretty sure the flow here is all correct, but the initial (and only) + //LSF frame is demodulated incorrectly, so more than likely, this frame will + //always be bad since there is only one sent before STR + + //NOTE2: Even with the preamble detection and LSF sync, we still get bad decode on this + + int i, j, k, x; + uint8_t dbuf[384]; //384-bit frame - 16-bit (8 symbol) sync pattern (184 dibits) + uint8_t m17_rnd_bits[500]; //368 bits that are still scrambled (randomized) + uint8_t m17_int_bits[500]; //368 bits that are still interleaved + uint8_t m17_bits[500]; //368 bits that have been de-interleaved and de-scramble + uint8_t m17_depunc[500]; //488 bits after depuncturing + + memset (dbuf, 0, sizeof(dbuf)); + memset (m17_rnd_bits, 0, sizeof(m17_rnd_bits)); + memset (m17_int_bits, 0, sizeof(m17_int_bits)); + memset (m17_bits, 0, sizeof(m17_bits)); + memset (m17_depunc, 0, sizeof(m17_depunc)); + + //load dibits into dibit buffer + for (i = 0; i < 184; i++) + dbuf[i] = (uint8_t) getDibit(opts, state); + + //convert dbuf into a bit array + for (i = 0; i < 184; i++) + { + m17_rnd_bits[i*2+0] = (dbuf[i] >> 1) & 1; + m17_rnd_bits[i*2+1] = (dbuf[i] >> 0) & 1; + } + + //descramble the frame + for (i = 0; i < 368; i++) + m17_int_bits[i] = (m17_rnd_bits[i] ^ m17_scramble[i]) & 1; + + //deinterleave the bit array using Quadratic Permutation Polynomial + //function π(x) = (45x + 92x^2 ) mod 368 + for (i = 0; i < 368; i++) + { + x = ((45*i)+(92*i*i)) % 368; + m17_bits[i] = m17_int_bits[x]; + } + + j = 0; k = 0; x = 0; + + // P1 Depuncture + for (i = 0; i < 488; i++) + { + if (p1[k++] == 1) m17_depunc[x++] = m17_bits[j++]; + else m17_depunc[x++] = 1; + + if (k == 61) k = 0; //61 -- should reset 8 times againt the array + + } + + //debug -- values seem okay at end of run + // fprintf (stderr, "K = %d; J = %d; X = %d", k, j, x); + + //setup the convolutional decoder + uint8_t temp[500]; + uint8_t s0; + uint8_t s1; + uint8_t m_data[32]; + uint8_t trellis_buf[400]; + memset (trellis_buf, 0, sizeof(trellis_buf)); + memset (temp, 0, sizeof (temp)); + memset (m_data, 0, sizeof (m_data)); + + for (i = 0; i < 488; i++) + temp[i] = m17_depunc[i] << 1; + + CNXDNConvolution_start(); + for (i = 0; i < 244; i++) + { + s0 = temp[(2*i)]; + s1 = temp[(2*i)+1]; + + CNXDNConvolution_decode(s0, s1); + } + + CNXDNConvolution_chainback(m_data, 240); + + //244/8 = 30, last 4 (244-248) are trailing zeroes + for(i = 0; i < 30; 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; + } + + memset (state->m17_lsf, 0, sizeof(state->m17_lsf)); + memcpy (state->m17_lsf, trellis_buf, 240); + + uint8_t lsf_packed[30]; + memset (lsf_packed, 0, sizeof(lsf_packed)); + + //need to pack bytes for the sw5wwp variant of the crc (might as well, may be useful in the future) + for (i = 0; i < 30; i++) + lsf_packed[i] = (uint8_t)ConvertBitIntoBytes(&state->m17_lsf[i*8], 8); + + uint16_t crc_cmp = crc16m17(lsf_packed, 28); + uint16_t crc_ext = (uint16_t)ConvertBitIntoBytes(&state->m17_lsf[224], 16); + int crc_err = 0; + + if (crc_cmp != crc_ext) crc_err = 1; + + if (crc_err == 0) + M17decodeLSF(state); + + if (opts->payload == 1) + { + fprintf (stderr, "\n LSF: "); + for (i = 0; i < 30; i++) + { + if (i == 15) fprintf (stderr, "\n "); + fprintf (stderr, "[%02X]", lsf_packed[i]); + } + } + + //debug + // fprintf (stderr, " E-%04X; C-%04X (CRC CHK)", crc_ext, crc_cmp); + + if (crc_err == 1) fprintf (stderr, " CRC ERR"); + + //ending linebreak + fprintf (stderr, "\n"); + +} //end processM17LSF