/*------------------------------------------------------------------------------- * ysf.c * Yaesu Fusion Decoder (WIP) * * Bits of code and ideas from DSDcc, Osmocom OP25, gr-ysf, Munaut sprinkled in * * LWVMOBILE * 2023-07 DSD-FME Florida Man Edition *-----------------------------------------------------------------------------*/ #include "dsd.h" /* thx gr-ysf fr_vch_decoder_bb_impl.cc * Copyright 2015 Mathias Weyland */ // I hold Sylvain Munaut in high esteem for figuring this out. uint8_t fr_interleave[144] = { 0, 7, 12, 19, 24, 31, 36, 43, 48, 55, 60, 67, // [ 0 - 11] yellow message 72, 79, 84, 91, 96, 103, 108, 115, 120, 127, 132, // [ 12 - 22] yellow FEC 139, 1, 6, 13, 18, 25, 30, 37, 42, 49, 54, 61, // [ 23 - 34] orange message 66, 73, 78, 85, 90, 97, 102, 109, 114, 121, 126, // [ 35 - 45] orange FEC 133, 138, 2, 9, 14, 21, 26, 33, 38, 45, 50, 57, // [ 46 - 57] red message 62, 69, 74, 81, 86, 93, 98, 105, 110, 117, 122, // [ 58 - 68] red FEC 129, 134, 141, 3, 8, 15, 20, 27, 32, 39, 44, 51, // [ 69 - 80] pink message 56, 63, 68, 75, 80, 87, 92, 99, 104, 111, 116, // [ 81 - 91] pink FEC 123, 128, 135, 140, 4, 11, 16, 23, 28, 35, 40, // [ 92 - 102] dark blue message 47, 52, 59, 64, // [103 - 106] dark blue FEC 71, 76, 83, 88, 95, 100, 107, 112, 119, 124, 131, // [107 - 117] light blue message 136, 143, 5, 10, // [118 - 121] light blue FEC 17, 22, 29, 34, 41, 46, 53, 58, 65, 70, 77, // [122 - 132] green message 82, 89, 94, 101, // [133 - 136] green FEC 106, 113, 118, 125, 130, 137, 142, // [137 - 143] unprotected }; uint8_t pn95[512] = { 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1 }; //half-rate (from NXDN) const int YnW[36] = { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 }; const int YnX[36] = { 23, 10, 22, 9, 21, 8, 20, 7, 19, 6, 18, 5, 17, 4, 16, 3, 15, 2, 14, 1, 13, 0, 12, 10, 11, 9, 10, 8, 9, 7, 8, 6, 7, 5, 6, 4 }; const int YnY[36] = { 0, 2, 0, 2, 0, 2, 0, 2, 0, 3, 0, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3 }; const int YnZ[36] = { 5, 3, 4, 2, 3, 1, 2, 0, 1, 13, 0, 12, 22, 11, 21, 10, 20, 9, 19, 8, 18, 7, 17, 6, 16, 5, 15, 4, 14, 3, 13, 2, 12, 1, 11, 0 }; //M = 26, depth of 4; -- from DSDcc const int vd2Interleave[104] = { 0, 26, 52, 78, 1, 27, 53, 79, 2, 28, 54, 80, 3, 29, 55, 81, 4, 30, 56, 82, 5, 31, 57, 83, 6, 32, 58, 84, 7, 33, 59, 85, 8, 34, 60, 86, 9, 35, 61, 87, 10, 36, 62, 88, 11, 37, 63, 89, 12, 38, 64, 90, 13, 39, 65, 91, 14, 40, 66, 92, 15, 41, 67, 93, 16, 42, 68, 94, 17, 43, 69, 95, 18, 44, 70, 96, 19, 45, 71, 97, 20, 46, 72, 98, 21, 47, 73, 99, 22, 48, 74, 100, 23, 49, 75, 101, 24, 50, 76, 102, 25, 51, 77, 103 }; void ysf_dch_decode (dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint8_t ft, uint8_t cm, uint8_t input[]) { //TODO: Per Call WAV files using these strings int i; char dch_bytes[20]; memset (dch_bytes, 0, sizeof(dch_bytes)); char string1[11]; char string2[11]; char rem1[6]; char rem2[6]; UNUSED3(bt, fn, ft); for (i = 0; i < 20; i++) dch_bytes[i] = (char)ConvertBitIntoBytes(&input[i*8], 8); switch(bn){ //using bn here so we can use the frame number for sorting the text messages found in here case 0: //CSD1 //Destination / Target if (cm != 1) { memcpy (string1, dch_bytes, 10); string1[10] = '\0'; fprintf (stderr, "DST: "); fprintf (stderr, "%s ", string1); } else //Radio ID Mode -- updated in the 1V02 spec manual { memcpy (rem1, dch_bytes, 5); rem1[5] = '\0'; fprintf (stderr, "DST RID: "); fprintf (stderr, "%s ", rem1); memcpy (rem2, dch_bytes+5, 5); rem2[5] = '\0'; fprintf (stderr, "SRC RID: "); fprintf (stderr, "%s ", rem2); } //Source memcpy (string2, dch_bytes+10, 10); string2[10] = '\0'; fprintf (stderr, "SRC: "); fprintf (stderr, "%s ", string2); //Copy both to Ncurses Call String memcpy (state->ysf_tgt, dch_bytes, 10); state->ysf_tgt[10] = '\0'; memcpy (state->ysf_src, dch_bytes+10, 10); state->ysf_src[10] = '\0'; break; case 1: //CSD2 //Uplink memcpy (string1, dch_bytes, 10); string1[10] = '\0'; fprintf (stderr, "U/L: "); fprintf (stderr, "%s ", string1); //Downlink memcpy (string2, dch_bytes+10, 10); string2[10] = '\0'; fprintf (stderr, "D/L: "); fprintf (stderr, "%s ", string2); //Copy both to Ncurses Call String memcpy (state->ysf_upl, dch_bytes, 10); state->ysf_upl[10] = '\0'; state->ysf_dnl[10] = '\0'; memcpy (state->ysf_dnl, dch_bytes+10, 10); break; case 2: for (i = 0; i < 20; i++) { if (dch_bytes[i] > 0x19 && dch_bytes[i] < 0x7F) fprintf (stderr, "%c", dch_bytes[i]); else fprintf (stderr, "."); } fprintf (stderr, " "); // if (fn == 0) memset (state->ysf_txt, 0, sizeof(state->ysf_txt)); //copy text to txt storage -- works now (had to expand storage space), but is cumbersome in ncurses (too long) // if (fn < 20) // { // //switch to checking each byte for a 'nice' ASCII character and // //not a 'naughty' del/rem/break/garbled non ASCII/ALPHANUMERIC type character // for (i = 0; i < 20; i++) // { // C = dch_bytes[i]; //skipping 0x20 space key // if (C > 0x20 && C < 0x7F) state->ysf_txt[fn][i] = C; // else state->ysf_txt[fn][i] = 0; //NULL // } // } break; default: break; } } void ysf_dch_decode2 (dsd_state * state, uint8_t bn, uint8_t bt, uint8_t fn, uint8_t ft, uint8_t cm, uint8_t input[]) { //TODO: Per Call WAV files using these strings int i; char dch_bytes[20]; memset (dch_bytes, 0, sizeof(dch_bytes)); char string[11]; char rem1[6]; char rem2[6]; UNUSED4(bn, bt, fn, ft); for (i = 0; i < 10; i++) dch_bytes[i] = (char)ConvertBitIntoBytes(&input[i*8], 8); switch(fn){ case 0: //Destination / Target if (cm != 1) { memcpy (string, dch_bytes, 10); string[10] = '\0'; fprintf (stderr, "DST: "); fprintf (stderr, "%s ", string); } else //Radio ID Mode -- updated in the 1V02 spec manual { memcpy (rem1, dch_bytes, 5); rem1[5] = '\0'; fprintf (stderr, "DST RID: "); fprintf (stderr, "%s ", rem1); memcpy (rem2, dch_bytes+5, 5); rem2[5] = '\0'; fprintf (stderr, "SRC RID: "); fprintf (stderr, "%s ", rem2); } memcpy (state->ysf_tgt, dch_bytes, 10); state->ysf_tgt[10] = '\0'; break; case 1: //Source memcpy (string, dch_bytes, 10); string[10] = '\0'; fprintf (stderr, "SRC: "); fprintf (stderr, "%s", string); memcpy (state->ysf_src, dch_bytes, 10); state->ysf_src[10] = '\0'; break; case 2: //Uplink memcpy (string, dch_bytes, 10); string[10] = '\0'; fprintf (stderr, "U/L: "); fprintf (stderr, "%s", string); memcpy (state->ysf_upl, dch_bytes, 10); state->ysf_upl[10] = '\0'; break; case 3: //Downlink memcpy (string, dch_bytes, 10); string[10] = '\0'; fprintf (stderr, "D/L: "); fprintf (stderr, "%s", string); state->ysf_dnl[10] = '\0'; memcpy (state->ysf_dnl, dch_bytes, 10); break; case 4: //Remarks 1 and 2 memcpy (rem1, dch_bytes, 5); rem1[5] = '\0'; fprintf (stderr, "RM1: "); fprintf (stderr, "%s ", rem1); memcpy (rem2, dch_bytes+5, 5); rem2[5] = '\0'; fprintf (stderr, "RM2: "); fprintf (stderr, "%s ", rem2); memcpy (state->ysf_rm1, dch_bytes, 5); state->ysf_rm1[5] = '\0'; memcpy (state->ysf_rm2, dch_bytes+5, 5); state->ysf_rm2[5] = '\0'; break; case 5: //Remarks 3 and 4 memcpy (rem1, dch_bytes, 5); rem1[5] = '\0'; fprintf (stderr, "RM3: "); fprintf (stderr, "%s ", rem1); memcpy (rem2, dch_bytes+5, 5); rem2[5] = '\0'; fprintf (stderr, "RM4: "); fprintf (stderr, "%s ", rem2); memcpy (state->ysf_rm3, dch_bytes, 5); state->ysf_rm3[5] = '\0'; memcpy (state->ysf_rm4, dch_bytes+5, 5); state->ysf_rm4[5] = '\0'; break; default: break; } } static inline uint16_t crc16ysf(const uint8_t buf[], int len) { uint32_t poly = (1<<12) + (1<<5) + (1<<0); uint32_t crc = 0; for(int i=0; i> 1) & 1; bits[k++] = (buf[i] >> 0) & 1; } //setup for the convolutional decoder for (i = 0; i < 200; i++) 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; } uint16_t crc = crc16ysf(trellis_buf, 96); if (crc != 0) err = -2; // crc failure for (i = 0; i < 80; i++) trellis_buf[i] = trellis_buf[i] ^ pn95[i]; //reload after de-whitening memset (m_data, 0, sizeof (m_data)); for (i = 0; i < 12; i++) m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[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); else { fprintf (stderr, "%s", KRED); fprintf (stderr, "DCH (CRC ERR) "); fprintf (stderr, "%s", KNRM); } if (opts->payload == 1) { fprintf (stderr, "\n "); fprintf (stderr, "DCH2: "); for (i = 0; i < 12; i++) fprintf (stderr, "[%02X]", m_data[i]); } return err; } //modified version of nxdn_deperm_facch1 -- this one for 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]; uint8_t temp[370]; uint8_t m_data[100]; uint8_t bits[370]; memset (trellis_buf, 0, sizeof(trellis_buf)); memset (temp, 0, sizeof (temp)); memset (m_data, 0, sizeof (m_data)); memset (bits, 0, sizeof(bits)); err = 0; //dibit de-interleave block length M = 9 dibits and depth N = 20 uint8_t buf[180]; memset (buf, 0, sizeof(buf)); for (i=0; i<20; i++) { //20*9 = 180 for (j=0; j<9; j++) { buf[j+(i*9)] = input[i+(j*20)]; } } k = 0; //convert dibits to bits for (i = 0; i < 180; i++) { bits[k++] = (buf[i] >> 1) & 1; bits[k++] = (buf[i] >> 0) & 1; } //setup for the convolutional decoder for (i = 0; i < 360; i++) temp[i] = bits[i] << 1; 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); if (crc != 0) err = -2; // crc failure for (i = 0; i < 160; i++) trellis_buf[i] = trellis_buf[i] ^ pn95[i]; //reload after de-whitening memset (m_data, 0, sizeof (m_data)); for (i = 0; i < 22; i++) m_data[i] = (uint8_t)ConvertBitIntoBytes(&trellis_buf[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); else { fprintf (stderr, "%s", KRED); fprintf (stderr, "DCH (CRC ERR) "); fprintf (stderr, "%s", KNRM); } if (opts->payload == 1) { fprintf (stderr, "\n "); fprintf (stderr, "DCH1: "); for (i = 0; i < 22; i++) fprintf (stderr, "[%02X]", m_data[i]); } return err; } //modified version of nxdn_deperm_facch1 int ysf_conv_fich (uint8_t input[], uint8_t dest[32]) { int i, j, k, err; uint8_t s0, s1; uint8_t trellis_buf[100]; uint8_t temp[210]; uint8_t m_data[100]; uint8_t bits[210]; memset (trellis_buf, 0, sizeof(trellis_buf)); memset (temp, 0, sizeof (temp)); memset (m_data, 0, sizeof (m_data)); memset (bits, 0, sizeof(bits)); err = 0; //dibit de-interleave block length M = 5 dibits and depth N = 10 uint8_t buf[100]; memset (buf, 0, sizeof(buf)); for (i=0; i<20; i++) { for (j=0; j<5; j++) { buf[j+(i*5)] = input[i+(j*20)]; } } k = 0; //convert dibits to bits for (i = 0; i < 100; i++) { bits[k++] = (buf[i] >> 1) & 1; bits[k++] = (buf[i] >> 0) & 1; } //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; } uint8_t fich_bits[12*4]; uint8_t temp_b[24]; bool g[4]; // run golay 24_12 error correction for (i = 0; i < 4; i++) { memset (temp, 0, sizeof(temp_b)); g[i] = FALSE; for (j = 0; j < 24; j++) temp_b[j] = (char) trellis_buf[(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]; } //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]; } 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; } //YSF pn95 scrambler/whitening bit generator with seed 111001001 void pn95_lfsr() //test to see if this generates the correct bits now { int i; int lfsr; int pN[513]; memset (pN, 0, sizeof(pN)); int bit = 0; lfsr = 0x1c9; //111001001 initial value fprintf (stderr, "\n pN95 = { \n"); for (i = 0; i < 512; i++) { pN[i] = lfsr & 0x1; bit = ( (lfsr >> 4) ^ (lfsr >> 0) ) & 1; lfsr = ( (lfsr >> 1 ) | (bit << 9) ); //9, or 8 if (i % 8 == 0) fprintf (stderr, "\n"); fprintf (stderr, " %d,", pN[i]); } fprintf (stderr, "};"); } void ysf_ehr (dsd_opts * opts, dsd_state * state, uint8_t dbuf[180], int start, int stop ) { int i; char ambe_fr[4][24]; memset (ambe_fr, 0, sizeof(ambe_fr)); const int *w, *x, *y, *z; int st = state->synctype; state->synctype = 28; uint8_t b1, b2; for (; start < stop; start++) { w = YnW; x = YnX; y = YnY; z = YnZ; //debug // fprintf (stderr, " DBUF = "); for (i = 0; i < 36; i++) { //debug // fprintf (stderr, "%d", dbuf[(start*36)+i]); b1 = dbuf[(start*36)+i] >> 1; b2 = dbuf[(start*36)+i] & 1; //should all be loaded back to back ambe_fr[*w][*x] = (char)b1; ambe_fr[*y][*z] = (char)b2; w++; x++; y++; z++; } processMbeFrame (opts, state, NULL, ambe_fr, NULL); if (opts->floating_point == 0) { // processAudio(opts, state); //needed here? -- nothign to test it with if (opts->wav_out_f != NULL) writeSynthesizedVoice (opts, state); if (opts->pulse_digi_out_channels == 1) playSynthesizedVoiceMS(opts, state); if(opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); } if (opts->floating_point == 1) //float audio is really quiet now (look into it) { memcpy (state->f_l, state->audio_out_temp_buf, sizeof(state->f_l)); if (opts->pulse_digi_out_channels == 1) playSynthesizedVoiceFM(opts, state); if(opts->pulse_digi_out_channels == 2) playSynthesizedVoiceFS(opts, state); } } if (opts->payload == 1) { fprintf(stderr, "\n"); } state->synctype = st; } void processYSF(dsd_opts * opts, dsd_state * state) { //TODO: Reorganize and remove unused variables and arrays, etc static uint8_t last_dt, last_fi; //if we can't get a good dt and fi, then just use the last one instead int i, j, k, l, err, vstart, vstop, dstart, dstop; //start stops are for Full Rate when we might have some portions of Data present in the Comm Channel int dibit; uint8_t fichdibits[100]; //fich dibits uint8_t fichrawdibits[100]; uint8_t fichrawbits[200]; uint8_t fich_d_bits[100]; uint8_t fich_decode[32]; memset (fichdibits, 0, sizeof(fichdibits)); memset (fichrawdibits, 0, sizeof(fichrawdibits)); memset (fichrawbits, 0, sizeof(fichrawbits)); memset (fich_d_bits, 0, sizeof(fich_d_bits)); memset (fich_decode, 0, sizeof(fich_decode)); char ambe_fr[4][24]; memset (ambe_fr, 0, sizeof(ambe_fr)); char imbe_fr[8][23]; memset (imbe_fr, 0, sizeof(imbe_fr)); uint8_t b1, b2, msb, lsb; b1 = b2 = msb = lsb = vstart = vstop = dstart = dstop = 0; //fich information uint8_t fi = 9; //Header, Communications, or Terminator // uint8_t cs = 9; //Type of Callsign uint8_t cm = 9; //Type of Call uint8_t bn = 9; //block number uint8_t bt = 9; //block total uint8_t fn = 9; //frame number uint8_t ft = 9; //frame total uint8_t mr = 9; //message path uint8_t vp = 9; //VoIP path uint8_t dt = 9; //Data Type -- type 1 (EHR no VeCH); type 2 (EHR w/ VeCH); type 3 (EFR) uint8_t st = 9; //SQL Type uint8_t sc = 69; //SQL Code // pn95lfsr(); //run to print the bits for the DCH/VeCH whitening array //FICH you for (i = 0; i < 100; i++) fichrawdibits[i] = getDibit(opts, state); //from nxdn_deperm_facch1 w/ nxdn convolutional decoder err = ysf_conv_fich (fichrawdibits, fich_decode); //if errors decoding fich, then just treat it like the last frame that came in if (err == 0) { fi = (uint8_t)ConvertBitIntoBytes(&fich_decode[0], 2); // cs = (uint8_t)ConvertBitIntoBytes(&fich_decode[2], 2); cm = (uint8_t)ConvertBitIntoBytes(&fich_decode[4], 2); bn = (uint8_t)ConvertBitIntoBytes(&fich_decode[6], 2); bt = (uint8_t)ConvertBitIntoBytes(&fich_decode[8], 2); fn = (uint8_t)ConvertBitIntoBytes(&fich_decode[10], 3); ft = (uint8_t)ConvertBitIntoBytes(&fich_decode[13], 3); mr = (uint8_t)ConvertBitIntoBytes(&fich_decode[18], 3); vp = fich_decode[21]; dt = (uint8_t)ConvertBitIntoBytes(&fich_decode[22], 2); st = fich_decode[24]; sc = (uint8_t)ConvertBitIntoBytes(&fich_decode[25], 7); state->ysf_dt = dt; state->ysf_fi = fi; state->ysf_cm = cm; last_dt = dt; last_fi = fi; } else { dt = last_dt; fi = last_fi; } //print some useful decoded stuff if (dt == 0 && err == 0) fprintf (stderr, "V/D1 "); //Voice/Data Type 1 if (dt == 1 && err == 0) fprintf (stderr, "DATA "); //Full Rate Data if (dt == 2 && err == 0) fprintf (stderr, "V/D2 "); //Voice/Data Type 2 if (dt == 3 && err == 0) fprintf (stderr, "VWFR "); //Full Rate Voice if (cm == 0) fprintf (stderr, "Group/CQ "); if (cm == 3) fprintf (stderr, "Private "); if (cm == 1) fprintf (stderr, "RID Mode "); //Radio ID Mode -- updated in the 1V02 spec manual if (cm == 2) fprintf (stderr, "Res: 2 "); // if (vp == 0) fprintf (stderr, "Local (Simplex) "); // if (vp == 1) fprintf (stderr, "Internet (Rep) "); if (vp == 0) fprintf (stderr, "-Simplex "); if (vp == 1) fprintf (stderr, "Repeater "); //disabling below lines, seems mostly redundant, //any Simplex is Direct Wave, and if its VoIP, then the Uplink is always busy (I think) // if (mr == 0) fprintf (stderr, "(Direct Wave) "); // if (mr == 1) fprintf (stderr, "(Uplink Free) "); // if (mr == 2) fprintf (stderr, "(Uplink Busy) "); if (mr > 2 && mr < 7) fprintf (stderr, "Res: %03d ", mr); if (fi == 0 && err == 0) fprintf (stderr, "HC "); //Header if (fi == 1 && err == 0) fprintf (stderr, "CC "); //Communication if (fi == 2 && err == 0) fprintf (stderr, "TC "); //Terminator if (fi == 3 && err == 0) fprintf (stderr, "XX "); //Test if (st && sc != 69) fprintf (stderr, "SQL "); if (st && sc != 69) fprintf (stderr, "CODE: %03d ", sc); //simplified version if (err == 0 && opts->payload == 1) fprintf (stderr, "FN:%d-%d ", fn, ft); if (err != 0) { fprintf (stderr, "%s", KRED); fprintf (stderr, "FICH "); if (err == -1) fprintf (stderr, "(FEC ERR) "); if (err == -2) fprintf (stderr, "(CRC ERR) "); fprintf (stderr, "%s", KNRM); } if (opts->payload == 1) { fprintf (stderr, " FICH: "); for (int i = 0; i < 4; i++) fprintf (stderr, "[%02X]", (uint8_t)ConvertBitIntoBytes(&fich_decode[i*8], 8)); } // if (fi == 0) fprintf (stderr, "%s", KGRN); //HC Channel // else if (fi == 2) fprintf (stderr, "%s", KRED); //TC Channel // else if (dt == 0 || dt == 2) fprintf (stderr, "%s", KGRN); //Voice CC // else fprintf (stderr, "%s", KCYN); //Data //DCH and VCH uint8_t dbuf[190]; //data dibit buffer uint8_t vbuf[190]; //voice dibit buffer uint8_t vech[255]; //VeCH dibit buffer uint8_t temp[512]; ///temp space for manipulating VeCH into VBUF char ambe_d[49]; memset (vbuf, 0, sizeof(vbuf)); memset (dbuf, 0, sizeof(dbuf)); memset (vech, 0, sizeof(vech)); memset (temp, 0, sizeof(temp)); //bit buffers // uint8_t dch_bits[6][72]; uint8_t dch_bits[160]; uint8_t vch_bits[6][72]; uint8_t vech_bits[104]; memset (dch_bits, 0, sizeof(dch_bits)); memset (vch_bits, 0, sizeof(vch_bits)); memset (vech_bits, 0, sizeof(vech_bits)); // V/D Mode Type 1 (no VeCH) if (fi == 1 && dt == 0) { //need to double check all this if I can get a sample of it for (i = 0; i < 5; i++) //5 on EHR Type 1 (no VeCH) { // DCH First for (j = 0; j < 36; j++) dbuf[(i*36)+j] = getDibit(opts, state); //VCH Second for (j = 0; j < 36; j++) vbuf[(i*36)+j] = getDibit(opts, state); } // send VCH to ehr voice handler ysf_ehr (opts, state, vbuf, 0, 4); //send DCH to decoder ysf_conv_dch (opts, state, bn, bt, fn, ft, cm, dbuf); } /* In the case of V/D mode type 2, error correction for the purpose of improving the connectivity of a weak electric field is carried out (separately from the error correction function of the voice encoder). */ // V/D Mode Type 2 (w/ VeCH and bs ECC) if (fi == 1 && dt == 2) { int d = 0; for (i = 0; i < 5; i++) { //DCH for (j = 0; j < 20; j++) dbuf[d++] = getDibit(opts, state); //VeCH k = 0; l = 0; memset (vech_bits, 0, sizeof(vech_bits)); memset (temp, 0, sizeof(temp)); state->errs = 0; state->errs2 = 0; for (j = 0; j < 52; j++) { dibit = getDibit(opts, state); //deinterleave and de-whiten VeCH (from DSDcc) b1 = (dibit >> 1) & 1; b2 = (dibit >> 0) & 1; msb = vd2Interleave[k]; k++; lsb = vd2Interleave[k]; k++; vech_bits[msb] = b1 ^ pn95[msb]; vech_bits[lsb] = b2 ^ pn95[lsb]; } uint8_t majority[8] = {0,0,0,1,0,1,1,1}; //I have no idea how this is considered to be a form of forward error correction l = 0; for (j = 0; j < 81; j++) { //OP25 majority method with table if (j % 3 == 2) { //I still have no idea what the method of this is...hamming table? temp[l] = majority[ (vech_bits[j-2] << 2) | (vech_bits[j-1] << 1) | vech_bits[j] ]; l++; } } for (j = 0; j < 22; j++) temp[j+27] = vech_bits[j+81]; for (j = 0; j < 49; j++) ambe_d[j] = temp[j]; //(char) state->errs2 = vech_bits[103]; //should be zero, but if it isn't, then its an error state->debug_audio_errors += state->errs2; mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality); if (opts->payload == 1) PrintAMBEData (opts, state, ambe_d); if (opts->floating_point == 0) { processAudio(opts, state); if (opts->wav_out_f != NULL) writeSynthesizedVoice (opts, state); if (opts->pulse_digi_out_channels == 1) playSynthesizedVoiceMS(opts, state); if(opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); } if (opts->floating_point == 1) { memcpy (state->f_l, state->audio_out_temp_buf, sizeof(state->f_l)); if (opts->pulse_digi_out_channels == 1) playSynthesizedVoiceFM(opts, state); if(opts->pulse_digi_out_channels == 2) playSynthesizedVoiceFS(opts, state); } } //process completed DCH ysf_conv_dch2 (opts, state, bn, bt, fn, ft, cm, dbuf); } //Full-Rate AMBE+2 EFR (Works with IMBE decoder) uint8_t imbe_vch[144]; memset (imbe_vch, 0, sizeof(imbe_vch)); uint8_t imbe_raw[144]; //Voice FR Mode (Type 3 AMBE+2 Full Rate) if (fi == 1 && dt == 3) { /* Under this pattern, when the initial HC and initial CC (Sub header (CSD3)) cannot be received, the Call sign information can not be obtained. No CSD3 information can be obtained from other frames except the FT=1/FN=0 frame. What we need to do is look at the frame, and determine if its a CSD3 by the ft == 1 and fn == 0 and set an appropirate start/stop value here */ if (ft == 1 && fn == 0) { dstart = 0; dstop = 6; //5 DCH and 1 reserved bank vstart = 0; vstop = 2; //only 2 VCH in the CSD3 Sub Header } else { dstart = 0; dstop = 0; vstart = 0; vstop = 5; } for (; dstart < dstop; dstart++) { //get dibits for CSD3 Sub Header DCH -- still need samples to test this with for (j = 0; j < 36; j++) //dbufFR[2][190] { if (dstart != 5) //only want the first 5 dbuf[(dstart*36)+j] = getDibit(opts, state); else skipDibit(opts, state, 1); //skip the reserved bank } } for (; vstart < vstop; vstart++) { //init a bunch of stuff memset (imbe_raw, 0, sizeof(imbe_raw)); memset (imbe_fr, 0, sizeof(imbe_fr)); for (j = 0; j < 72; j++) { dibit = getDibit(opts, state); b1 = (dibit >> 1) & 1; b2 = (dibit >> 0) & 1; imbe_raw[(j*2)+0] = b1; imbe_raw[(j*2)+1] = b2; } //de-interleave to vch bits for (j = 0; j < 144; j++) imbe_vch[j] = imbe_raw[fr_interleave[j]]; k = 0; int n, m; //load the bits into an imbe_fr backwards for (n = 0; n < 4; n++) { for (m = 22; m >= 0; m--) imbe_fr[n][m] = imbe_vch[k++]; } for (n = 4; n < 7; n++) { for (m = 14; m >= 0; m--) imbe_fr[n][m] = imbe_vch[k++]; } for (m = 7; m >= 0; m--) imbe_fr[7][m] = imbe_vch[k++]; //fake it as P25p1 and sent to processMBEFrame st = state->synctype; state->synctype = 0; //P25p1 processMbeFrame(opts, state, imbe_fr, NULL, NULL); state->synctype = st; if (opts->floating_point == 0) { // processAudio(opts, state); //needed here? -- seems to be running from within mbelib if (opts->wav_out_f != NULL) writeSynthesizedVoice (opts, state); if (opts->pulse_digi_out_channels == 1) playSynthesizedVoiceMS(opts, state); if(opts->pulse_digi_out_channels == 2) playSynthesizedVoiceSS(opts, state); } if (opts->floating_point == 1) //float audio is really quiet now (look into it) { memcpy (state->f_l, state->audio_out_temp_buf, sizeof(state->f_l)); if (opts->pulse_digi_out_channels == 1) playSynthesizedVoiceFM(opts, state); if(opts->pulse_digi_out_channels == 2) playSynthesizedVoiceFS(opts, state); } } if (ft == 1 && fn == 0) { //process completed DCH -- use 2 for bn to switch CSD 3 info ysf_conv_dch (opts, state, 2, bt, fn, ft, cm, dbuf); } } //full rate data uint8_t dbufFR[2][180]; memset (dbufFR, 0, sizeof(dbufFR)); if (dt == 1 || fi == 0 || fi == 2) { for (i = 0; i < 10; i++) { for (j = 0; j < 36; j++) dbufFR[i % 2][((i/2)*36)+j] = getDibit(opts, state); } //clear old txt data // if (fi == 0) memset (state->ysf_txt, 0, sizeof(state->ysf_txt)); fprintf (stderr, "\n "); for (i = 0; i < 2; i++) { //process completed DCH -- use i to for bn to switch CSD1 and CSD2 on HC and TC if (fi == 0 || fi == 2) ysf_conv_dch (opts, state, i, bt, fn, ft, cm, dbufFR[i]); //using bn == 2 for full rate data and fn*2+1 for easier frame storage else ysf_conv_dch (opts, state, 2, bt, fn*2+i, ft, cm, dbufFR[i]); } } //ending line break fprintf (stderr, "%s", KNRM); fprintf (stderr, "\n"); } //end processYSF