M17 - M17 STR Encoder and Playback (WIP);

This commit is contained in:
lwvmobile
2024-03-01 23:55:09 -05:00
parent 5830dfa991
commit c6bc10f1ba
5 changed files with 563 additions and 28 deletions

View File

@@ -270,6 +270,7 @@ typedef struct
int ssize;
int msize;
int playfiles;
int m17encoder;
int delay;
int use_cosine_filter;
int unmute_encrypted_p25;
@@ -1006,6 +1007,7 @@ void processDSTAR_SD (dsd_opts * opts, dsd_state * state, uint8_t * sd); //DSTAR
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
void processM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR)
void processM17LSF(dsd_opts * opts, dsd_state * state); //M17 (LSF)
void encodeM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR) encoder
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)

View File

@@ -1,29 +1,39 @@
//M17 Filter -- RRC Alpha = 0.5 S/s 48000 with 81 taps
#define M17ZEROS 80 //ONLY SEE 79 THOUGH
float m17gain = 10.0f;
float m17gain = 6.92820323f; //sqrt of 48 //10.0f
static float m17xv[M17ZEROS+1];
float m17coeffs[81] =
//sample RRC filter for 48kHz sample rate
//alpha=0.5, span=8, sps=10, gain=sqrt(sps)
float m17coeffs[81] = //replaced old ones with ones from libm17
{
-0.009265784007800534, -0.006136551625729697, -0.001125978562075172, 0.004891777252042491,
0.01071805138282269, 0.01505751553351295, 0.01679337935001369, 0.015256245142156299,
0.01042830577908502, 0.003031522725559901, -0.0055333532968188165, -0.013403099825723372,
-0.018598682349642525, -0.01944761739590459, -0.015005271935951746, -0.0053887880354343935,
0.008056525910253532, 0.022816244158307273, 0.035513467692208076, 0.04244131815783876,
0.04025481153629372, 0.02671818654865632, 0.0013810216516704976, -0.03394615682795165,
-0.07502635967975885, -0.11540977897637611, -0.14703962203941534, -0.16119995609538576,
-0.14969512896336504, -0.10610329539459686, -0.026921412469634916, 0.08757875030779196,
0.23293327870303457, 0.4006012210123992, 0.5786324696325503, 0.7528286479934068,
0.908262741447522, 1.0309661131633199, 1.1095611856548013, 1.1366197723675815,
1.1095611856548013, 1.0309661131633199, 0.908262741447522, 0.7528286479934068,
0.5786324696325503, 0.4006012210123992, 0.23293327870303457, 0.08757875030779196,
-0.026921412469634916, -0.10610329539459686, -0.14969512896336504, -0.16119995609538576,
-0.14703962203941534, -0.11540977897637611, -0.07502635967975885, -0.03394615682795165,
0.0013810216516704976, 0.02671818654865632, 0.04025481153629372, 0.04244131815783876,
0.035513467692208076, 0.022816244158307273, 0.008056525910253532, -0.0053887880354343935,
-0.015005271935951746, -0.01944761739590459, -0.018598682349642525, -0.013403099825723372,
-0.0055333532968188165, 0.003031522725559901, 0.01042830577908502, 0.015256245142156299,
0.01679337935001369, 0.01505751553351295, 0.01071805138282269, 0.004891777252042491,
-0.001125978562075172, -0.006136551625729697, -0.009265784007800534
-0.003195702904062073f, -0.002930279157647190f, -0.001940667871554463f,
-0.000356087678023658f, 0.001547011339077758f, 0.003389554791179751f,
0.004761898604225673f, 0.005310860846138910f, 0.004824746306020221f,
0.003297923526848786f, 0.000958710871218619f, -0.001749908029791816f,
-0.004238694106631223f, -0.005881783042101693f, -0.006150256456781309f,
-0.004745376707651645f, -0.001704189656473565f, 0.002547854551539951f,
0.007215575568844704f, 0.011231038205363532f, 0.013421952197060707f,
0.012730475385624438f, 0.008449554307303753f, 0.000436744366018287f,
-0.010735380379191660f, -0.023726883538258272f, -0.036498030780605324f,
-0.046500883189991064f, -0.050979050575999614f, -0.047340680079891187f,
-0.033554880492651755f, -0.008513823955725943f, 0.027696543159614194f,
0.073664520037517042f, 0.126689053778116234f, 0.182990955139333916f,
0.238080025892859704f, 0.287235637987091563f, 0.326040247765297220f,
0.350895727088112619f, 0.359452932027607974f, 0.350895727088112619f,
0.326040247765297220f, 0.287235637987091563f, 0.238080025892859704f,
0.182990955139333916f, 0.126689053778116234f, 0.073664520037517042f,
0.027696543159614194f, -0.008513823955725943f, -0.033554880492651755f,
-0.047340680079891187f, -0.050979050575999614f, -0.046500883189991064f,
-0.036498030780605324f, -0.023726883538258272f, -0.010735380379191660f,
0.000436744366018287f, 0.008449554307303753f, 0.012730475385624438f,
0.013421952197060707f, 0.011231038205363532f, 0.007215575568844704f,
0.002547854551539951f, -0.001704189656473565f, -0.004745376707651645f,
-0.006150256456781309f, -0.005881783042101693f, -0.004238694106631223f,
-0.001749908029791816f, 0.000958710871218619f, 0.003297923526848786f,
0.004824746306020221f, 0.005310860846138910f, 0.004761898604225673f,
0.003389554791179751f, 0.001547011339077758f, -0.000356087678023658f,
-0.001940667871554463f, -0.002930279157647190f, -0.003195702904062073f
};
// Original DMR filter

View File

@@ -625,6 +625,7 @@ initOpts (dsd_opts * opts)
opts->ssize = 128; //36 default, max is 128, much cleaner data decodes on Phase 2 cqpsk at max
opts->msize = 1024; //15 default, max is 1024, much cleaner data decodes on Phase 2 cqpsk at max
opts->playfiles = 0;
opts->m17encoder = 0;
opts->delay = 0;
opts->use_cosine_filter = 1;
opts->unmute_encrypted_p25 = 0;
@@ -1271,6 +1272,9 @@ usage ()
printf (" Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T\n");
printf (" Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:6021\n");
printf ("\n");
printf ("Encoder options:\n");
printf (" -fZ M17 Stream Voice Encoder\n");
printf ("\n");
printf ("Decoder options:\n");
printf (" -fa Auto Detection\n");
printf (" -ft TDMA Trunking P25p1 Control and Voice, P25p2 Trunked Channels, and DMR\n");
@@ -2370,6 +2374,13 @@ main (int argc, char **argv)
fprintf(stderr, "Notice: M17 cannot autodetect polarity. \n Use -xz option if Inverted Signal expected.\n");
fprintf(stderr, "Decoding only M17 frames.\n");
}
else if (optarg[0] == 'Z') //Captial Z to Run the M17 encoder
{
opts.m17encoder = 1;
opts.pulse_digi_rate_out = 48000;
opts.pulse_digi_out_channels = 1;
sprintf (opts.output_name, "M17 Encoder");
}
break;
//don't mess with the modulations unless you really need to
case 'm':
@@ -2906,6 +2917,27 @@ main (int argc, char **argv)
playMbeFiles (&opts, &state, argc, argv);
}
else if (opts.m17encoder == 1)
{
opts.pulse_digi_rate_out = 8000;
//open any inputs, if not alread opened, OSS input and output already handled
if (opts.audio_in_type == 0) openPulseInput(&opts);
#ifdef USE_RTLSDR
else if(opts.audio_in_type == 3)
{
open_rtlsdr_stream(&opts);
opts.rtl_started = 1;
}
#endif
//open any outputs, if not already opened
if (opts.audio_out_type == 0) openPulseOutput(&opts);
//All input and output now opened and handled correctly, so let's not break things by tweaking
encodeM17STR(&opts, &state);
}
else
{

View File

@@ -1308,12 +1308,12 @@ void Golay_24_12_init()
}
}
// Not very efficient but encode is used for unit testing only
// Not very efficient but encode is used for unit testing only, I think this is wrong for M17
void Golay_24_12_encode(unsigned char *origBits, unsigned char *encodedBits)
{
int i = 0, j = 0;
memset(encodedBits, 0, 24);
// memset(encodedBits, 0, 24);
for (i = 0; i < 12; i++)
{
@@ -1326,7 +1326,7 @@ void Golay_24_12_encode(unsigned char *origBits, unsigned char *encodedBits)
for (i = 0; i < 24; i++)
{
encodedBits[i] %= 2;
// fprintf (stderr, "%b", encodedBits[i]); //disable later on
// fprintf (stderr, "%b", encodedBits[i]); //disable later on
}
}

497
src/m17.c
View File

@@ -1,12 +1,12 @@
/*-------------------------------------------------------------------------------
* m17.c
* M17 Decoder (WIP)
* M17 Decoder and Decoder (LSF and STR)
*
* m17_scramble Bit Array from SDR++
* CRC16 from M17-Implementations (thanks again, sp5wwp)
*
* LWVMOBILE
* 2023-07 DSD-FME Florida Man Edition
* 2024-03 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
@@ -239,7 +239,14 @@ int M17processLICH(dsd_state * state, dsd_opts * opts, uint8_t * lich_bits)
fprintf (stderr, "LC: %d/6 ", lich_counter+1);
else fprintf (stderr, "LICH G24 ERR");
if (err = 0 && lich_reserve != 0) fprintf(stderr, " LRS: %d", lich_reserve);
// if (err == 0 && lich_reserve != 0) fprintf(stderr, " LRS: %d", lich_reserve);
//This is not M17 standard, but use the LICH reserved bits to signal data type and CAN value
if (err == 0 && opts->m17encoder == 1) //only use when using built in encoder
{
state->m17_str_dt = lich_reserve & 0x3;
state->m17_can = (lich_reserve >> 2) & 0x7;
}
//transfer to storage
for (i = 0; i < 40; i++)
@@ -795,3 +802,487 @@ void processM17LSF(dsd_opts * opts, dsd_state * state)
fprintf (stderr, "\n");
} //end processM17LSF
//debug M17STR for the encoder to pass bits into
void processM17STR_debug(dsd_opts * opts, dsd_state * state, uint8_t * m17_rnd_bits)
{
int i, x;
uint8_t m17_int_bits[368]; //368 bits that are still interleaved
uint8_t m17_bits[368]; //368 bits that have been de-interleaved and de-scrambled
uint8_t lich_bits[96];
int lich_err = -1;
memset (m17_int_bits, 0, sizeof(m17_int_bits));
memset (m17_bits, 0, sizeof(m17_bits));
memset (lich_bits, 0, sizeof(lich_bits));
//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; UNUSED(x);
m17_bits[i] = m17_int_bits[x];
}
for (i = 0; i < 96; i++)
lich_bits[i] = m17_bits[i];
//check lich first, and handle LSF chunk and completed LSF
lich_err = M17processLICH(state, opts, lich_bits);
if (lich_err == 0)
M17prepareStream(opts, state, m17_bits);
} //end processM17STR_debug
//simple convolutional encoder
void simple_conv_encoder (uint8_t * input, uint8_t * output, int len)
{
int i, k = 0;
uint8_t g1 = 0;
uint8_t g2 = 0;
uint8_t d = 0;
uint8_t d1 = 0;
uint8_t d2 = 0;
uint8_t d3 = 0;
uint8_t d4 = 0;
for (i = 0U; i < len; i++)
{
d = input[i];
g1 = (d + d3 + d4) & 1;
g2 = (d + d1 + d2 + d4) & 1;
d4 = d3;
d3 = d2;
d2 = d1;
d1 = d;
output[k++] = g1;
output[k++] = g2;
}
}
//dibits-symbols map (TX)
const int8_t symbol_map[4]={+1, +3, -1, -3};
//encode and create audio of a Project M17 STR signal
void encodeM17STR(dsd_opts * opts, dsd_state * state)
{
//TODO: Cleanup Tons of Unused Arrays and Variables
//TODO: Switch to M17 Viterbi Decoder?
//User Defined Variables
uint8_t can = 7;
//numerical representation of dst and src after encoding, or special/reserved value
unsigned long long int dst = 0;
unsigned long long int src = 0;
//DST and SRC Callsign Data (up to 9 characters in the b40 array)
char d40[] = "ABCDEFGHI"; UNUSED(d40); //DST
char s40[] = "IHGFEDCBA"; UNUSED(s40); //SRC
//end User Defined Variables
int i, j, k, x; //basic utility counters
short sample = 0;
size_t nsam = 160; //number of samples to be read in (default is for codec2 3200 bps)
size_t nbit = 64; //number of bits in each frame
#ifdef USE_CODEC2
nsam = codec2_samples_per_frame(state->codec2_3200);
nbit = codec2_bits_per_frame(state->codec2_3200);
#endif
short * samp1 = malloc (sizeof(short) * nsam);
short * samp2 = malloc (sizeof(short) * nsam);
short voice1[nsam]; //read in xxx ms of audio from input source
short voice2[nsam]; //read in xxx ms of audio from input source
int fsn, eot = 0; //frame sequence number and eot bit
int lich_cnt = 0; //lich frame number counter
uint8_t m17_lich[48]; UNUSED(m17_lich); //48-bit LICH (chunk+lich_cnt+reserved bits)
uint8_t lsf_chunk[6][48]; UNUSED(lsf_chunk); //40 bit chunks of link information spread across 6 frames
uint8_t lsf_chunkr[6][48]; UNUSED(lsf_chunkr); //reverse order bits
uint8_t m17_lsf[240]; UNUSED(m17_lsf); //the complete LSF
memset (m17_lich, 0, sizeof(m17_lich));
memset (lsf_chunk, 0, sizeof(lsf_chunk));
memset (lsf_chunkr, 0, sizeof(lsf_chunkr));
memset (m17_lsf, 0, sizeof(m17_lsf));
//NOTE: Most lich and lsf_chunk bits can be pre-set before the while loop,
//only need to refresh the lich_cnt value and golay (no meta/IV/enc)
uint16_t lsf_dt = 2; //voice (3200bps)
uint16_t lsf_et = 0; UNUSED(lsf_et);
uint16_t lsf_es = 0; UNUSED(lsf_es);
uint16_t lsf_cn = can; //can value
uint16_t lsf_rs = 0; UNUSED(lsf_rs);
uint16_t lsf_fi = 0; //16-bit frame information
lsf_fi = (lsf_dt << 1) + (lsf_cn << 7);
for (i = 0; i < 16; i++) m17_lsf[96+i] = (lsf_fi >> 15-i) & 1;
//TODO: Only the below if not reserved values "BROADCAST", "ALL", etc
//Convert base40 CSD to numerical values (lifted from libM17)
for(i = strlen((const char*)d40)-1; i >= 0; i--)
{
for(j = 0; j < 40; j++)
{
if(d40[i]==b40[j])
{
dst=dst*40+j;
break;
}
}
}
for(i = strlen((const char*)s40)-1; i >= 0; i--)
{
for(j = 0; j < 40; j++)
{
if(s40[i]==b40[j])
{
src=src*40+j;
break;
}
}
}
//end CSD conversion
for (i = 0; i < 48; i++) m17_lsf[i] = (dst >> 47ULL-(unsigned long long int)i) & 1;
for (i = 0; i < 48; i++) m17_lsf[i+48] = (src >> 47ULL-(unsigned long long int)i) & 1;
//compute the CRC16 for LSF
uint16_t crc_cmp = 0;
uint8_t lsf_packed[30];
memset (lsf_packed, 0, sizeof(lsf_packed));
for (i = 0; i < 30; i++)
lsf_packed[i] = (uint8_t)ConvertBitIntoBytes(&m17_lsf[i*8], 8);
crc_cmp = crc16m17(lsf_packed, 28);
//attach the crc16 bits to the end of the LSF data
for (i = 0; i < 16; i++) m17_lsf[224+i] = (crc_cmp >> 15-i) & 1;
fprintf (stderr, "M17 Voice Stream Encoding: %ld Samples and %ld Bits Per Frame (3200 bps); ", nsam, nbit);
//TODO: Make this Ncurses + Transmit Button?
while (!exitflag) //while the software is running
{
//read some audio samples from mic and load them into an audio buffer
//NOTE: Reconfigured to use default 48k input, take 1 out of every 6 samples (Codec2 is 8k encoder)
if (opts->audio_in_type == 0) //pulse audio
{
for (i = 0; i < nsam; i++)
{
for (j = 0; j < 6; j++)
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
voice1[i] = sample; //only store the 6th sample
}
for (i = 0; i < nsam; i++)
{
for (j = 0; j < 6; j++)
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
voice2[i] = sample; //only store the 6th sample
}
}
else if (opts->audio_in_type == 1) //stdin
{
int result = 0;
for (i = 0; i < nsam; i++)
{
for (j = 0; j < 6; j++)
result = sf_read_short(opts->audio_in_file, &sample, 1);
voice1[i] = sample;
if (result == 0)
{
sf_close(opts->audio_in_file);
fprintf (stderr, "Connection to STDIN Disconnected.\n");
fprintf (stderr, "Closing DSD-FME.\n");
exitflag = 1;
break;
}
}
for (i = 0; i < nsam; i++)
{
for (j = 0; j < 6; j++)
result = sf_read_short(opts->audio_in_file, &sample, 1);
voice2[i] = sample;
if (result == 0)
{
sf_close(opts->audio_in_file);
fprintf (stderr, "Connection to STDIN Disconnected.\n");
fprintf (stderr, "Closing DSD-FME.\n");
exitflag = 1;
break;
}
}
}
else if (opts->audio_in_type == 5) //OSS, testing working
{
for (i = 0; i < nsam; i++)
{
for (j = 0; j < 6; j++)
read (opts->audio_in_fd, &sample, 2);
voice1[i] = sample;
}
for (i = 0; i < nsam; i++)
{
for (j = 0; j < 6; j++)
read (opts->audio_in_fd, &sample, 2);
voice2[i] = sample;
}
}
else if (opts->audio_in_type == 8) //TCP
{
int result = 0;
for (i = 0; i < nsam; i++)
{
for (j = 0; j < 6; j++)
result = sf_read_short(opts->tcp_file_in, &sample, 1);
voice1[i] = sample;
if (result == 0)
{
sf_close(opts->tcp_file_in);
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
fprintf (stderr, "Closing DSD-FME.\n");
exitflag = 1;
break;
}
}
for (i = 0; i < nsam; i++)
{
for (j = 0; j < 6; j++)
result = sf_read_short(opts->tcp_file_in, &sample, 1);
voice2[i] = sample;
if (result == 0)
{
sf_close(opts->tcp_file_in);
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
fprintf (stderr, "Closing DSD-FME.\n");
exitflag = 1;
break;
}
}
}
else if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
for (i = 0; i < nsam; i++)
{
for (j = 0; j < 6; j++)
if (get_rtlsdr_sample(&sample, opts, state) < 0)
cleanupAndExit(opts, state);
voice1[i] = sample;
}
for (i = 0; i < nsam; i++)
{
for (j = 0; j < 6; j++)
if (get_rtlsdr_sample(&sample, opts, state) < 0)
cleanupAndExit(opts, state);
voice2[i] = sample;
}
#endif
}
//convert out audio input into CODEC2 (3200bps) 8 byte data stream
uint8_t vc1_bytes[8]; memset (vc1_bytes, 0, sizeof(vc1_bytes));
uint8_t vc2_bytes[8]; memset (vc2_bytes, 0, sizeof(vc2_bytes));
#ifdef USE_CODEC2
codec2_encode(state->codec2_3200, vc1_bytes, voice1);
codec2_encode(state->codec2_3200, vc2_bytes, voice2);
#endif
//start assembling our completed frame
//STR frame sync pattern - 0xFF5D //-3, -3, -3, -3, +3, +3, -3, +3
uint8_t m17_str_fs[16] = {1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1}; UNUSED(m17_str_fs);
//Data/Voice Portion of Stream Data Link Layer w/ FSN
uint8_t m17_v1[148]; memset (m17_v1, 0, sizeof(m17_v1)); UNUSED(m17_v1);
//Data/Voice Portion of Stream Data Link Layer w/ FSN (after Convolutional Encode)
uint8_t m17_v1c[296]; memset (m17_v1c, 0, sizeof(m17_v1c)); UNUSED(m17_v1c);
//Data/Voice Portion of Stream Data Link Layer w/ FSN (after P2 Puncturing)
uint8_t m17_v1p[272]; memset (m17_v1p, 0, sizeof(m17_v1p)); UNUSED(m17_v1p);
//LSF Chunk + LICH CNT of Stream Data Link Layer
uint8_t m17_l1[48]; memset (m17_l1, 0, sizeof(m17_l1)); UNUSED(m17_l1);
//LSF Chunk + LICH CNT of Stream Data Link Layer (after Golay 24,12 Encoding)
uint8_t m17_l1g[96]; memset (m17_l1g, 0, sizeof(m17_l1g)); UNUSED(m17_l1g);
//Type 4c - Combined LSF Content Chuck and Voice/Data (96 + 272)
uint8_t m17_t4c[368]; memset (m17_t4c, 0, sizeof(m17_t4c)); UNUSED(m17_t4c);
//Type 4i - Interleaved Bits
uint8_t m17_t4i[368]; memset (m17_t4i, 0, sizeof(m17_t4i)); UNUSED(m17_t4i);
//Type 4s - Interleaved Bits with Scrambling Applied
uint8_t m17_t4s[368]; memset (m17_t4s, 0, sizeof(m17_t4s)); UNUSED(m17_t4s);
//OTA Symbol Convertion (best variable type for this? int16?)
uint8_t m17_sym[184]; memset (m17_sym, 0, sizeof(m17_sym)); UNUSED(m17_sym);
//insert the voice bytes into voice bits, and voice bits into v1 in their appropriate location
uint8_t v1_bits[64]; memset (v1_bits, 0, sizeof(v1_bits)); UNUSED(v1_bits);
uint8_t v2_bits[64]; memset (v2_bits, 0, sizeof(v2_bits)); UNUSED(v2_bits);
k = 0; x = 0;
for (j = 0; j < 8; j++)
{
for (i = 0; i < 8; i++)
{
v1_bits[k++] = (vc1_bytes[j] >> 7-i) & 1;
v2_bits[x++] = (vc2_bytes[j] >> 7-i) & 1;
}
}
for (i = 0; i < 64; i++)
{
m17_v1[i+16] = v1_bits[i];
m17_v1[i+16+64] = v2_bits[i];
}
//set end of tx bit on the exitflag (sig)
if (exitflag) eot = 1;
m17_v1[0] = (uint8_t)eot; //set as first bit of the stream
//set current frame number as bits 1-15 of the v1 stream
for (i = 0; i < 15; i++)
m17_v1[i+1] = ( (uint8_t)(fsn >> 14-i) ) &1;
fsn++; //increment the frame sequence number
//Use the convolutional encoder to encode the voice / data stream
simple_conv_encoder (m17_v1, m17_v1c, 144); //seems to be working now, or at least, the same bits before and after on the playback side
//use the P2 puncture to...puncture and collapse the voice / data stream
k = 0; x = 0;
for (i = 0; i < 25; i++)
{
m17_v1p[k++] = m17_v1c[x++];
m17_v1p[k++] = m17_v1c[x++];
m17_v1p[k++] = m17_v1c[x++];
m17_v1p[k++] = m17_v1c[x++];
m17_v1p[k++] = m17_v1c[x++];
m17_v1p[k++] = m17_v1c[x++];
if (k == 272) break; //quit early on last set of i when 272 k bits reached
m17_v1p[k++] = m17_v1c[x++];
if (k == 272) break; //quit early on last set of i when 272 k bits reached
m17_v1p[k++] = m17_v1c[x++];
if (k == 272) break; //quit early on last set of i when 272 k bits reached
m17_v1p[k++] = m17_v1c[x++];
if (k == 272) break; //quit early on last set of i when 272 k bits reached
m17_v1p[k++] = m17_v1c[x++];
m17_v1p[k++] = m17_v1c[x++];
x++;
}
//add punctured voice / data bits to the combined frame
for (i = 0; i < 272; i++)
m17_t4c[i+96] = m17_v1p[i];
//ignore the LSF/LICH reserved bits (unused currently)
//load up the lsf chunk for this cnt
for (i = 0; i < 40; i++)
lsf_chunk[lich_cnt][i] = m17_lsf[((lich_cnt)*40)+i];
//update lich_cnt in the current LSF chunk
lsf_chunk[lich_cnt][40] = (lich_cnt >> 2) & 1;
lsf_chunk[lich_cnt][41] = (lich_cnt >> 1) & 1;
lsf_chunk[lich_cnt][42] = (lich_cnt >> 0) & 1;
//This is not M17 standard (yet), but use the LICH reserved bits to signal dt
lsf_chunk[lich_cnt][46] = (lsf_dt >> 1) & 1;
lsf_chunk[lich_cnt][47] = (lsf_dt >> 0) & 1;
//use the last 3 bits of reserved to signal the can value
lsf_chunk[lich_cnt][43] = (lsf_cn >> 2) & 1;
lsf_chunk[lich_cnt][44] = (lsf_cn >> 1) & 1;
lsf_chunk[lich_cnt][45] = (lsf_cn >> 0) & 1;
//encode with golay 24,12 and load into m17_lig
Golay_24_12_encode (lsf_chunk[lich_cnt]+00, m17_l1g+00);
Golay_24_12_encode (lsf_chunk[lich_cnt]+12, m17_l1g+24);
Golay_24_12_encode (lsf_chunk[lich_cnt]+24, m17_l1g+48);
Golay_24_12_encode (lsf_chunk[lich_cnt]+36, m17_l1g+72);
//add lsf chunk to the combined frame
for (i = 0; i < 96; i++)
m17_t4c[i] = m17_l1g[i];
//interleave 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_t4i[x] = m17_t4c[i];
}
//scramble the frame
for (i = 0; i < 368; i++)
m17_t4s[i] = (m17_t4i[i] ^ m17_scramble[i]) & 1;
//-----------------------------------------
//decode stream with the M17STR_debug
fprintf (stderr, "\n M17 STR (ENCODER): ");
processM17STR_debug(opts, state, m17_t4s);
//load bits inti a dibit array plus the framesync bits
uint8_t output_dibits[192]; memset (output_dibits, 0, sizeof(output_dibits)); UNUSED(output_dibits);
for (i = 0; i < 8; i++)
output_dibits[i] = (m17_str_fs[i*2+0] << 1) + (m17_str_fs[i*2+1] << 0);
//load rest of combined frame
for (i = 0; i < 184; i++)
output_dibits[i+8] = (m17_t4s[i*2+0] << 1) + (m17_t4s[i*2+1] << 0);
//Start working on converting the bitsream to an audio stream
//convert to symbols @ 4800 bps
int output_symbols[192]; memset (output_symbols, 0, sizeof(output_symbols)); UNUSED(output_symbols);
for (i = 0; i < 192; i++)
output_symbols[i] = symbol_map[output_dibits[i]];
//debug output symbols
// fprintf (stderr, "\n sym:");
// for (i = 0; i < 192; i++)
// {
// if (i%24 == 0) fprintf (stderr, "\n");
// fprintf (stderr, " %d", output_symbols[i]);
// }
//TODO: symbols to audio
//end by incrementing lich_cnt, reset on 6
lich_cnt++;
if (lich_cnt == 6) lich_cnt = 0;
}
//free allocated memory
free(samp1);
free(samp2);
}