mirror of https://github.com/lwvmobile/dsd-fme.git
1208 lines
33 KiB
C
1208 lines
33 KiB
C
/*-------------------------------------------------------------------------------
|
|
* 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<len; i++)
|
|
{
|
|
uint8_t bit = buf[i] & 1;
|
|
crc = ((crc << 1) | bit) & 0x1ffff;
|
|
if (crc & 0x10000) crc = (crc & 0xffff) ^ poly;
|
|
}
|
|
crc = crc ^ 0xffff;
|
|
return crc & 0xffff;
|
|
}
|
|
|
|
//modified version of nxdn_deperm_facch1 -- this one for V/D Type 2 CC DCH (100 dibit version)
|
|
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];
|
|
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 = 9 dibits and depth N = 20
|
|
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++)
|
|
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; 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; 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; 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
|