dsd-fme_18_05_2023/src/dmr_bs.c

749 lines
20 KiB
C

/*-------------------------------------------------------------------------------
* dmr_bs.c
* DMR BS Voice Handling and Data Gathering Routines - "DMR STEREO"
*
* LWVMOBILE
* 2022-12 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include "dmr_const.h"
//A subroutine for processing each TDMA frame individually to allow for
//processing voice and/or data on both BS slots (channels) simultaneously
void dmrBS (dsd_opts * opts, dsd_state * state)
{
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24];
char ambe_fr2[4][24];
char ambe_fr3[4][24];
//redundancy check (carrier signal loss event)
char redundancyA[36];
char redundancyB[36];
//memcpy of ambe_fr for late entry
char m1[4][24];
char m2[4][24];
char m3[4][24];
const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
char EmbeddedSignalling[16];
uint8_t emb_ok = 0;
uint8_t tact_okay = 0;
uint8_t cach_err = 0;
uint8_t internalslot;
uint8_t vc1;
uint8_t vc2;
//assign as nonsensical numbers
uint8_t cc = 25;
uint8_t power = 9; //power and pre-emption indicator
uint8_t lcss = 9;
//would be ideal to grab all dibits and break them into bits to pass to new data handler?
uint8_t dummy_bits[196];
memset (dummy_bits, 0, sizeof(dummy_bits));
//add time to mirror printFrameSync
time_t now;
char * getTime(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
//Init slot lights
sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 ");
//Init the color code status
state->color_code_ok = 0;
vc1 = 2;
vc2 = 2;
short int loop = 1;
short int skipcount = 0;
//cach
char cachdata[25];
int cachInterleave[24] =
{0, 7, 8, 9, 1, 10,
11, 12, 2, 13, 14,
15, 3, 16, 4, 17, 18,
19, 5, 20, 21, 22, 6, 23
};
//cach tact bits
uint8_t tact_bits[7];
//Run Loop while the getting is good
while (loop == 1) {
internalslot = -1; //reset here so we know if this value is being set properly
for(i = 0; i < 12; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i] = dibit;
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
}
for (i = 0; i < 7; i++)
{
tact_bits[i] = cachdata[i];
}
//disabled for dmr_cach testing
tact_okay = 0;
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
if (tact_okay != 1) goto END; //when going to end for bad tact or emb, consider using getdibit or skipdibit to 'stash' dibits and get better return on next sync pattern? in data particularly
internalslot = state->currentslot = tact_bits[1];
//Setup for first AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//First AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+12] = dibit;
redundancyA[i] = dibit;
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//check for repetitive data if caught in a 'no carrier' loop? Just picking random values.
//this will test for no carrier (input signal) and return us to no sync state if necessary
if (redundancyA[16] == redundancyB[16] && redundancyA[27] == redundancyB[27] &&
redundancyA[01] == redundancyB[01] && redundancyA[32] == redundancyB[32] &&
redundancyA[03] == redundancyB[03] && redundancyA[33] == redundancyB[33] &&
redundancyA[13] == redundancyB[13] && redundancyA[07] == redundancyB[07] )
{
goto END;
}
//end redundancy test, set B to A
memcpy (redundancyB, redundancyA, sizeof (redundancyA));
//Setup for Second AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+48] = dibit;
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//signaling data or sync
for(i = 0; i < 24; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+66] = dibit;
sync[i] = (dibit | 1) + 48; //how does this work again?
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
syncdata[(2*i)+1] = (1 & dibit); // bit 0
//embedded link control
if(internalslot == 0 && vc1 > 1 && vc1 < 7) //grab on vc1 values 2-5 B C D and E (F for Single/RC)
{
state->dmr_embedded_signalling[internalslot][vc1-1][i*2] = (1 & (dibit >> 1)); // bit 1
state->dmr_embedded_signalling[internalslot][vc1-1][i*2+1] = (1 & dibit); // bit 0
}
if(internalslot == 1 && vc2 > 1 && vc2 < 7) //grab on vc2 values 2-5 B C D and E (F for Single/RC)
{
state->dmr_embedded_signalling[internalslot][vc2-1][i*2] = (1 & (dibit >> 1)); // bit 1
state->dmr_embedded_signalling[internalslot][vc2-1][i*2+1] = (1 & dibit); // bit 0
}
}
sync[24] = 0;
for(i = 0; i < 8; i++) EmbeddedSignalling[i] = syncdata[i];
for(i = 0; i < 8; i++) EmbeddedSignalling[i + 8] = syncdata[i + 40];
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+90] = dibit;
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//Setup for Third AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Third AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+108] = dibit;
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//reset vc counters to 1 if new voice sync frame on each slot
if ( strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
{
if (internalslot == 0) vc1 = 1;
if (internalslot == 1) vc2 = 1;
}
//check for sync pattern here after collected the rest of the payload, decide what to do with it
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
{
fprintf (stderr,"%s ", getTime());
if (internalslot == 0)
{
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR ");
}
else fprintf (stderr,"Sync: -DMR ");
vc1 = 1;
//close MBEout file - slot 1
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
}
if (internalslot == 1)
{
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR ");
}
else fprintf (stderr,"Sync: -DMR ");
vc2 = 1;
//close MBEout file - slot 2
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
}
dmr_data_sync (opts, state);
skipcount++;
goto SKIP;
}
//only play voice on no data sync
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0) //we already have a tact ecc check, so we won't get here without that, see if there is any other eccs we can run just to make sure
{
//check the embedded signalling, if bad at this point, we probably aren't quite in sync
if(QR_16_7_6_decode(EmbeddedSignalling)) emb_ok = 1;
else emb_ok = 0;
//disable the goto END; if this causes more problems than fixing on late entry dual voices i.e., lots of forced resyncs
if ( (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) && emb_ok == 0) goto END; //fprintf (stderr, "EMB BAD? ");
else if (emb_ok == 1)
{
cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
power = EmbeddedSignalling[4];
lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
}
skipcount = 0; //reset skip count if processing voice frames
fprintf (stderr,"%s ", getTime());
//simplifying things
char polarity[3];
char light[18];
uint8_t vc;
if (internalslot == 0)
{
state->dmrburstL = 16;
vc = vc1;
sprintf (light, "%s", " [SLOT1] slot2 ");
//open MBEout file - slot 1
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
}
else
{
state->dmrburstR = 16;
vc = vc2;
sprintf (light, "%s", " slot1 [SLOT2] ");
//open MBEout file - slot 2
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state);
}
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
else sprintf (polarity, "%s", "-");
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC%d ", polarity, light, state->dmr_color_code, vc);
if (internalslot == 0 && vc1 == 6)
{
//process embedded link control
fprintf (stderr, "\n");
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
//check the single burst/reverse channel opportunity
dmr_sbrc (opts, state, power);
}
if (internalslot == 1 && vc2 == 6)
{
//process embedded link control
fprintf (stderr, "\n");
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
//check the single burst/reverse channel opportunity
dmr_sbrc (opts, state, power);
}
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
//copy ambe_fr frames first, running process mbe will correct them,
//but this also leads to issues extracting good le mi values when
//we go to do correction on them there too
memcpy (m1, ambe_fr, sizeof(m1));
memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3));
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
cach_err = dmr_cach (opts, state, cachdata);
fprintf (stderr, "\n");
// run alg refresh after vc6 ambe processing
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
if (internalslot == 1 && vc2 == 6) dmr_alg_refresh (opts, state);
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
//increment the vc counters
if (internalslot == 0) vc1++;
if (internalslot == 1) vc2++;
//update cc amd vc sync time for trunking purposes (particularly Con+)
if (opts->p25_is_tuned == 1)
{
state->last_vc_sync_time = time(NULL);
state->last_cc_sync_time = time(NULL);
}
//'DSP' output to file
if (opts->use_dsp_output == 1)
{
FILE * pFile; //file pointer
pFile = fopen (opts->dsp_out_file, "a");
fprintf (pFile, "\n%d 98 ", internalslot+1); //'98' is CACH designation value
for (i = 0; i < 6; i++) //3 byte CACH
{
int cach_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
fprintf (pFile, "%X", cach_byte);
}
fprintf (pFile, "\n%d 10 ", internalslot+1); //0x10 for voice burst
for (i = 6; i < 72; i++) //33 bytes, no CACH
{
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
fprintf (pFile, "%X", dsp_byte);
}
fclose (pFile);
}
//reset err checks
cach_err = 1;
tact_okay = 0;
emb_ok = 0;
//reset emb components
cc = 25;
power = 9;
lcss = 9;
//Extra safeguards to break loop
// if ( (vc1 > 7 && vc2 > 7) ) goto END;
if ( (vc1 > 14 || vc2 > 14) ) goto END;
}
SKIP:
if (skipcount > 2) //after 2 consecutive data frames, drop back to getFrameSync and process with dmr_data_sync
{
//set tests to all good so we don't get a bogus/redundant voice error
cach_err = 0;
tact_okay = 1;
emb_ok = 1;
goto END;
}
//since we are in a while loop, run ncursesPrinter here.
if (opts->use_ncurses_terminal == 1)
{
ncursesPrinter(opts, state);
}
} // while loop
END:
state->dmr_stereo = 0;
state->errs = 0;
state->errs2 = 0;
state->errs2R = 0;
state->errs2 = 0;
//close any open MBEout files
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
//if we have a tact or emb err, then produce sync pattern/err message
if (tact_okay != 1 || emb_ok != 1)
{
fprintf (stderr,"%s ", getTime());
fprintf (stderr,"Sync: DMR ");
fprintf (stderr, "%s", KRED);
fprintf (stderr, "| VOICE CACH/EMB ERR");
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
//run refresh if either slot had an active MI in it.
if (state->payload_algid != 0)
{
state->currentslot = 0;
dmr_alg_refresh (opts, state);
}
if (state->payload_algidR != 0)
{
state->currentslot = 1;
dmr_alg_refresh (opts, state);
}
//failsafe to reset all data header and blocks when bad tact or emb
dmr_reset_blocks (opts, state);
}
}
//Process buffered half frame and 2nd half and then jump to full BS decoding
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
{
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24];
char ambe_fr2[4][24];
char ambe_fr3[4][24];
//memcpy of ambe_fr for late entry
char m1[4][24];
char m2[4][24];
char m3[4][24];
const int *w, *x, *y, *z;
char sync[25];
uint8_t tact_okay = 0;
uint8_t cach_err = 0;
uint8_t sync_okay = 1;
uint8_t internalslot;
char cachdata[25];
int cachInterleave[24] =
{0, 7, 8, 9, 1, 10,
11, 12, 2, 13, 14,
15, 3, 16, 4, 17, 18,
19, 5, 20, 21, 22, 6, 23
};
//add time to mirror printFrameSync
time_t now;
char * getTime(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
//payload buffer
//CACH + First Half Payload + Sync = 12 + 54 + 24
dibit_p = state->dmr_payload_p - 90;
for (i = 0; i < 90; i++) //90
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
state->dmr_stereo_payload[i] = dibit;
}
for(i = 0; i < 12; i++)
{
dibit = state->dmr_stereo_payload[i];
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
}
//cach tact bits
uint8_t tact_bits[7];
for (i = 0; i < 7; i++)
{
tact_bits[i] = cachdata[i];
}
//decode and correct tact and compare
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
if (tact_okay != 1) goto END;
internalslot = state->currentslot = tact_bits[1];
//Setup for first AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//First AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = state->dmr_stereo_payload[i+12];
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//Setup for Second AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = state->dmr_stereo_payload[i+48];
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
// signaling data or sync, just redo it
for(i = 0; i < 24; i++)
{
dibit = state->dmr_stereo_payload[i+66];
sync[i] = (dibit | 1) + 48;
}
sync[24] = 0;
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
{
sync_okay = 0;
goto END;
}
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+90] = dibit;
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//Setup for Third AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Third AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+108] = dibit;
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//'DSP' output to file
if (opts->use_dsp_output == 1)
{
FILE * pFile; //file pointer
pFile = fopen (opts->dsp_out_file, "a");
fprintf (pFile, "\n%d 98 ", internalslot+1); //'98' is CACH designation value
for (i = 0; i < 6; i++) //3 byte CACH
{
int cach_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
fprintf (pFile, "%X", cach_byte);
}
fprintf (pFile, "\n%d 10 ", internalslot+1); //0x10 for "voice burst"
for (i = 6; i < 72; i++) //33 bytes, no CACH
{
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
fprintf (pFile, "%X", dsp_byte);
}
fclose (pFile);
}
fprintf (stderr,"%s ", getTime());
char polarity[3];
char light[18];
if (state->currentslot == 0)
{
sprintf (light, "%s", " [SLOT1] slot2 ");
//open MBEout file - slot 1
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
}
else
{
sprintf (light, "%s", " slot1 [SLOT2] ");
//open MBEout file - slot 2
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state);
}
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
else sprintf (polarity, "%s", "-");
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC1*", polarity, light, state->dmr_color_code);
dmr_alg_reset (opts, state);
//copy ambe_fr frames first, running process mbe will correct them,
//but this also leads to issues extracting good le mi values when
//we go to do correction on them there too
memcpy (m1, ambe_fr, sizeof(m1));
memcpy (m2, ambe_fr2, sizeof(m2));
memcpy (m3, ambe_fr3, sizeof(m3));
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
//collect the mi fragment
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
cach_err = dmr_cach (opts, state, cachdata);
fprintf (stderr, "\n");
//update voice sync time for trunking purposes (particularly Con+)
if (opts->p25_is_tuned == 1) state->last_vc_sync_time = time(NULL);
dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode
END:
//if we have a tact err, then produce sync pattern/err message
if (tact_okay != 1 || sync_okay != 1)
{
fprintf (stderr,"%s ", getTime());
fprintf (stderr,"Sync: DMR ");
fprintf (stderr, "%s", KRED);
fprintf (stderr, "| VOICE CACH/SYNC ERR");
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
//run refresh if either slot had an active MI in it.
if (state->payload_algid != 0)
{
state->currentslot = 0;
dmr_alg_refresh (opts, state);
}
if (state->payload_algidR != 0)
{
state->currentslot = 1;
dmr_alg_refresh (opts, state);
}
//failsafe to reset all data header and blocks when bad tact or emb
dmr_reset_blocks (opts, state);
}
}