mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-14 17:57:28 -05:00
Phase 2 Audio Decoding
Phase 2 Audio Decoding Implemented XDMA decoder class implemented and set to default (-ft) --decoder class includes P25 1, 2, DMR BS/MS, and X2-TDMA -- -fr -T option reworked, -T now for Per Call, use -ft (default) Force Privacy Key --force privacy key use over system FID and SVC options. Per call now works on XDMA decoding and NXDN decoding Limited Phase 1 TSBK and Phase 2 LCCH handling (for Network Status Broadcast) --Need to get WACN, SYSID, and CC values for Phase 2 descrambling Fix most of the compiler warnings
This commit is contained in:
@@ -817,8 +817,8 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
|
||||
|
||||
sprintf(state->slot1light, "");
|
||||
sprintf(state->slot2light, "");
|
||||
sprintf(state->slot1light, "%s", "");
|
||||
sprintf(state->slot2light, "%s", "");
|
||||
|
||||
//process data
|
||||
state->dmr_stereo = 1;
|
||||
|
||||
@@ -72,7 +72,8 @@ int Pr[256] = {
|
||||
};
|
||||
|
||||
|
||||
if (state->currentslot == 0 && state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
|
||||
if ( (state->currentslot == 0 && state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||
(state->currentslot == 0 && state->K > 0 && state->M == 1) )
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf(stderr, " PrK %lld", state->K);
|
||||
@@ -93,7 +94,8 @@ if (state->currentslot == 0 && state->K > 0 && state->dmr_so & 0x40 && state->pa
|
||||
}
|
||||
}
|
||||
|
||||
if (state->currentslot == 1 && state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10)
|
||||
if ( (state->currentslot == 1 && state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
|
||||
(state->currentslot == 1 && state->K > 0 && state->M == 1) )
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf(stderr, " PrK %lld", state->K);
|
||||
@@ -114,10 +116,11 @@ if (state->currentslot == 1 && state->K > 0 && state->dmr_soR & 0x40 && state->p
|
||||
}
|
||||
}
|
||||
|
||||
if (state->currentslot == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
|
||||
if ( (state->currentslot == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
|
||||
(state->currentslot == 0 && state->K1 > 0 && state->M == 1) )
|
||||
{
|
||||
|
||||
int pos = 0;
|
||||
int pos = 0;
|
||||
|
||||
unsigned long long int k1 = state->K1;
|
||||
unsigned long long int k2 = state->K2;
|
||||
@@ -186,7 +189,8 @@ if (state->currentslot == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->p
|
||||
|
||||
}
|
||||
|
||||
if (state->currentslot == 1 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68)
|
||||
if ( (state->currentslot == 1 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
|
||||
(state->currentslot == 1 && state->K1 > 0 && state->M == 1) )
|
||||
{
|
||||
|
||||
int pos = 0;
|
||||
|
||||
@@ -484,15 +484,14 @@ void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint
|
||||
}
|
||||
|
||||
|
||||
//set source here so we can reference it in our text dump
|
||||
state->dmr_lrrp_source[state->currentslot] = (DmrDataByte[5] <<16 ) + (DmrDataByte[6] << 8) + DmrDataByte[7];
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
//end collecting data header info
|
||||
|
||||
//look at see which type of data service ap we have, and then assign src and dst later for lrrp text dump, etc.
|
||||
fprintf (stderr, "%s ", KMAG);
|
||||
if (state->data_header_sap[state->currentslot] == 0x4 && state->data_p_head[state->currentslot] == 0) //4 is IP data
|
||||
if (state->data_header_sap[state->currentslot] == 0x4 && state->data_p_head[state->currentslot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n IP4 Data - Source: ");
|
||||
fprintf (stderr, "\n IP Data - Source: ");
|
||||
//fprintf (stderr, " %d.%d.%d.%d", (DmrDataByte[0] & 0x3F), DmrDataByte[1], DmrDataByte[2], DmrDataByte[3]); //not sure if these are right or not
|
||||
fprintf (stderr, "[%d]", (DmrDataByte[5] <<16 ) + (DmrDataByte[6] << 8) + DmrDataByte[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
@@ -507,6 +506,90 @@ void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint
|
||||
( (DmrDataByte[2] <<16 ) + (DmrDataByte[3] <<8 ) + DmrDataByte[4]) );
|
||||
}
|
||||
|
||||
state->dmr_lrrp_source[state->currentslot] = (DmrDataByte[5] <<16 ) + (DmrDataByte[6] << 8) + DmrDataByte[7];
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[state->currentslot] == 0x0 && state->data_p_head[state->currentslot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Unified Data Transport - Source: ");
|
||||
//fprintf (stderr, " %d.%d.%d.%d", (DmrDataByte[0] & 0x3F), DmrDataByte[1], DmrDataByte[2], DmrDataByte[3]); //not sure if these are right or not
|
||||
fprintf (stderr, "[%d]", (DmrDataByte[5] <<16 ) + (DmrDataByte[6] << 8) + DmrDataByte[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
//fprintf (stderr, " %d.%d.%d.%d", (DmrDataByte[4] & 0x3F), DmrDataByte[5], DmrDataByte[6], DmrDataByte[7]); //not sure if these are right or not
|
||||
fprintf (stderr, "[%d]", (DmrDataByte[2] <<16 ) + (DmrDataByte[3] <<8 ) + DmrDataByte[4] );
|
||||
|
||||
//part below should probably be inside of above if condition
|
||||
if (1 == 1) //already setting by current slot, no need for checking first
|
||||
{
|
||||
sprintf ( state->dmr_lrrp[state->currentslot][1], "SRC [%d] DST [%d] ",
|
||||
( (DmrDataByte[5] <<16 ) + (DmrDataByte[6] << 8) + DmrDataByte[7]),
|
||||
( (DmrDataByte[2] <<16 ) + (DmrDataByte[3] <<8 ) + DmrDataByte[4]) );
|
||||
}
|
||||
|
||||
state->dmr_lrrp_source[state->currentslot] = (DmrDataByte[5] <<16 ) + (DmrDataByte[6] << 8) + DmrDataByte[7];
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[state->currentslot] == 0x9 && state->data_p_head[state->currentslot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Proprietary Packet Data - Source: ");
|
||||
//fprintf (stderr, " %d.%d.%d.%d", (DmrDataByte[0] & 0x3F), DmrDataByte[1], DmrDataByte[2], DmrDataByte[3]); //not sure if these are right or not
|
||||
fprintf (stderr, "[%d]", (DmrDataByte[5] <<16 ) + (DmrDataByte[6] << 8) + DmrDataByte[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
//fprintf (stderr, " %d.%d.%d.%d", (DmrDataByte[4] & 0x3F), DmrDataByte[5], DmrDataByte[6], DmrDataByte[7]); //not sure if these are right or not
|
||||
fprintf (stderr, "[%d]", (DmrDataByte[2] <<16 ) + (DmrDataByte[3] <<8 ) + DmrDataByte[4] );
|
||||
|
||||
//part below should probably be inside of above if condition
|
||||
if (1 == 1) //already setting by current slot, no need for checking first
|
||||
{
|
||||
sprintf ( state->dmr_lrrp[state->currentslot][1], "SRC [%d] DST [%d] ",
|
||||
( (DmrDataByte[5] <<16 ) + (DmrDataByte[6] << 8) + DmrDataByte[7]),
|
||||
( (DmrDataByte[2] <<16 ) + (DmrDataByte[3] <<8 ) + DmrDataByte[4]) );
|
||||
}
|
||||
|
||||
state->dmr_lrrp_source[state->currentslot] = (DmrDataByte[5] <<16 ) + (DmrDataByte[6] << 8) + DmrDataByte[7];
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[state->currentslot] == 0x2 && state->data_p_head[state->currentslot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n TCP/IP Header Compression Data - Source: ");
|
||||
//fprintf (stderr, " %d.%d.%d.%d", (DmrDataByte[0] & 0x3F), DmrDataByte[1], DmrDataByte[2], DmrDataByte[3]); //not sure if these are right or not
|
||||
fprintf (stderr, "[%d]", DmrDataByte[3]);
|
||||
fprintf (stderr, " Destination: ");
|
||||
//fprintf (stderr, " %d.%d.%d.%d", (DmrDataByte[4] & 0x3F), DmrDataByte[5], DmrDataByte[6], DmrDataByte[7]); //not sure if these are right or not
|
||||
fprintf (stderr, "[%d]", DmrDataByte[2]);
|
||||
|
||||
//part below should probably be inside of above if condition
|
||||
if (1 == 1) //already setting by current slot, no need for checking first
|
||||
{
|
||||
sprintf ( state->dmr_lrrp[state->currentslot][1], "SRC [%d] DST [%d] ",
|
||||
DmrDataByte[3], DmrDataByte[2] );
|
||||
}
|
||||
|
||||
state->dmr_lrrp_source[state->currentslot] = DmrDataByte[3];
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[state->currentslot] == 0x3 && state->data_p_head[state->currentslot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n UDP/IP Header Compression Data - Source: ");
|
||||
//fprintf (stderr, " %d.%d.%d.%d", (DmrDataByte[0] & 0x3F), DmrDataByte[1], DmrDataByte[2], DmrDataByte[3]); //not sure if these are right or not
|
||||
fprintf (stderr, "[%d]", DmrDataByte[3]);
|
||||
fprintf (stderr, " Destination: ");
|
||||
//fprintf (stderr, " %d.%d.%d.%d", (DmrDataByte[4] & 0x3F), DmrDataByte[5], DmrDataByte[6], DmrDataByte[7]); //not sure if these are right or not
|
||||
fprintf (stderr, "[%d]", DmrDataByte[2]);
|
||||
|
||||
//part below should probably be inside of above if condition
|
||||
if (1 == 1) //already setting by current slot, no need for checking first
|
||||
{
|
||||
sprintf ( state->dmr_lrrp[state->currentslot][1], "SRC [%d] DST [%d] ",
|
||||
DmrDataByte[3], DmrDataByte[2] );
|
||||
}
|
||||
|
||||
state->dmr_lrrp_source[state->currentslot] = DmrDataByte[3];
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[state->currentslot] == 0x5 && state->data_p_head[state->currentslot] == 0) //5 is ARP Address Resolution Protocol
|
||||
@@ -2404,7 +2487,7 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state)
|
||||
//Embedded Alias
|
||||
if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode > 0x03 && TSVoiceSupFrame->FullLC.FullLinkControlOpcode < 0x08)
|
||||
{
|
||||
sprintf (state->dmr_callsign[state->currentslot][TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3], ""); //blank here so it doesn't grow out of control?
|
||||
sprintf (state->dmr_callsign[state->currentslot][TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3], "%s", ""); //blank here so it doesn't grow out of control?
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
//full range of alphanumerical characters?
|
||||
@@ -2431,7 +2514,7 @@ void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state)
|
||||
|
||||
if ( TSVoiceSupFrame->FullLC.FullLinkControlOpcode == 0x08 && opts->payload == 1) //Embedded GPS
|
||||
{
|
||||
sprintf (state->dmr_callsign[state->currentslot][TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3], ""); //blank here so it doesn't grow out of control?
|
||||
sprintf (state->dmr_callsign[state->currentslot][TSVoiceSupFrame->FullLC.FullLinkControlOpcode - 3], "%s", ""); //blank here so it doesn't grow out of control?
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
//full range of alphanumerical characters?
|
||||
|
||||
@@ -64,8 +64,8 @@ void openPulseOutput(dsd_opts * opts)
|
||||
|
||||
if (opts->dmr_stereo == 1)
|
||||
{
|
||||
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME1", PA_STREAM_PLAYBACK, NULL, "DMR SLOT 1", &tt, left, NULL, NULL);
|
||||
opts->pulse_digi_dev_outR = pa_simple_new(NULL, "DSD-FME2", PA_STREAM_PLAYBACK, NULL, "DMR SLOT 2", &tt, right, NULL, NULL);
|
||||
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME1", PA_STREAM_PLAYBACK, NULL, "XDMA SLOT 1", &tt, left, NULL, NULL);
|
||||
opts->pulse_digi_dev_outR = pa_simple_new(NULL, "DSD-FME2", PA_STREAM_PLAYBACK, NULL, "XDMA SLOT 2", &tt, right, NULL, NULL);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -92,6 +92,11 @@ processAudio (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
// detect max level
|
||||
max = 0;
|
||||
//attempt to reduce crackle by overriding the max value when not using pulse input
|
||||
if (opts->audio_in_type != 0)
|
||||
{
|
||||
max = 3000;
|
||||
}
|
||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
@@ -233,6 +238,11 @@ processAudioR (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
// detect max level
|
||||
max = 0;
|
||||
//attempt to reduce crackle by overriding the max value when not using pulse input
|
||||
if (opts->audio_in_type != 0)
|
||||
{
|
||||
max = 3000;
|
||||
}
|
||||
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
|
||||
204
src/dsd_file.c
204
src/dsd_file.c
@@ -17,6 +17,34 @@
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
time_t nowF;
|
||||
char * getTimeF(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;
|
||||
}
|
||||
|
||||
char * getDateF(void) {
|
||||
char datename[120]; //bug in 32-bit Ubuntu when using date in filename, date is garbage text
|
||||
char * curr2;
|
||||
struct tm * to;
|
||||
time_t t;
|
||||
t = time(NULL);
|
||||
to = localtime( & t);
|
||||
strftime(datename, sizeof(datename), "%Y-%m-%d", to);
|
||||
curr2 = strtok(datename, " ");
|
||||
return curr2;
|
||||
}
|
||||
|
||||
void
|
||||
saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
||||
{
|
||||
@@ -72,7 +100,7 @@ saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||
//fprintf(stderr, "\n AMBE ");
|
||||
}
|
||||
//for (i = 0; i < 6; i++)
|
||||
for (i = 0; i < 7; i++) //using 7 seems to break older amb files where it was 6, need to test 7 on 7 some more
|
||||
for (i = 0; i < 6; i++) //using 7 seems to break older amb files where it was 6, need to test 7 on 7 some more
|
||||
{
|
||||
b = 0;
|
||||
for (j = 0; j < 8; j++)
|
||||
@@ -181,7 +209,7 @@ PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stere
|
||||
b = ambe_d[48];
|
||||
if (opts->dmr_stereo == 1) //need to fix the printouts again
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -250,7 +278,7 @@ readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
//for (i = 0; i < 6; i++)
|
||||
for (i = 0; i < 7; i++) //breaks backwards compatablilty with 6 files
|
||||
for (i = 0; i < 6; i++) //breaks backwards compatablilty with 6 files
|
||||
{
|
||||
b = fgetc (opts->mbe_in_f);
|
||||
if (feof (opts->mbe_in_f))
|
||||
@@ -321,85 +349,105 @@ openMbeInFile (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||
//temporarily disabled until the compiler warnings can be fixed, use symbol capture or per call instead!
|
||||
//probably the easiest thing is to just have this run fclose, and have name assignment on open instead.
|
||||
// void
|
||||
// closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||
// {
|
||||
//
|
||||
// char shell[255], newfilename[64], ext[5], datestr[32], new_path[1024];
|
||||
// char tgid[17];
|
||||
// int sum, i, j;
|
||||
// int talkgroup;
|
||||
// struct tm timep;
|
||||
// int result;
|
||||
//
|
||||
// if (opts->mbe_out_f != NULL)
|
||||
// {
|
||||
// if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
||||
// {
|
||||
// sprintf (ext, ".imb");
|
||||
// //strptime (opts->mbe_out_file, "%s.imb", &timep);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// sprintf (ext, ".amb");
|
||||
// //strptime (opts->mbe_out_file, "%s.amb", &timep);
|
||||
// }
|
||||
//
|
||||
// if (state->tgcount > 0)
|
||||
// {
|
||||
// for (i = 0; i < 16; i++)
|
||||
// {
|
||||
// sum = 0;
|
||||
// for (j = 0; j < state->tgcount; j++)
|
||||
// {
|
||||
// sum = sum + state->tg[j][i] - 48;
|
||||
// }
|
||||
// tgid[i] = (char) (((float) sum / (float) state->tgcount) + 48.5);
|
||||
// }
|
||||
// tgid[16] = 0;
|
||||
// talkgroup = (int) strtol (tgid, NULL, 2);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// talkgroup = 0;
|
||||
// }
|
||||
//
|
||||
// fflush (opts->mbe_out_f);
|
||||
// fclose (opts->mbe_out_f);
|
||||
// opts->mbe_out_f = NULL;
|
||||
// strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep);
|
||||
//
|
||||
// sprintf (newfilename, "%s%s", datestr, ext); //this one
|
||||
//
|
||||
// if (state->lastsynctype == 0 || state->lastsynctype == 1)
|
||||
// {
|
||||
// sprintf (newfilename, "%s-nac%X-tg%i%s", datestr, state->nac, talkgroup, ext);
|
||||
// }
|
||||
//
|
||||
// //sprintf (new_path, "%s%s", opts->mbe_out_dir, newfilename);
|
||||
// #ifdef _WIN32
|
||||
// //sprintf (shell, "move %s %s", opts->mbe_out_path, new_path);
|
||||
// #else
|
||||
// //sprintf (shell, "mv %s %s", opts->mbe_out_path, new_path);
|
||||
// #endif
|
||||
// //result = system (shell);
|
||||
//
|
||||
// state->tgcount = 0;
|
||||
// for (i = 0; i < 25; i++)
|
||||
// {
|
||||
// for (j = 0; j < 16; j++)
|
||||
// {
|
||||
// state->tg[i][j] = 48;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
//much simpler version
|
||||
void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
char shell[255], newfilename[64], ext[5], datestr[32], new_path[1024];
|
||||
char tgid[17];
|
||||
int sum, i, j;
|
||||
int talkgroup;
|
||||
struct tm timep;
|
||||
int result;
|
||||
|
||||
if (opts->mbe_out_f != NULL)
|
||||
if (opts->mbe_out == 1)
|
||||
{
|
||||
if (opts->mbe_out_f != NULL)
|
||||
{
|
||||
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
||||
{
|
||||
sprintf (ext, ".imb");
|
||||
strptime (opts->mbe_out_file, "%s.imb", &timep);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (ext, ".amb");
|
||||
strptime (opts->mbe_out_file, "%s.amb", &timep);
|
||||
}
|
||||
|
||||
if (state->tgcount > 0)
|
||||
{
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
sum = 0;
|
||||
for (j = 0; j < state->tgcount; j++)
|
||||
{
|
||||
sum = sum + state->tg[j][i] - 48;
|
||||
}
|
||||
tgid[i] = (char) (((float) sum / (float) state->tgcount) + 48.5);
|
||||
}
|
||||
tgid[16] = 0;
|
||||
talkgroup = (int) strtol (tgid, NULL, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
talkgroup = 0;
|
||||
}
|
||||
|
||||
fflush (opts->mbe_out_f);
|
||||
fclose (opts->mbe_out_f);
|
||||
opts->mbe_out_f = NULL;
|
||||
strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep);
|
||||
|
||||
sprintf (newfilename, "%s%s", datestr, ext); //this one
|
||||
|
||||
if (state->lastsynctype == 0 || state->lastsynctype == 1)
|
||||
{
|
||||
sprintf (newfilename, "%s-nac%X-tg%i%s", datestr, state->nac, talkgroup, ext);
|
||||
}
|
||||
|
||||
sprintf (new_path, "%s%s", opts->mbe_out_dir, newfilename);
|
||||
#ifdef _WIN32
|
||||
sprintf (shell, "move %s %s", opts->mbe_out_path, new_path);
|
||||
#else
|
||||
sprintf (shell, "mv %s %s", opts->mbe_out_path, new_path);
|
||||
#endif
|
||||
result = system (shell);
|
||||
|
||||
state->tgcount = 0;
|
||||
for (i = 0; i < 25; i++)
|
||||
{
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
state->tg[i][j] = 48;
|
||||
}
|
||||
}
|
||||
opts->mbe_out = 0;
|
||||
fprintf (stderr, "\nClosing MBE out file.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
struct timeval tv;
|
||||
//struct timeval tv;
|
||||
int i, j;
|
||||
char ext[5];
|
||||
|
||||
@@ -423,18 +471,22 @@ openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
state->tgcount = 0;
|
||||
|
||||
gettimeofday (&tv, NULL);
|
||||
sprintf (opts->mbe_out_file, "%i%s", (int) tv.tv_sec, ext);
|
||||
//gettimeofday (&tv, NULL);
|
||||
//rewrite below line to take getTime function instead?
|
||||
// sprintf (opts->mbe_out_file, "%i%s", (int) tv.tv_sec, ext);
|
||||
|
||||
sprintf (opts->mbe_out_file, "%s %s%s", getDateF(), getTimeF(), ext);
|
||||
|
||||
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_file);
|
||||
|
||||
opts->mbe_out_f = fopen (opts->mbe_out_path, "w");
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
fprintf (stderr,"Error, couldn't open %s\n", opts->mbe_out_path);
|
||||
}
|
||||
{
|
||||
fprintf (stderr,"Error, couldn't open %s\n", opts->mbe_out_path);
|
||||
}
|
||||
else opts->mbe_out = 1;
|
||||
|
||||
// write magic
|
||||
//
|
||||
fprintf (opts->mbe_out_f, "%s", ext);
|
||||
|
||||
fflush (opts->mbe_out_f);
|
||||
|
||||
@@ -209,7 +209,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->dmr_stereo == 0 && state->synctype == 32)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, "Please use the -T option to handle MS Mode with DMR Stereo TDMA Handling\n");
|
||||
fprintf (stderr, "Please use XDMA decoding class to decode MS/Simplex \n");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
if (opts->dmr_stereo == 1)
|
||||
@@ -229,7 +229,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
closeMbeOutFile (opts, state);
|
||||
state->err_str[0] = 0;
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, "Please use the -T option to handle MS Mode with DMR Stereo TDMA Handling\n");
|
||||
fprintf (stderr, "Please use XDMA decoding class to decode MS/Simplex \n");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
if (opts->dmr_stereo == 1)
|
||||
@@ -397,7 +397,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
bch_code[index_bch_code] = v;
|
||||
index_bch_code++;
|
||||
}
|
||||
state->nac = strtol (nac, NULL, 2);
|
||||
//this one setting bogus nac data
|
||||
// state->nac = strtol (nac, NULL, 2);
|
||||
|
||||
// Read the DUID, 4 bits
|
||||
for (i = 0; i < 2; i++)
|
||||
@@ -445,6 +446,10 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
if (new_nac != state->nac) {
|
||||
// NAC fixed by error correction
|
||||
state->nac = new_nac;
|
||||
if (state->p2_hardset == 0)
|
||||
{
|
||||
state->p2_cc = new_nac;
|
||||
}
|
||||
state->debug_header_errors++;
|
||||
}
|
||||
if (strcmp(new_duid, duid) != 0) {
|
||||
@@ -478,6 +483,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
state->lastp25type = 2;
|
||||
state->dmrburstL = 25;
|
||||
state->currentslot = 0;
|
||||
sprintf (state->fsubtype, " HDU ");
|
||||
processHDU (opts, state);
|
||||
}
|
||||
@@ -497,12 +504,13 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
}
|
||||
state->lastp25type = 1;
|
||||
state->dmrburstL = 26;
|
||||
state->currentslot = 0;
|
||||
sprintf (state->fsubtype, " LDU1 ");
|
||||
state->numtdulc = 0;
|
||||
if (state->payload_algid == 0x81)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
//LFSRP (state); //HERE HERE best placement?
|
||||
}
|
||||
|
||||
processLDU1 (opts, state);
|
||||
@@ -510,6 +518,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
else if (strcmp (duid, "22") == 0)
|
||||
{
|
||||
// Logical Link Data Unit 2
|
||||
state->dmrburstL = 27;
|
||||
state->currentslot = 0;
|
||||
if (state->lastp25type != 1)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
@@ -543,6 +553,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
else if (strcmp (duid, "33") == 0)
|
||||
{
|
||||
// Terminator with subsequent Link Control
|
||||
state->dmrburstL = 28;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
@@ -569,6 +580,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
else if (strcmp (duid, "03") == 0)
|
||||
{
|
||||
// Terminator without subsequent Link Control
|
||||
state->dmrburstL = 28;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
@@ -589,10 +601,11 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else if (strcmp (duid, "13") == 0)
|
||||
{
|
||||
state->dmrburstL = 29;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," TSDU\n");
|
||||
fprintf (stderr," TSBK");
|
||||
}
|
||||
if (opts->resume > 0)
|
||||
{
|
||||
@@ -601,11 +614,11 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lastp25type = 3;
|
||||
sprintf (state->fsubtype, " TSDU ");
|
||||
sprintf (state->fsubtype, " TSBK ");
|
||||
|
||||
// Now processing NID
|
||||
processTSBK(opts, state);
|
||||
|
||||
skipDibit (opts, state, 328-25);
|
||||
}
|
||||
else if (strcmp (duid, "30") == 0)
|
||||
{
|
||||
@@ -676,15 +689,14 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," (TSDU)\n");
|
||||
fprintf (stderr," (TSBK)\n");
|
||||
}
|
||||
//state->lastp25type = 0;
|
||||
// Guess that the state is TSDU
|
||||
// Guess that the state is TSBK
|
||||
state->lastp25type = 3;
|
||||
sprintf (state->fsubtype, "(TSDU) ");
|
||||
sprintf (state->fsubtype, "(TSBK) ");
|
||||
|
||||
// Now processing NID
|
||||
|
||||
skipDibit (opts, state, 328-25);
|
||||
}
|
||||
else if (state->lastp25type == 4)
|
||||
@@ -703,8 +715,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
//fprintf (stderr," duid:%s *Unknown DUID*\n", duid); //prints on dPMR frame 3
|
||||
fprintf (stderr, "\n"); //prints on dPMR frame 3
|
||||
fprintf (stderr," duid:%s *Unknown DUID*\n", duid); //prints on dPMR frame 3
|
||||
// fprintf (stderr, "\n"); //prints on dPMR frame 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,14 +295,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
state->dmr_payload_p = state->dmr_payload_buf + 200;
|
||||
}
|
||||
|
||||
// int valid;
|
||||
//running estimate_symbol causes an issue with P25 syncing properly
|
||||
// if (opts->frame_p25p1 != 1)
|
||||
// {
|
||||
// valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||
// }
|
||||
|
||||
if (1 == 1) //opts->dmr_stereo //opts->frame_dmr == 1 && valid == 0
|
||||
if (1 == 1)
|
||||
{
|
||||
if (symbol > state->center)
|
||||
{
|
||||
@@ -474,6 +468,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
state->dmrburstR = 17;
|
||||
sprintf (state->ftype, "P25 Phase 1");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
@@ -489,6 +484,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
state->dmrburstR = 17;
|
||||
sprintf (state->ftype, "P25 Phase 1");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
@@ -616,11 +612,12 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
}
|
||||
//end YSF sync
|
||||
//P25 P2 sync
|
||||
strncpy(synctest20, (synctest_p - 19), 20); //double check make sure this is right
|
||||
if(opts->frame_p25p2 == 1) //(opts->frame_ysf == 1
|
||||
|
||||
//P25 P2 sync S-ISCH VCH
|
||||
strncpy(synctest20, (synctest_p - 19), 20);
|
||||
if(opts->frame_p25p2 == 1)
|
||||
{
|
||||
if (0 == 0) //opts->inverted_ysf == 0
|
||||
if (0 == 0)
|
||||
{
|
||||
if (strcmp(synctest20, P25P2_SYNC) == 0)
|
||||
{
|
||||
@@ -628,40 +625,69 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
fprintf (stderr, "\n+P25-P2 Sync");
|
||||
opts->inverted_ysf = 0; //should we set this here?
|
||||
state->lastsynctype = 30;
|
||||
opts->inverted_p2 = 0;
|
||||
state->lastsynctype = 35; //35
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "+P25p2 SISCH", synctest_pos + 1, modulation);
|
||||
}
|
||||
if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0)
|
||||
{
|
||||
fprintf (stderr, " WACN [%05llX] SYS [%03llX] NAC [%03llX] ", state->p2_wacn, state->p2_sysid, state->p2_cc);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " P2 Missing Parameters ");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
if ( opts->monitor_input_audio == 1)
|
||||
{
|
||||
|
||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
||||
}
|
||||
return (35);
|
||||
return (35); //35
|
||||
}
|
||||
}
|
||||
}
|
||||
if(opts->frame_p25p2 == 1) //(opts->frame_p25p2 == 1
|
||||
if(opts->frame_p25p2 == 1)
|
||||
{
|
||||
if (0 == 0) //inverted p25
|
||||
if (0 == 0)
|
||||
{
|
||||
//S-ISCH VCH
|
||||
if (strcmp(synctest20, INV_P25P2_SYNC) == 0)
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
fprintf (stderr, "\n-P25-P2 Sync");
|
||||
opts->inverted_ysf = 1; //should we set this here?
|
||||
state->lastsynctype = 31;
|
||||
opts->inverted_p2 = 1;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "-P25p2 SISCH", synctest_pos + 1, modulation);
|
||||
}
|
||||
if (state->p2_wacn != 0 && state->p2_cc != 0 && state->p2_sysid != 0)
|
||||
{
|
||||
//fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " WACN [%05llX] SYS [%03llX] NAC [%03llX] ", state->p2_wacn, state->p2_sysid, state->p2_cc);
|
||||
//fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " P2 Missing Parameters ");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
state->lastsynctype = 36; //36
|
||||
if ( opts->monitor_input_audio == 1)
|
||||
{
|
||||
|
||||
pa_simple_flush(opts->pulse_raw_dev_out, NULL);
|
||||
}
|
||||
return (36);
|
||||
return (36); //36
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//dPMR sync
|
||||
strncpy(synctest, (synctest_p - 23), 24);
|
||||
strncpy(synctest12, (synctest_p - 11), 12);
|
||||
|
||||
304
src/dsd_main.c
304
src/dsd_main.c
@@ -43,7 +43,6 @@ int pretty_colors()
|
||||
|
||||
#include "p25p1_heuristics.h"
|
||||
#include "pa_devs.h"
|
||||
short int butt = 0;
|
||||
|
||||
char * FM_banner[9] = {
|
||||
" ",
|
||||
@@ -69,6 +68,7 @@ comp (const void *a, const void *b)
|
||||
|
||||
//struct for checking existence of directory to write to
|
||||
struct stat st = {0};
|
||||
char wav_file_directory[1024] = {0};
|
||||
|
||||
void
|
||||
noCarrier (dsd_opts * opts, dsd_state * state)
|
||||
@@ -105,10 +105,8 @@ noCarrier (dsd_opts * opts, dsd_state * state)
|
||||
state->repeat = 0;
|
||||
state->nac = 0;
|
||||
state->numtdulc = 0;
|
||||
//sprintf (state->slot0light, " slot0 ");
|
||||
//sprintf (state->slot0light, " slot0 ");
|
||||
sprintf (state->slot1light, "");
|
||||
sprintf (state->slot2light, "");
|
||||
sprintf (state->slot1light, "%s", "");
|
||||
sprintf (state->slot2light, "%s", "");
|
||||
state->firstframe = 0;
|
||||
if (opts->audio_gain == (float) 0)
|
||||
{
|
||||
@@ -196,6 +194,18 @@ noCarrier (dsd_opts * opts, dsd_state * state)
|
||||
state->dmr_encL = 0;
|
||||
state->dmr_encR = 0;
|
||||
|
||||
state->dmrburstL = 17;
|
||||
state->dmrburstR = 17;
|
||||
|
||||
//reset P2 ESS_B fragments and 4V counter
|
||||
for (short i = 0; i < 4; i++)
|
||||
{
|
||||
state->ess_b[0][i] = 0;
|
||||
state->ess_b[1][i] = 0;
|
||||
}
|
||||
state->fourv_counter[0] = 0;
|
||||
state->fourv_counter[1] = 0;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
@@ -237,6 +247,7 @@ initOpts (dsd_opts * opts)
|
||||
opts->lrrp_out_file[0] = 0;
|
||||
opts->symbol_out_f = NULL;
|
||||
opts->symbol_out = 0;
|
||||
opts->mbe_out = 0;
|
||||
opts->wav_out_f = NULL;
|
||||
opts->wav_out_fR = NULL;
|
||||
opts->wav_out_raw = NULL;
|
||||
@@ -246,18 +257,18 @@ initOpts (dsd_opts * opts)
|
||||
opts->serial_baud = 115200;
|
||||
sprintf (opts->serial_dev, "/dev/ttyUSB0");
|
||||
opts->resume = 0;
|
||||
opts->frame_dstar = 1;
|
||||
opts->frame_dstar = 0;
|
||||
opts->frame_x2tdma = 1;
|
||||
opts->frame_p25p1 = 1;
|
||||
opts->frame_p25p2 = 0;
|
||||
opts->frame_p25p2 = 1;
|
||||
opts->frame_nxdn48 = 0;
|
||||
opts->frame_nxdn96 = 0;
|
||||
opts->frame_dmr = 1;
|
||||
opts->frame_dpmr = 0;
|
||||
opts->frame_provoice = 0;
|
||||
opts->mod_c4fm = 1;
|
||||
opts->mod_qpsk = 1;
|
||||
opts->mod_gfsk = 1;
|
||||
opts->mod_qpsk = 0;
|
||||
opts->mod_gfsk = 0;
|
||||
opts->uvquality = 3;
|
||||
opts->inverted_x2tdma = 1; // most transmitter + scanner + sound card combinations show inverted signals for this
|
||||
opts->inverted_dmr = 0; // most transmitter + scanner + sound card combinations show non-inverted signals for this
|
||||
@@ -279,20 +290,20 @@ initOpts (dsd_opts * opts)
|
||||
opts->pulse_raw_rate_in = 48000;
|
||||
opts->pulse_raw_rate_out = 48000; //
|
||||
opts->pulse_digi_rate_in = 48000;
|
||||
opts->pulse_digi_rate_out = 8000; // reverted to 8/1, crisper sound, dmr stereo will switch to 24/2
|
||||
opts->pulse_digi_rate_out = 24000; //new default for XDMA
|
||||
opts->pulse_raw_in_channels = 1;
|
||||
opts->pulse_raw_out_channels = 1;
|
||||
opts->pulse_digi_in_channels = 1; //2
|
||||
opts->pulse_digi_out_channels = 1; //2
|
||||
opts->pulse_digi_out_channels = 2; //new default for XDMA
|
||||
|
||||
sprintf (opts->output_name, "Auto Detect");
|
||||
sprintf (opts->output_name, "XDMA");
|
||||
opts->pulse_flush = 1; //set 0 to flush, 1 for flushed
|
||||
opts->use_ncurses_terminal = 0;
|
||||
opts->ncurses_compact = 0;
|
||||
opts->ncurses_history = 1;
|
||||
opts->payload = 0;
|
||||
opts->inverted_dpmr = 0;
|
||||
opts->dmr_stereo = 0;
|
||||
opts->dmr_stereo = 1;
|
||||
opts->aggressive_framesync = 1; //more aggressive to combat wonk wonk voice decoding
|
||||
//see if initializing these values causes issues elsewhere, if so, then disable.
|
||||
opts->audio_in_type = 0; //this was never initialized, causes issues on rPI 64 (bullseye) if not initialized
|
||||
@@ -305,6 +316,9 @@ initOpts (dsd_opts * opts)
|
||||
|
||||
opts->monitor_input_audio = 0;
|
||||
|
||||
opts->inverted_p2 = 0;
|
||||
opts->p2counter = 0;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
@@ -395,10 +409,8 @@ initState (dsd_state * state)
|
||||
state->optind = 0;
|
||||
state->numtdulc = 0;
|
||||
state->firstframe = 0;
|
||||
//sprintf (state->slot0light, " slot0 ");
|
||||
//sprintf (state->slot1light, " slot1 ");
|
||||
sprintf (state->slot1light, "");
|
||||
sprintf (state->slot2light, "");
|
||||
sprintf (state->slot1light, "%s", "");
|
||||
sprintf (state->slot2light, "%s", "");
|
||||
state->aout_gain = 25;
|
||||
memset (state->aout_max_buf, 0, sizeof (float) * 200);
|
||||
state->aout_max_buf_p = state->aout_max_buf;
|
||||
@@ -435,7 +447,7 @@ initState (dsd_state * state)
|
||||
state->nxdn_last_tg = 0;
|
||||
state->nxdn_cipher_type = 0;
|
||||
state->nxdn_key = 0;
|
||||
sprintf (state->nxdn_call_type, " ");
|
||||
sprintf (state->nxdn_call_type, "%s", "");
|
||||
state->payload_miN = 0;
|
||||
|
||||
state->dpmr_color_code = -1;
|
||||
@@ -451,41 +463,52 @@ initState (dsd_state * state)
|
||||
state->payload_keyid = 0;
|
||||
state->payload_keyidR = 0;
|
||||
|
||||
sprintf (state->dmr_branding, " ");
|
||||
sprintf (state->dmr_callsign[0][0], "");
|
||||
sprintf (state->dmr_callsign[0][1], "");
|
||||
sprintf (state->dmr_callsign[0][2], "");
|
||||
sprintf (state->dmr_callsign[0][3], "");
|
||||
sprintf (state->dmr_callsign[0][4], "");
|
||||
sprintf (state->dmr_callsign[0][5], "");
|
||||
sprintf (state->dmr_callsign[1][0], "");
|
||||
sprintf (state->dmr_callsign[1][1], "");
|
||||
sprintf (state->dmr_callsign[1][2], "");
|
||||
sprintf (state->dmr_callsign[1][3], "");
|
||||
sprintf (state->dmr_callsign[1][4], "");
|
||||
sprintf (state->dmr_callsign[1][5], "");
|
||||
sprintf (state->dmr_lrrp[0][0], "");
|
||||
sprintf (state->dmr_lrrp[0][1], "");
|
||||
sprintf (state->dmr_lrrp[0][2], "");
|
||||
sprintf (state->dmr_lrrp[0][3], "");
|
||||
sprintf (state->dmr_lrrp[0][4], "");
|
||||
sprintf (state->dmr_lrrp[0][5], "");
|
||||
sprintf (state->dmr_lrrp[1][0], "");
|
||||
sprintf (state->dmr_lrrp[1][1], "");
|
||||
sprintf (state->dmr_lrrp[1][2], "");
|
||||
sprintf (state->dmr_lrrp[1][3], "");
|
||||
sprintf (state->dmr_lrrp[1][4], "");
|
||||
sprintf (state->dmr_lrrp[1][5], "");
|
||||
//init P2 ESS_B fragments and 4V counter
|
||||
for (short i = 0; i < 4; i++)
|
||||
{
|
||||
state->ess_b[0][i] = 0;
|
||||
state->ess_b[1][i] = 0;
|
||||
}
|
||||
state->fourv_counter[0] = 0;
|
||||
state->fourv_counter[1] = 0;
|
||||
|
||||
sprintf (state->dmr_branding, "%s", "");
|
||||
sprintf (state->dmr_callsign[0][0], "%s", "");
|
||||
sprintf (state->dmr_callsign[0][1], "%s", "");
|
||||
sprintf (state->dmr_callsign[0][2], "%s", "");
|
||||
sprintf (state->dmr_callsign[0][3], "%s", "");
|
||||
sprintf (state->dmr_callsign[0][4], "%s", "");
|
||||
sprintf (state->dmr_callsign[0][5], "%s", "");
|
||||
sprintf (state->dmr_callsign[1][0], "%s", "");
|
||||
sprintf (state->dmr_callsign[1][1], "%s", "");
|
||||
sprintf (state->dmr_callsign[1][2], "%s", "");
|
||||
sprintf (state->dmr_callsign[1][3], "%s", "");
|
||||
sprintf (state->dmr_callsign[1][4], "%s", "");
|
||||
sprintf (state->dmr_callsign[1][5], "%s", "");
|
||||
sprintf (state->dmr_lrrp[0][0], "%s", "");
|
||||
sprintf (state->dmr_lrrp[0][1], "%s", "");
|
||||
sprintf (state->dmr_lrrp[0][2], "%s", "");
|
||||
sprintf (state->dmr_lrrp[0][3], "%s", "");
|
||||
sprintf (state->dmr_lrrp[0][4], "%s", "");
|
||||
sprintf (state->dmr_lrrp[0][5], "%s", "");
|
||||
sprintf (state->dmr_lrrp[1][0], "%s", "");
|
||||
sprintf (state->dmr_lrrp[1][1], "%s", "");
|
||||
sprintf (state->dmr_lrrp[1][2], "%s", "");
|
||||
sprintf (state->dmr_lrrp[1][3], "%s", "");
|
||||
sprintf (state->dmr_lrrp[1][4], "%s", "");
|
||||
sprintf (state->dmr_lrrp[1][5], "%s", "");
|
||||
|
||||
state->K = 0;
|
||||
state->R = 0;
|
||||
state->RR = 0;
|
||||
state->H = 0;
|
||||
state->K1 = 0;
|
||||
state->K2 = 0;
|
||||
state->K3 = 0;
|
||||
state->K4 = 0;
|
||||
state->M = 0; //force key priority over settings from fid/so
|
||||
|
||||
state->dmr_stereo = 0;
|
||||
state->dmr_stereo = 0; //1, or 0?
|
||||
state->dmrburstL = 17; //initialize at higher value than possible
|
||||
state->dmrburstR = 17; //17 in char array is set for ERR
|
||||
state->dmr_so = 0;
|
||||
@@ -542,6 +565,16 @@ initState (dsd_state * state)
|
||||
state->dmr_encL = 0;
|
||||
state->dmr_encR = 0;
|
||||
|
||||
//P2 variables
|
||||
state->p2_wacn = 0;
|
||||
state->p2_sysid = 0;
|
||||
state->p2_cc = 0;
|
||||
state->p2_hardset = 0;
|
||||
state->p2_is_lcch = 0;
|
||||
|
||||
state->p2_scramble_offset = 0;
|
||||
state->p2_vch_chan_num = 0;
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
state->debug_sample_index = 0;
|
||||
state->debug_label_file = NULL;
|
||||
@@ -563,7 +596,7 @@ usage ()
|
||||
printf ("\n");
|
||||
printf ("Display Options:\n");
|
||||
printf (" -N Use NCurses Terminal\n");
|
||||
printf (" -use with 2> log.txt at end of command\n");
|
||||
printf (" dsd-fme -N 2> log.txt \n");
|
||||
printf (" -e Show Frame Info and errorbars (default)\n");
|
||||
printf (" -pe Show P25 encryption sync bits\n");
|
||||
printf (" -pl Show P25 link control bits\n");
|
||||
@@ -582,8 +615,11 @@ usage ()
|
||||
printf (" -r <files> Read/Play saved mbe data from file(s)\n");
|
||||
printf (" -g <num> Audio output gain (default = 0 = auto, disable = -1)\n");
|
||||
printf (" -w <file> Output synthesized speech to a .wav file\n");
|
||||
printf (" -T Enable Per Call WAV file saving in XDMA and NXDN decoding classes\n");
|
||||
printf (" (Per Call can only be used in Ncurses Terminal!)\n");
|
||||
printf (" (Running in console will use static wav files)\n");
|
||||
//printf (" -a Display port audio devices\n");
|
||||
//printf (" -W Monitor Source Audio When No Sync Detected (WIP!)\n");
|
||||
//printf (" -W Monitor Source Audio When No Sync Detected (broken!)\n");
|
||||
printf ("\n");
|
||||
printf ("RTL-SDR options:\n");
|
||||
printf (" -c <hertz> RTL-SDR Frequency\n");
|
||||
@@ -601,7 +637,8 @@ usage ()
|
||||
printf (" -R <num> Resume scan after <num> TDULC frames or any PDU or TSDU\n");
|
||||
printf ("\n");
|
||||
printf ("Decoder options:\n");
|
||||
printf (" -fa Auto-detect frame type (default)\n");
|
||||
printf (" -fa Legacy Auto Detection (old methods default)\n");
|
||||
printf (" -ft XDMA P25 and DMR BS/MS frame types (new default)\n");
|
||||
printf (" -f1 Decode only P25 Phase 1\n");
|
||||
printf (" -fd Decode only D-STAR\n");
|
||||
printf (" -fr Decode only DMR\n");
|
||||
@@ -615,7 +652,7 @@ usage ()
|
||||
printf (" -u <num> Unvoiced speech quality (default=3)\n");
|
||||
printf (" -xx Expect non-inverted X2-TDMA signal\n");
|
||||
printf (" -xr Expect inverted DMR signal\n");
|
||||
printf (" -xd Expect inverted ICOM dPMR signal\n"); //still need to do this
|
||||
printf (" -xd Expect inverted ICOM dPMR signal\n");
|
||||
printf ("\n");
|
||||
printf (" * denotes frame types that cannot be auto-detected.\n");
|
||||
printf ("\n");
|
||||
@@ -625,18 +662,19 @@ usage ()
|
||||
printf (" (default=36)\n");
|
||||
printf (" -M <num> Min/Max buffer size for QPSK decision point tracking\n");
|
||||
printf (" (default=15)\n");
|
||||
printf (" -T Enable DMR TDMA Stereo Voice (Two Slot Dual Voices)\n");
|
||||
printf (" This feature will open two streams for slot 1 voice and slot 2 voice\n");
|
||||
printf (" May Cause Skipping or sync issues if bad signal/errors\n");
|
||||
printf (" -F Enable DMR TDMA Stereo Passive Frame Sync\n");
|
||||
printf (" This feature will attempt to resync less often due to excessive voice errors\n");
|
||||
printf (" Use if skipping occurs, but may cause wonky audio due to loss of good sync\n");
|
||||
// printf (" -T Enable DMR TDMA Stereo Voice (Two Slot Dual Voices)\n");
|
||||
// printf (" This feature will open two streams for slot 1 voice and slot 2 voice\n");
|
||||
// printf (" May Cause Skipping or sync issues if bad signal/errors\n");
|
||||
// printf (" -F Enable DMR TDMA Stereo Passive Frame Sync\n");
|
||||
// printf (" This feature will attempt to resync less often due to excessive voice errors\n");
|
||||
// printf (" Use if skipping occurs, but may cause wonky audio due to loss of good sync\n");
|
||||
printf (" -Z Log MBE/Frame Payloads to console\n");
|
||||
printf ("\n");
|
||||
printf (" -K <dec> Manually Enter DMRA Privacy Key (Decimal Value of Key Number)\n");
|
||||
printf ("\n");
|
||||
printf (" -H <hex> Manually Enter **tera 10 Privacy Key (40-Bit/10-Char Hex Value)\n");
|
||||
printf (" (32/64-Char values can only be entered in the NCurses Terminal)\n");
|
||||
printf ("\n");
|
||||
printf (" -R <dec> Manually Enter NXDN 4800 EHR Scrambler Key Value \n");
|
||||
printf (" \n");
|
||||
printf ("\n");
|
||||
@@ -757,7 +795,7 @@ cleanupAndExit (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
closeWavOutFileRaw (opts, state);
|
||||
}
|
||||
if (opts->dmr_stereo_wav == 1)
|
||||
if (opts->dmr_stereo_wav == 1) //cause of crash on exit, need to check if NULL first, may need to set NULL when turning off in nterm
|
||||
{
|
||||
closeWavOutFileL (opts, state);
|
||||
closeWavOutFileR (opts, state);
|
||||
@@ -1028,19 +1066,22 @@ main (int argc, char **argv)
|
||||
fprintf (stderr,"Logging LRRP data to ~/lrrp.txt \n");
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
opts.dmr_stereo = 1; //this value is the end user option
|
||||
state.dmr_stereo = 1; //this values toggles on and off depending on voice or data handling
|
||||
opts.pulse_digi_rate_out = 24000;
|
||||
opts.pulse_digi_out_channels = 2;
|
||||
sprintf (opts.output_name, "DMR STEREO");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr,"DMR Stereo Sync and Functionality Enabled.\n");
|
||||
fprintf (stderr,"DMR Stereo will disable MBE file saving!\n");
|
||||
fprintf (stderr,"DMR Stereo per call WAV file can be enabled in the NCurses Terminal!\n");
|
||||
fprintf (stderr,"Also consider using -F if playback is too choppy!\n");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
break;
|
||||
case 'T': //repurposed to TDMA/NXDN Per Call
|
||||
sprintf (wav_file_directory, "./WAV"); // /wav, or ./wav
|
||||
wav_file_directory[1023] = '\0';
|
||||
if (stat(wav_file_directory, &st) == -1)
|
||||
{
|
||||
fprintf (stderr, "-T %s WAV file directory does not exist\n", wav_file_directory);
|
||||
fprintf (stderr, "Creating directory %s to save decoded wav files\n", wav_file_directory);
|
||||
mkdir(wav_file_directory, 0700); //user read write execute, needs execute for some reason or segfault
|
||||
}
|
||||
fprintf (stderr,"XDMA and NXDN Per Call Wav File Saving Enabled. (NCurses Terminal Only)\n");
|
||||
sprintf (opts.wav_out_file, "./WAV/DSD-FME-X1.wav"); // foward slash here, on wav_file_directory?
|
||||
sprintf (opts.wav_out_fileR, "./WAV/DSD-FME-X2.wav");
|
||||
opts.dmr_stereo_wav = 1;
|
||||
openWavOutFileL (&opts, &state); //testing for now, will want to move to per call later
|
||||
openWavOutFileR (&opts, &state); //testing for now, will want to move to per call later
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
opts.aggressive_framesync = 0;
|
||||
@@ -1079,6 +1120,7 @@ main (int argc, char **argv)
|
||||
mkdir(opts.mbe_out_dir, 0700); //user read write execute, needs execute for some reason or segfault
|
||||
}
|
||||
else fprintf (stderr,"Writing mbe data files to directory %s\n", opts.mbe_out_dir);
|
||||
// fprintf (stderr,"Writing mbe data temporarily disabled!\n");
|
||||
break;
|
||||
case 'c':
|
||||
opts.rtlsdr_center_freq = (uint32_t)atofs(optarg);
|
||||
@@ -1130,23 +1172,33 @@ main (int argc, char **argv)
|
||||
opts.frame_dstar = 1;
|
||||
opts.frame_x2tdma = 1;
|
||||
opts.frame_p25p1 = 1;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 1;
|
||||
opts.frame_dpmr = 0;
|
||||
opts.frame_provoice = 0;
|
||||
sprintf (opts.output_name, "Auto Detect");
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "Legacy Auto");
|
||||
}
|
||||
else if (optarg[0] == 'd')
|
||||
{
|
||||
opts.frame_dstar = 1;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
opts.frame_dpmr = 0;
|
||||
opts.frame_provoice = 0;
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
state.rf_mod = 0;
|
||||
sprintf (opts.output_name, "D-STAR");
|
||||
fprintf (stderr,"Decoding only D-STAR frames.\n");
|
||||
@@ -1156,11 +1208,16 @@ main (int argc, char **argv)
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 1;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
opts.frame_dpmr = 0;
|
||||
opts.frame_provoice = 0;
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "X2-TDMA");
|
||||
fprintf (stderr,"Decoding only X2-TDMA frames.\n");
|
||||
}
|
||||
@@ -1169,6 +1226,7 @@ main (int argc, char **argv)
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
@@ -1180,6 +1238,10 @@ main (int argc, char **argv)
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 1;
|
||||
state.rf_mod = 2;
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "ProVoice");
|
||||
fprintf (stderr,"Setting symbol rate to 9600 / second\n");
|
||||
fprintf (stderr,"Decoding only ProVoice frames.\n");
|
||||
@@ -1189,15 +1251,20 @@ main (int argc, char **argv)
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 1;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
opts.frame_dpmr = 0;
|
||||
opts.frame_provoice = 0;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
opts.mod_c4fm = 1;
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 0; //
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
sprintf (opts.output_name, "P25P1");
|
||||
fprintf (stderr,"Decoding only P25 Phase 1 frames.\n");
|
||||
}
|
||||
@@ -1206,6 +1273,7 @@ main (int argc, char **argv)
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_nxdn48 = 1;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
@@ -1218,6 +1286,10 @@ main (int argc, char **argv)
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 0;
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "NXDN48");
|
||||
fprintf (stderr,"Setting symbol rate to 2400 / second\n");
|
||||
fprintf (stderr,"Decoding only NXDN 4800 baud frames.\n");
|
||||
@@ -1228,6 +1300,7 @@ main (int argc, char **argv)
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
@@ -1240,6 +1313,10 @@ main (int argc, char **argv)
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 0;
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "YSF");
|
||||
fprintf (stderr,"Setting symbol rate to 2400 / second\n");
|
||||
fprintf (stderr,"Decoding only YSF frames.\nNot working yet!\n");
|
||||
@@ -1262,14 +1339,43 @@ main (int argc, char **argv)
|
||||
opts.mod_qpsk = 1;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 0;
|
||||
opts.dmr_stereo = 1;
|
||||
state.dmr_stereo = 1;
|
||||
sprintf (opts.output_name, "P25-P2");
|
||||
fprintf (stderr,"Decoding P25-P2 frames.\nNot working yet!\n");
|
||||
fprintf (stderr,"Decoding P25-P2 frames C4FM or OP25 Symbol Captures!\n");
|
||||
}
|
||||
else if (optarg[0] == 't')
|
||||
{
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 1;
|
||||
opts.frame_p25p1 = 1;
|
||||
opts.frame_p25p2 = 1;
|
||||
opts.inverted_p2 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 1;
|
||||
opts.frame_dpmr = 0;
|
||||
opts.frame_provoice = 0;
|
||||
opts.frame_ysf = 0;
|
||||
opts.mod_c4fm = 1;
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 0;
|
||||
//Need a new demodulator is needed to handle p2 cqpsk
|
||||
//or consider using an external modulator (GNURadio?)
|
||||
state.rf_mod = 0;
|
||||
opts.dmr_stereo = 1;
|
||||
//state.dmr_stereo = 1;
|
||||
opts.pulse_digi_rate_out = 24000;
|
||||
opts.pulse_digi_out_channels = 2;
|
||||
sprintf (opts.output_name, "XDMA");
|
||||
fprintf (stderr,"Decoding XDMA P25 and DMR\n");
|
||||
}
|
||||
else if (optarg[0] == 'n')
|
||||
{
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 1;
|
||||
opts.frame_dmr = 0;
|
||||
@@ -1279,6 +1385,10 @@ main (int argc, char **argv)
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 0;
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "NXDN96");
|
||||
fprintf (stderr,"Decoding only NXDN 9600 baud frames.\n");
|
||||
}
|
||||
@@ -1287,6 +1397,7 @@ main (int argc, char **argv)
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 1;
|
||||
@@ -1296,8 +1407,12 @@ main (int argc, char **argv)
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 0; //
|
||||
state.rf_mod = 0; //
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "DMR LEH");
|
||||
//fprintf(stderr, "Notice: DMR cannot autodetect polarity. \n Use -xr option if Inverted Signal expected.\n");
|
||||
fprintf(stderr, "Notice: DMR cannot autodetect polarity. \n Use -xr option if Inverted Signal expected.\n");
|
||||
fprintf (stderr,"Decoding only DMR frames.\n");
|
||||
}
|
||||
else if (optarg[0] == 'm')
|
||||
@@ -1305,6 +1420,7 @@ main (int argc, char **argv)
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_p25p2 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
@@ -1317,6 +1433,10 @@ main (int argc, char **argv)
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 0;
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "dPMR");
|
||||
fprintf(stderr, "Notice: dPMR cannot autodetect polarity. \n Use -xd option if Inverted Signal expected.\n");
|
||||
fprintf(stderr, "Decoding only dPMR frames.\n");
|
||||
@@ -1326,34 +1446,34 @@ main (int argc, char **argv)
|
||||
case 'm':
|
||||
if (optarg[0] == 'a')
|
||||
{
|
||||
//opts.mod_c4fm = 1;
|
||||
//opts.mod_qpsk = 1;
|
||||
//opts.mod_gfsk = 1;
|
||||
//state.rf_mod = 0;
|
||||
opts.mod_c4fm = 1;
|
||||
opts.mod_qpsk = 1;
|
||||
opts.mod_gfsk = 1;
|
||||
state.rf_mod = 0;
|
||||
}
|
||||
else if (optarg[0] == 'c')
|
||||
{
|
||||
//opts.mod_c4fm = 1;
|
||||
//opts.mod_qpsk = 0;
|
||||
//opts.mod_gfsk = 0;
|
||||
//state.rf_mod = 0;
|
||||
//fprintf (stderr,"Enabling only C4FM modulation optimizations.\n");
|
||||
opts.mod_c4fm = 1;
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 0;
|
||||
fprintf (stderr,"Enabling only C4FM modulation optimizations.\n");
|
||||
}
|
||||
else if (optarg[0] == 'g')
|
||||
{
|
||||
//opts.mod_c4fm = 0;
|
||||
//opts.mod_qpsk = 0;
|
||||
//opts.mod_gfsk = 1;
|
||||
//state.rf_mod = 2;
|
||||
//fprintf (stderr,"Enabling only GFSK modulation optimizations.\n");
|
||||
opts.mod_c4fm = 0;
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 1;
|
||||
state.rf_mod = 2;
|
||||
fprintf (stderr,"Enabling only GFSK modulation optimizations.\n");
|
||||
}
|
||||
else if (optarg[0] == 'q')
|
||||
{
|
||||
//opts.mod_c4fm = 0;
|
||||
//opts.mod_qpsk = 1;
|
||||
//opts.mod_gfsk = 0;
|
||||
//state.rf_mod = 1;
|
||||
//fprintf (stderr,"Enabling only QPSK modulation optimizations.\n");
|
||||
opts.mod_c4fm = 0;
|
||||
opts.mod_qpsk = 1;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 1;
|
||||
fprintf (stderr,"Enabling only QPSK modulation optimizations.\n");
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
@@ -1416,6 +1536,10 @@ main (int argc, char **argv)
|
||||
opts.playfiles = 1;
|
||||
opts.errorbars = 0;
|
||||
opts.datascope = 0;
|
||||
opts.pulse_digi_rate_out = 8000;
|
||||
opts.pulse_digi_out_channels = 1;
|
||||
opts.dmr_stereo = 0;
|
||||
state.dmr_stereo = 0;
|
||||
sprintf (opts.output_name, "MBE Playback");
|
||||
state.optind = optind;
|
||||
break;
|
||||
|
||||
@@ -202,7 +202,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
||||
state->errs2 += mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||
|
||||
if (state->nxdn_cipher_type == 0x01 && state->R > 0)
|
||||
if ( (state->nxdn_cipher_type == 0x01 && state->R > 0) ||
|
||||
(state->M == 1 && state->R > 0) )
|
||||
{
|
||||
|
||||
if (state->payload_miN == 0)
|
||||
@@ -245,7 +246,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
||||
state->errs2 += mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||
|
||||
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
|
||||
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||
(state->K > 0 && state->M == 1) )
|
||||
{
|
||||
k = Pr[state->K];
|
||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||
@@ -256,7 +258,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
}
|
||||
|
||||
if (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
|
||||
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
|
||||
(state->K1 > 0 && state->M == 1) )
|
||||
{
|
||||
|
||||
int pos = 0;
|
||||
@@ -338,7 +341,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
||||
state->errs2R += mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||
|
||||
if (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10)
|
||||
if ( (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
|
||||
(state->K > 0 && state->M == 1) )
|
||||
{
|
||||
k = Pr[state->K];
|
||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||
@@ -349,7 +353,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
}
|
||||
|
||||
if (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68)
|
||||
if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
|
||||
(state->K1 > 0 && state->M == 1))
|
||||
{
|
||||
|
||||
int pos = 0;
|
||||
@@ -451,7 +456,23 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
{
|
||||
state->dmr_encL = 1;
|
||||
}
|
||||
|
||||
//checkdown for P25 1 and 2
|
||||
else if (state->payload_algid != 0 && state->payload_algid != 0x80)
|
||||
{
|
||||
state->dmr_encL = 1;
|
||||
}
|
||||
else state->dmr_encL = 0;
|
||||
|
||||
//second checkdown for P25-2 WACN, SYSID, and CC set
|
||||
if (state->synctype == 35 || state->synctype == 36)
|
||||
{
|
||||
if (state->p2_wacn == 0 || state->p2_sysid == 0 || state->p2_cc == 0)
|
||||
{
|
||||
state->dmr_encL = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
||||
{
|
||||
state->debug_audio_errors += state->errs2;
|
||||
@@ -470,7 +491,23 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
{
|
||||
state->dmr_encR = 1;
|
||||
}
|
||||
|
||||
//checkdown for P25 1 and 2
|
||||
else if (state->payload_algidR != 0 && state->payload_algidR != 0x80)
|
||||
{
|
||||
state->dmr_encR = 1;
|
||||
}
|
||||
else state->dmr_encR = 0;
|
||||
|
||||
//second checkdown for P25-2 WACN, SYSID, and CC set
|
||||
if (state->synctype == 35 || state->synctype == 36)
|
||||
{
|
||||
if (state->p2_wacn == 0 || state->p2_sysid == 0 || state->p2_cc == 0)
|
||||
{
|
||||
state->dmr_encR = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
||||
{
|
||||
state->debug_audio_errorsR += state->errs2R;
|
||||
@@ -500,7 +537,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
|
||||
//per call can only be used when ncurses terminal is active since we use its call history matrix for when to record
|
||||
if (opts->use_ncurses_terminal == 1 && opts->dmr_stereo_wav == 1 && opts->dmr_stereo == 1 && state->currentslot == 0)
|
||||
if (opts->dmr_stereo_wav == 1 && opts->dmr_stereo == 1 && state->currentslot == 0) //opts->use_ncurses_terminal == 1 &&
|
||||
{
|
||||
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
||||
{
|
||||
@@ -510,7 +547,7 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||
}
|
||||
|
||||
//per call can only be used when ncurses terminal is active since we use its call history matrix for when to record
|
||||
if (opts->use_ncurses_terminal == 1 && opts->dmr_stereo_wav == 1 && opts->dmr_stereo == 1 && state->currentslot == 1)
|
||||
if (opts->dmr_stereo_wav == 1 && opts->dmr_stereo == 1 && state->currentslot == 1) //opts->use_ncurses_terminal == 1 &&
|
||||
{
|
||||
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -371,9 +371,8 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
||||
openPulseInput(opts);
|
||||
}
|
||||
|
||||
//flipping these to 'positive' produces terrible results in P25 P1, and I tried flopping it every way I could
|
||||
//positive polarity works fine with DMR, but not P25, so may look at reading and storing based on frame type?
|
||||
if (opts->frame_p25p1 == 1) //use inverted polarity for reading P25, but not DMR (or others?)
|
||||
//flip dibit values when p25-p1, has issues when read in positive polarity
|
||||
if (state->synctype == 0 || state->synctype == 1 )
|
||||
{
|
||||
if (state->symbolc == 0)
|
||||
{
|
||||
|
||||
311
src/ez.cpp
Normal file
311
src/ez.cpp
Normal file
@@ -0,0 +1,311 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* ez.cpp
|
||||
* EZPWD R-S bridge and ISCH map lookup
|
||||
*
|
||||
* original copyrights for portions used below (OP25, EZPWD)
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-09 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include "ezpwd/rs"
|
||||
#include <map>
|
||||
|
||||
std::vector<uint8_t> ESS_A(28,0); // ESS_A and ESS_B are hexbits vectors
|
||||
std::vector<uint8_t> ESS_B(16,0);
|
||||
ezpwd::RS<63,35> rs28;
|
||||
std::vector<uint8_t> HB(63,0);
|
||||
std::vector<uint8_t> HBS(63,0);
|
||||
std::vector<uint8_t> HBL(63,0);
|
||||
std::vector<int> Erasures;
|
||||
|
||||
//Reed-Solomon Correction of ESS section
|
||||
int ez_rs28_ess (int payload[96], int parity[168])
|
||||
{
|
||||
//do something
|
||||
|
||||
uint8_t a, b, i, j, k;
|
||||
k = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
b = 0;
|
||||
for (j = 0; j < 6; j++)
|
||||
{
|
||||
b = b << 1;
|
||||
b = b + payload[k]; //convert bits to hexbits.
|
||||
k++;
|
||||
}
|
||||
ESS_B[i] = b;
|
||||
// fprintf (stderr, " %X", ESS_B[i]);
|
||||
}
|
||||
|
||||
k = 0;
|
||||
for (i = 0; i < 28; i++)
|
||||
{
|
||||
a = 0;
|
||||
for (j = 0; j < 6; j++)
|
||||
{
|
||||
a = a << 1;
|
||||
a = a + parity[k]; //convert bits to hexbits.
|
||||
k++;
|
||||
}
|
||||
ESS_A[i] = a;
|
||||
// fprintf (stderr, " %X ", ESS_A[i]);
|
||||
}
|
||||
|
||||
int ec;
|
||||
|
||||
ec = rs28.decode (ESS_B, ESS_A);
|
||||
// fprintf (stderr, "\n EC = %d \n", ec);
|
||||
|
||||
//convert ESS_B back to bits
|
||||
k = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
b = 0;
|
||||
for (j = 0; j < 6; j++)
|
||||
{
|
||||
b = (ESS_B[i] >> (5-j) & 0x1);
|
||||
payload[k] = b;
|
||||
k++;
|
||||
}
|
||||
// fprintf (stderr, " %X", ESS_B[i]);
|
||||
}
|
||||
|
||||
return(ec);
|
||||
|
||||
}
|
||||
|
||||
//Reed-Solomon Correction of FACCH section
|
||||
int ez_rs28_facch (int payload[156], int parity[114])
|
||||
{
|
||||
//do something!
|
||||
int ec = -2;
|
||||
int i, j, k, b;
|
||||
|
||||
//Erasures for FACCH
|
||||
Erasures = {0,1,2,3,4,5,6,7,8,54,55,56,57,58,59,60,61,62};
|
||||
|
||||
//convert bits to hexbits, 156 for payload, 114 parity
|
||||
j = 9; //starting position according to OP25
|
||||
for (i = 0; i < 156; i += 6)
|
||||
{
|
||||
HB[j] = (payload[i] << 5) + (payload[i+1] << 4) + (payload[i+2] << 3) + (payload[i+3] << 2) + (payload[i+4] << 1) + payload[i+5];
|
||||
j++;
|
||||
}
|
||||
//j should continue from its last increment
|
||||
for (i = 0; i < 114; i += 6)
|
||||
{
|
||||
HB[j] = (parity[i] << 5) + (parity[i+1] << 4) + (parity[i+2] << 3) + (parity[i+3] << 2) + (parity[i+4] << 1) + parity[i+5];
|
||||
j++;
|
||||
}
|
||||
|
||||
ec = rs28.decode(HB, Erasures);
|
||||
|
||||
//convert HB back to bits
|
||||
//fprintf (stderr, "\n");
|
||||
k = 0;
|
||||
for (i = 0; i < 26; i++) //26*6=156 bits
|
||||
{
|
||||
b = 0;
|
||||
for (j = 0; j < 6; j++)
|
||||
{
|
||||
b = (HB[i+9] >> (5-j) & 0x1); //+9 to mach our starting position
|
||||
payload[k] = b;
|
||||
//fprintf (stderr, "%d", payload[k]);
|
||||
k++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (ec);
|
||||
}
|
||||
|
||||
//Reed-Solomon Correction of SACCH section
|
||||
int ez_rs28_sacch (int payload[180], int parity[132])
|
||||
{
|
||||
//do something!
|
||||
int ec = -2;
|
||||
int i, j, k, b;
|
||||
|
||||
//Erasures for SACCH
|
||||
Erasures = {0,1,2,3,4,57,58,59,60,61,62};
|
||||
|
||||
//convert bits to hexbits, 156 for payload, 114 parity
|
||||
j = 5; //starting position according to OP25
|
||||
for (i = 0; i < 180; i += 6)
|
||||
{
|
||||
HBS[j] = (payload[i] << 5) + (payload[i+1] << 4) + (payload[i+2] << 3) + (payload[i+3] << 2) + (payload[i+4] << 1) + payload[i+5];
|
||||
j++;
|
||||
}
|
||||
//j should continue from its last increment
|
||||
for (i = 0; i < 132; i += 6)
|
||||
{
|
||||
HBS[j] = (parity[i] << 5) + (parity[i+1] << 4) + (parity[i+2] << 3) + (parity[i+3] << 2) + (parity[i+4] << 1) + parity[i+5];
|
||||
j++;
|
||||
}
|
||||
|
||||
ec = rs28.decode(HBS, Erasures);
|
||||
|
||||
//convert HBS back to bits
|
||||
// fprintf (stderr, "\n");
|
||||
k = 0;
|
||||
for (i = 0; i < 30; i++) //30*6=180 bits
|
||||
{
|
||||
b = 0;
|
||||
for (j = 0; j < 6; j++)
|
||||
{
|
||||
b = (HBS[i+5] >> (5-j) & 0x1); //+5 to mach our starting position
|
||||
payload[k] = b;
|
||||
// fprintf (stderr, "%d", payload[k]);
|
||||
k++;
|
||||
}
|
||||
|
||||
}
|
||||
return (ec);
|
||||
}
|
||||
|
||||
//I-ISCH Lookup section, borrowed from OP25
|
||||
std::map <std::string, int> isch_map;
|
||||
void map_isch()
|
||||
{
|
||||
isch_map["184229d461"] = 0;
|
||||
isch_map["18761451f6"] = 1;
|
||||
isch_map["181ae27e2f"] = 2;
|
||||
isch_map["182edffbb8"] = 3;
|
||||
isch_map["18df8a7510"] = 4;
|
||||
isch_map["18ebb7f087"] = 5;
|
||||
isch_map["188741df5e"] = 6;
|
||||
isch_map["18b37c5ac9"] = 7;
|
||||
isch_map["1146a44f13"] = 8;
|
||||
isch_map["117299ca84"] = 9;
|
||||
isch_map["111e6fe55d"] = 10;
|
||||
isch_map["112a5260ca"] = 11;
|
||||
isch_map["11db07ee62"] = 12;
|
||||
isch_map["11ef3a6bf5"] = 13;
|
||||
isch_map["1183cc442c"] = 14;
|
||||
isch_map["11b7f1c1bb"] = 15;
|
||||
isch_map["1a4a2e239e"] = 16;
|
||||
isch_map["1a7e13a609"] = 17;
|
||||
isch_map["1a12e589d0"] = 18;
|
||||
isch_map["1a26d80c47"] = 19;
|
||||
isch_map["1ad78d82ef"] = 20;
|
||||
isch_map["1ae3b00778"] = 21;
|
||||
isch_map["1a8f4628a1"] = 22;
|
||||
isch_map["1abb7bad36"] = 23;
|
||||
isch_map["134ea3b8ec"] = 24;
|
||||
isch_map["137a9e3d7b"] = 25;
|
||||
isch_map["13166812a2"] = 26;
|
||||
isch_map["1322559735"] = 27;
|
||||
isch_map["13d300199d"] = 28;
|
||||
isch_map["13e73d9c0a"] = 29;
|
||||
isch_map["138bcbb3d3"] = 30;
|
||||
isch_map["13bff63644"] = 31;
|
||||
isch_map["1442f705ef"] = 32;
|
||||
isch_map["1476ca8078"] = 33;
|
||||
isch_map["141a3cafa1"] = 34;
|
||||
isch_map["142e012a36"] = 35;
|
||||
isch_map["14df54a49e"] = 36;
|
||||
isch_map["14eb692109"] = 37;
|
||||
isch_map["14879f0ed0"] = 38;
|
||||
isch_map["14b3a28b47"] = 39;
|
||||
isch_map["1d467a9e9d"] = 40;
|
||||
isch_map["1d72471b0a"] = 41;
|
||||
isch_map["1d1eb134d3"] = 42;
|
||||
isch_map["1d2a8cb144"] = 43;
|
||||
isch_map["1ddbd93fec"] = 44;
|
||||
isch_map["1defe4ba7b"] = 45;
|
||||
isch_map["1d831295a2"] = 46;
|
||||
isch_map["1db72f1035"] = 47;
|
||||
isch_map["164af0f210"] = 48;
|
||||
isch_map["167ecd7787"] = 49;
|
||||
isch_map["16123b585e"] = 50;
|
||||
isch_map["162606ddc9"] = 51;
|
||||
isch_map["16d7535361"] = 52;
|
||||
isch_map["16e36ed6f6"] = 53;
|
||||
isch_map["168f98f92f"] = 54;
|
||||
isch_map["16bba57cb8"] = 55;
|
||||
isch_map["1f4e7d6962"] = 56;
|
||||
isch_map["1f7a40ecf5"] = 57;
|
||||
isch_map["1f16b6c32c"] = 58;
|
||||
isch_map["1f228b46bb"] = 59;
|
||||
isch_map["1fd3dec813"] = 60;
|
||||
isch_map["1fe7e34d84"] = 61;
|
||||
isch_map["1f8b15625d"] = 62;
|
||||
isch_map["1fbf28e7ca"] = 63;
|
||||
isch_map["84d62c339"] = 64;
|
||||
isch_map["8795f46ae"] = 65;
|
||||
isch_map["815a96977"] = 66;
|
||||
isch_map["82194ece0"] = 67;
|
||||
isch_map["8d0c16248"] = 68;
|
||||
isch_map["8e4fce7df"] = 69;
|
||||
isch_map["8880ac806"] = 70;
|
||||
isch_map["8bc374d91"] = 71;
|
||||
isch_map["149ef584b"] = 72;
|
||||
isch_map["17dd2dddc"] = 73;
|
||||
isch_map["11124f205"] = 74;
|
||||
isch_map["125197792"] = 75;
|
||||
isch_map["1d44cf93a"] = 76;
|
||||
isch_map["1e0717cad"] = 77;
|
||||
isch_map["18c875374"] = 78;
|
||||
isch_map["1b8bad6e3"] = 79;
|
||||
isch_map["a456534c6"] = 80;
|
||||
isch_map["a7158b151"] = 81;
|
||||
isch_map["a1dae9e88"] = 82;
|
||||
isch_map["a29931b1f"] = 83;
|
||||
isch_map["ad8c695b7"] = 84;
|
||||
isch_map["aecfb1020"] = 85;
|
||||
isch_map["a800d3ff9"] = 86;
|
||||
isch_map["ab430ba6e"] = 87;
|
||||
isch_map["341e8afb4"] = 88;
|
||||
isch_map["375d52a23"] = 89;
|
||||
isch_map["3192305fa"] = 90;
|
||||
isch_map["32d1e806d"] = 91;
|
||||
isch_map["3dc4b0ec5"] = 92;
|
||||
isch_map["3e8768b52"] = 93;
|
||||
isch_map["38480a48b"] = 94;
|
||||
isch_map["3b0bd211c"] = 95;
|
||||
isch_map["44dbc12b7"] = 96;
|
||||
isch_map["479819720"] = 97;
|
||||
isch_map["41577b8f9"] = 98;
|
||||
isch_map["4214a3d6e"] = 99;
|
||||
isch_map["4d01fb3c6"] = 100;
|
||||
isch_map["4e4223651"] = 101;
|
||||
isch_map["488d41988"] = 102;
|
||||
isch_map["4bce99c1f"] = 103;
|
||||
isch_map["d493189c5"] = 104;
|
||||
isch_map["d7d0c0c52"] = 105;
|
||||
isch_map["d11fa238b"] = 106;
|
||||
isch_map["d25c7a61c"] = 107;
|
||||
isch_map["dd49228b4"] = 108;
|
||||
isch_map["de0afad23"] = 109;
|
||||
isch_map["d8c5982fa"] = 110;
|
||||
isch_map["db864076d"] = 111;
|
||||
isch_map["645bbe548"] = 112;
|
||||
isch_map["6718660df"] = 113;
|
||||
isch_map["61d704f06"] = 114;
|
||||
isch_map["6294dca91"] = 115;
|
||||
isch_map["6d8184439"] = 116;
|
||||
isch_map["6ec25c1ae"] = 117;
|
||||
isch_map["680d3ee77"] = 118;
|
||||
isch_map["6b4ee6be0"] = 119;
|
||||
isch_map["f41367e3a"] = 120;
|
||||
isch_map["f750bfbad"] = 121;
|
||||
isch_map["f19fdd474"] = 122;
|
||||
isch_map["f2dc051e3"] = 123;
|
||||
isch_map["fdc95df4b"] = 124;
|
||||
isch_map["fe8a85adc"] = 125;
|
||||
isch_map["f845e7505"] = 126;
|
||||
isch_map["fb063f092"] = 127;
|
||||
}
|
||||
|
||||
unsigned long long int isch_lookup (unsigned long long int isch)
|
||||
{
|
||||
map_isch(); //initialize the lookup map
|
||||
char s[64];
|
||||
unsigned long long int decoded = -2; //initialize lookup to an invalid number
|
||||
sprintf(s, "%llx", isch);
|
||||
decoded = isch_map[s];
|
||||
return(decoded);
|
||||
}
|
||||
128
src/ezpwd/asserter
Normal file
128
src/ezpwd/asserter
Normal file
@@ -0,0 +1,128 @@
|
||||
#ifndef _EZPWD_ASSERTER
|
||||
#define _EZPWD_ASSERTER
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
namespace ezpwd {
|
||||
|
||||
#define ISEQUAL( ... ) isequal(__FILE__, __LINE__, __VA_ARGS__ )
|
||||
#define ISTRUE( ... ) istrue( __FILE__, __LINE__, __VA_ARGS__ )
|
||||
#define ISFALSE( ... ) isfalse(__FILE__, __LINE__, __VA_ARGS__ )
|
||||
#define ISNEAR( ... ) isnear( __FILE__, __LINE__, __VA_ARGS__ )
|
||||
#define FAILURE( ... ) failure(__FILE__, __LINE__, __VA_ARGS__ )
|
||||
|
||||
struct asserter {
|
||||
bool failed; // The last test failed
|
||||
int failures; // Total number of failures
|
||||
std::string out; // Last failure
|
||||
|
||||
asserter()
|
||||
: failed( false )
|
||||
, failures( 0 )
|
||||
, out()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
//
|
||||
// output( <std::ostream> ) -- Output description of last failed test (or nothing if successful)
|
||||
// <std::ostream> << <asserter>
|
||||
//
|
||||
std::ostream &output(
|
||||
std::ostream &lhs )
|
||||
const
|
||||
{
|
||||
return lhs << out;
|
||||
}
|
||||
|
||||
//
|
||||
// (bool) <asserter> -- Return status of last test
|
||||
//
|
||||
operator bool()
|
||||
{
|
||||
return failed;
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
asserter &istrue( const char *file, int line, const T &a, const std::string &comment = std::string() )
|
||||
{
|
||||
return isequal( file, line, !!a, true, comment );
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
asserter &isfalse( const char *file, int line, const T &a, const std::string &comment = std::string() )
|
||||
{
|
||||
return isequal( file, line, !!a, false, comment );
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
asserter &isequal( const char *file, int line, const T &a, const T &b, const std::string &comment = std::string() )
|
||||
{
|
||||
if ( ! ( a == b )) {
|
||||
std::ostringstream oss;
|
||||
oss << a << " != " << b;
|
||||
return failure( file, line, oss.str(), comment );
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
asserter &isnear( const char *file, int line, const T &a, const T &b, const T &delta, const std::string &comment = std::string() )
|
||||
{
|
||||
T difference;
|
||||
difference = ( a < b
|
||||
? T( b - a )
|
||||
: T( a - b ));
|
||||
if ( ! ( difference < ( delta < T( 0 ) ? T( -delta ) : T( delta )))) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setprecision( 13 ) << a << " != " << b << " +/- " << delta;
|
||||
return failure( file, line, oss.str(), comment );
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
asserter &failure( const char *file, int line, const std::string &comparison,
|
||||
const std::string &comment = std::string() )
|
||||
{
|
||||
++failures;
|
||||
const char *needle = "/";
|
||||
const char *slash = std::find_end( file, file + strlen( file ),
|
||||
needle, needle + strlen( needle ));
|
||||
if ( slash == file + strlen( file ))
|
||||
slash = file;
|
||||
else
|
||||
slash += 1;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss
|
||||
<< std::setw( 24 ) << slash << ", "
|
||||
<< std::setw( -5 ) << line
|
||||
<< "; FAILURE: " << comparison
|
||||
<< ( comment.size() ? ": " : "" ) << comment
|
||||
<< std::endl;
|
||||
out = oss.str();
|
||||
failed = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
asserter &success()
|
||||
{
|
||||
out.clear();
|
||||
failed = false;
|
||||
return *this;
|
||||
}
|
||||
}; // class asserter
|
||||
} // namespace ezpwd
|
||||
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
ezpwd::asserter &rhs )
|
||||
{
|
||||
return rhs.output( lhs );
|
||||
}
|
||||
|
||||
#endif // _EZPWD_ARRAY
|
||||
485
src/ezpwd/bch
Normal file
485
src/ezpwd/bch
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
|
||||
*
|
||||
* Copyright (c) 2017, Hard Consulting Corporation.
|
||||
*
|
||||
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
|
||||
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. See the LICENSE file at the top of the
|
||||
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. The Djelic BCH code
|
||||
* under djelic/ and the c++/ezpwd/bch_base wrapper is redistributed under the terms of the GPLv2+,
|
||||
* regardless of the overall licensing terms.
|
||||
*
|
||||
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||
* the GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _EZPWD_BCH
|
||||
#define _EZPWD_BCH
|
||||
|
||||
#include <sstream>
|
||||
#include "rs_base" // Basic DEBUG, EZPWD_... preprocessor stuff, ezpwd::log_, etc.
|
||||
#include "bch_base"
|
||||
|
||||
namespace ezpwd {
|
||||
//
|
||||
// ezpwd::bch_base -- Interface to underlying Djelic Linux Kernel API
|
||||
// ezpwd::bch<SYMBOLS, CORRECTION> -- General BCH codec types; min. CORRECTION capacity, undefined PAYLOAD
|
||||
//
|
||||
// These implementations retain the original Djelic Linux Kernel API; specifically, they find
|
||||
// a BCH codec of the given Galois order M (ie. has codewords of size 2**M-1), and at least the
|
||||
// target bit-error correction capacity T. They may correct more than T errors, and the number
|
||||
// of parity ECC bits will be selected by the algorithm. You need to compute the maximum
|
||||
// non-parity payload by computing _bch->n - _bch->ecc_bits.
|
||||
//
|
||||
// The data and parity bits must always be on separate blocks of int8_t/uint8_t-sized data
|
||||
// in the container. This is required because the underlying API must break the data and parity
|
||||
// out as separate arrays for processing. So, if the computed ecc_bits is not evenly divisible
|
||||
// by 8, some care must be taken to ensure that it is packed into exactly ecc_bytes of data at
|
||||
// the end of the supplied container. Alternatively, it can be kept in a separate container.
|
||||
// This is probably safest, as the bch_base/bch/BCH classes will never attempt to resize the
|
||||
// data/parity containers when supplied separately.
|
||||
//
|
||||
// Like the Reed-Solomon APIs, the bch_base/bch/BCH APIs will alter the size of a variable
|
||||
// container in encode(...), to add the BCH ECC "parity" data (eg. std::vector, std::string).
|
||||
// Fixed containers (eg. std::array) are never resized, and it is assumed that ecc_bytes of
|
||||
// parity data exist at the end of the container.
|
||||
//
|
||||
class bch_base {
|
||||
public:
|
||||
ezpwd::bch_control *_bch;
|
||||
|
||||
bch_base( const bch_base & ) = delete; // no copy-constructor
|
||||
|
||||
bch_base(
|
||||
size_t m,
|
||||
size_t t,
|
||||
unsigned int prim_poly = 0 )
|
||||
: _bch( ezpwd::init_bch( int( m ), int( t ), prim_poly ))
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
virtual ~bch_base()
|
||||
{
|
||||
ezpwd::free_bch( this->_bch );
|
||||
}
|
||||
|
||||
size_t ecc_bytes()
|
||||
const
|
||||
{
|
||||
return _bch->ecc_bytes;
|
||||
}
|
||||
size_t ecc_bits()
|
||||
const
|
||||
{
|
||||
return _bch->ecc_bits;
|
||||
}
|
||||
|
||||
size_t t()
|
||||
const
|
||||
{
|
||||
return _bch->t;
|
||||
}
|
||||
|
||||
//
|
||||
// <ostream> << bch_base -- output codec in standard BCH( N, N-ECC, T ) form
|
||||
//
|
||||
virtual std::ostream &output(
|
||||
std::ostream &lhs )
|
||||
const
|
||||
{
|
||||
return lhs << *this->_bch;
|
||||
}
|
||||
|
||||
//
|
||||
// encode -- container interfaces
|
||||
//
|
||||
// Returns number of ECC *bits* initialized (to be consistent w/ decode, which returns
|
||||
// number of bit errors corrected).
|
||||
//
|
||||
int encode(
|
||||
std::string &data )
|
||||
const
|
||||
{
|
||||
typedef uint8_t uT;
|
||||
typedef std::pair<uT *, uT *>
|
||||
uTpair;
|
||||
data.resize( data.size() + ecc_bytes() );
|
||||
return encode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ));
|
||||
}
|
||||
|
||||
int encode(
|
||||
const std::string &data,
|
||||
std::string &parity )
|
||||
const
|
||||
{
|
||||
typedef uint8_t uT;
|
||||
typedef std::pair<const uT *, const uT *>
|
||||
cuTpair;
|
||||
typedef std::pair<uT *, uT *>
|
||||
uTpair;
|
||||
parity.resize( ecc_bytes() );
|
||||
return encode( cuTpair( (const uT *)&data.front(), (const uT *)&data.front() + data.size() ),
|
||||
uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() ));
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
int encode(
|
||||
std::vector<T> &data )
|
||||
const
|
||||
{
|
||||
typedef typename std::make_unsigned<T>::type
|
||||
uT;
|
||||
typedef std::pair<uT *, uT *>
|
||||
uTpair;
|
||||
data.resize( data.size() + ecc_bytes() );
|
||||
return encode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ));
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
int encode(
|
||||
const std::vector<T>&data,
|
||||
std::vector<T> &parity )
|
||||
const
|
||||
{
|
||||
typedef typename std::make_unsigned<T>::type
|
||||
uT;
|
||||
typedef std::pair<const uT *, const uT *>
|
||||
cuTpair;
|
||||
typedef std::pair<uT *, uT *>
|
||||
uTpair;
|
||||
parity.resize( ecc_bytes() );
|
||||
return encode( cuTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ),
|
||||
uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() ));
|
||||
}
|
||||
|
||||
template < typename T, size_t N >
|
||||
int encode(
|
||||
std::array<T,N> &data,
|
||||
int pad = 0 ) // ignore 'pad' symbols at start of array
|
||||
const
|
||||
{
|
||||
typedef typename std::make_unsigned<T>::type
|
||||
uT;
|
||||
typedef std::pair<uT *, uT *>
|
||||
uTpair;
|
||||
return encode( uTpair( (uT *)&data.front() + pad, (uT *)&data.front() + data.size() ));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// encode -- denote data+parity or data, parity using pairs of uint8_t iterators
|
||||
// encode -- base implementation, in terms of uint8_t pointers
|
||||
//
|
||||
virtual int encode(
|
||||
const std::pair<uint8_t *, uint8_t *>
|
||||
&data )
|
||||
const
|
||||
{
|
||||
return encode( data.first, data.second - data.first - ecc_bytes(), data.second - ecc_bytes() );
|
||||
}
|
||||
|
||||
virtual int encode(
|
||||
const std::pair<const uint8_t *, const uint8_t *>
|
||||
&data,
|
||||
const std::pair<uint8_t *, uint8_t *>
|
||||
&parity )
|
||||
const
|
||||
{
|
||||
if ( size_t( parity.second - parity.first ) != ecc_bytes() ) {
|
||||
EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: parity length incompatible with number of ECC bytes", -1 );
|
||||
}
|
||||
return encode( data.first, data.second - data.first, parity.first );
|
||||
}
|
||||
|
||||
virtual int encode(
|
||||
const uint8_t *data,
|
||||
size_t len,
|
||||
uint8_t *parity )
|
||||
const
|
||||
{
|
||||
memset( parity, 0, ecc_bytes() ); // Djelic encode_bch requires ECC to be initialized to 0
|
||||
ezpwd::encode_bch( this->_bch, data, len, parity );
|
||||
return int( ecc_bits() );
|
||||
}
|
||||
|
||||
//
|
||||
// decode -- container interface, w/ optional corrected bit-error positions reported
|
||||
//
|
||||
// Does not correct errors in parity!
|
||||
//
|
||||
int decode(
|
||||
std::string &data,
|
||||
std::vector<int> *position= 0 )
|
||||
const
|
||||
{
|
||||
typedef uint8_t uT;
|
||||
typedef std::pair<uT *, uT *>
|
||||
uTpair;
|
||||
return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ),
|
||||
position );
|
||||
}
|
||||
|
||||
int decode(
|
||||
std::string &data,
|
||||
std::string &parity,
|
||||
std::vector<int> *position= 0 )
|
||||
const
|
||||
{
|
||||
typedef uint8_t uT;
|
||||
typedef std::pair<uT *, uT *>
|
||||
uTpair;
|
||||
return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ),
|
||||
uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() ),
|
||||
position );
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
int decode(
|
||||
std::vector<T> &data,
|
||||
std::vector<int> *position= 0 )
|
||||
const
|
||||
{
|
||||
typedef typename std::make_unsigned<T>::type
|
||||
uT;
|
||||
typedef std::pair<uT *, uT *>
|
||||
uTpair;
|
||||
return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ),
|
||||
position );
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
int decode(
|
||||
std::vector<T> &data,
|
||||
std::vector<T> &parity,
|
||||
std::vector<int> *position= 0 )
|
||||
const
|
||||
{
|
||||
typedef typename std::make_unsigned<T>::type
|
||||
uT;
|
||||
typedef std::pair<uT *, uT *>
|
||||
uTpair;
|
||||
return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ),
|
||||
uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() ),
|
||||
position );
|
||||
}
|
||||
|
||||
template < typename T, size_t N >
|
||||
int decode(
|
||||
std::array<T,N> &data,
|
||||
int pad = 0, // ignore 'pad' symbols at start of array
|
||||
std::vector<int> *position= 0 )
|
||||
const
|
||||
{
|
||||
typedef typename std::make_unsigned<T>::type
|
||||
uT;
|
||||
typedef std::pair<uT *, uT *>
|
||||
uTpair;
|
||||
return decode( uTpair( (uT *)&data.front() + pad, (uT *)&data.front() + data.size() ),
|
||||
position );
|
||||
}
|
||||
|
||||
//
|
||||
// decode -- denote data+parity or data, parity using pairs of uint8_t iterators
|
||||
// decode -- decode and correct BCH codeword, returning number of corrections, or -1 if failed
|
||||
//
|
||||
// Corrects data in-place (unlike the basic Djelic Linux Kernel API, which only returns
|
||||
// error positions. For consistency with ezpwd::rs..., we report all error positions as
|
||||
// std::vector<int>, even though the underlying Djelic API reports them as arrays of
|
||||
// unsigned int.
|
||||
//
|
||||
virtual int decode(
|
||||
const std::pair<uint8_t *, uint8_t *>
|
||||
&data,
|
||||
std::vector<int> *position= 0 )
|
||||
const
|
||||
{
|
||||
return decode( data.first, data.second - data.first - ecc_bytes(), data.second - ecc_bytes(),
|
||||
position );
|
||||
}
|
||||
|
||||
virtual int decode(
|
||||
const std::pair<uint8_t *, uint8_t *>
|
||||
&data,
|
||||
const std::pair<uint8_t *, uint8_t *>
|
||||
&parity,
|
||||
std::vector<int> *position= 0 )
|
||||
const
|
||||
{
|
||||
if ( size_t( parity.second - parity.first ) != ecc_bytes() ) {
|
||||
EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: parity length incompatible with number ECC bytes", -1 );
|
||||
}
|
||||
return decode( data.first, data.second - data.first, parity.first,
|
||||
position );
|
||||
}
|
||||
virtual int decode(
|
||||
uint8_t *data,
|
||||
size_t len,
|
||||
uint8_t *parity,
|
||||
std::vector<int> *position= 0 )
|
||||
const
|
||||
{
|
||||
if ( position )
|
||||
position->resize( t() * 2 ); // may be able to correct beyond stated capacity!
|
||||
int corrects = ezpwd::correct_bch(
|
||||
this->_bch, data, len, parity, 0, 0,
|
||||
position ? (unsigned int *)&(*position)[0] : 0 );
|
||||
if ( position && corrects >= 0 )
|
||||
position->resize( corrects );
|
||||
return corrects;
|
||||
}
|
||||
|
||||
//
|
||||
// {en,de}coded -- returns an encoded/corrected copy of the provided container
|
||||
//
|
||||
// NOTE:
|
||||
//
|
||||
// Must return exceptions on failure; If exceptions inhibited, returns a
|
||||
// default-constructed instance of the supplied data container. This may be sufficient to
|
||||
// reliably deduce failure; if not, this interface should not be used.
|
||||
//
|
||||
// Overloads decoded to also allow recovery of corrected error positions and count.
|
||||
//
|
||||
template <typename C>
|
||||
C encoded(
|
||||
C data )
|
||||
const
|
||||
{
|
||||
if ( encode( data ) < 0 )
|
||||
EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: Could not encode data", C() );
|
||||
return data;
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
C decoded(
|
||||
C data )
|
||||
const
|
||||
{
|
||||
if ( decode( data ) < 0 )
|
||||
EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: Could not decode data", C() );
|
||||
return data;
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
C decoded(
|
||||
C data,
|
||||
std::vector<int> &position )
|
||||
const
|
||||
{
|
||||
if ( decode( data, &position ) < 0 )
|
||||
EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: Could not decode data", C() );
|
||||
return data;
|
||||
}
|
||||
|
||||
}; // class bch_base
|
||||
|
||||
template < size_t SYMBOLS, size_t CORRECTION >
|
||||
class bch
|
||||
: public bch_base
|
||||
{
|
||||
public:
|
||||
bch()
|
||||
: bch_base( ezpwd::log_<SYMBOLS + 1>::value, CORRECTION )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
virtual ~bch()
|
||||
{
|
||||
;
|
||||
}
|
||||
}; // class bch
|
||||
|
||||
//
|
||||
// std::ostream << ezpwd::bch_base
|
||||
//
|
||||
// Output a BCH codec description in standard form eg. BCH( 255, 239, 2 )
|
||||
//
|
||||
inline
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const ezpwd::bch_base
|
||||
&rhs )
|
||||
{
|
||||
return rhs.output( lhs );
|
||||
}
|
||||
|
||||
//
|
||||
// ezpwd::BCH<SYMBOLS, PAYLOAD, CAPACITY> -- Standard BCH codec types
|
||||
//
|
||||
// Specify and create a standard BCH codec with exactly the specified capacities. We create
|
||||
// the undering BCH codec using SYMBOLS and CORRECTION capacity; the actual correction capacity
|
||||
// T, the number of PARITY bits and hence PAYLOAD (CAPACITY - PARITY) is selected automatically
|
||||
// by the underlying Djelic Linux Kernel BCH codec API. For this interface, we demand that the
|
||||
// caller *knows* all of these values at compile time, however, mostly for future optimization
|
||||
// purposes. We validate them, and fail the constructor if they don't match. See bch_test for
|
||||
// an enumeration of all possible BCH codecs.
|
||||
//
|
||||
// In the future, this API may be re-implemented to not use the generic BCH API, but a more
|
||||
// optimized locally-defined implementation that leverages the fixed SYMBOLS, PAYLOAD and
|
||||
// CORRECTION capacities to produce more optimal code.
|
||||
//
|
||||
template < size_t SYMBOLS, size_t PAYLOAD, size_t CORRECTION >
|
||||
class BCH
|
||||
: public bch<SYMBOLS, CORRECTION>
|
||||
{
|
||||
public:
|
||||
static const size_t M = ezpwd::log_<SYMBOLS + 1>::value; // Galois field order; eg. 255 --> 8
|
||||
static const size_t N = SYMBOLS;
|
||||
static const size_t T = CORRECTION;
|
||||
static const size_t LOAD = PAYLOAD;
|
||||
|
||||
BCH()
|
||||
: bch<SYMBOLS, CORRECTION>()
|
||||
{
|
||||
if ( this->_bch->t != T || this->_bch->n != N
|
||||
|| this->_bch->n - this->_bch->ecc_bits != LOAD ) {
|
||||
std::ostringstream err;
|
||||
this->output( err )
|
||||
<< " specified doesn't match underlying " << *this->_bch << " produced.";
|
||||
EZPWD_RAISE_OR_ABORT( std::runtime_error, err.str().c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~BCH()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
//
|
||||
// <ostream> << BCH<...> -- output codec in standard BCH( N, N-ECC, T ) form
|
||||
//
|
||||
virtual std::ostream &output(
|
||||
std::ostream &lhs )
|
||||
const
|
||||
{
|
||||
return lhs
|
||||
<< "BCH( " << std::setw( 3 ) << N
|
||||
<< ", " << std::setw( 3 ) << LOAD
|
||||
<< ", " << std::setw( 3 ) << T
|
||||
<< " )";
|
||||
}
|
||||
}; // class BCH
|
||||
|
||||
|
||||
//
|
||||
// std::ostream << ezpwd::BCH<...>
|
||||
//
|
||||
// Output a BCH codec description in standard form eg. BCH( 255, 239, 2 )
|
||||
//
|
||||
// NOTE: clang/gcc disagree on the scoping of operator<< template/non-template functions...
|
||||
//
|
||||
template <size_t SYMBOLS, size_t PAYLOAD, size_t CORRECTION>
|
||||
inline
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const ezpwd::BCH<SYMBOLS, PAYLOAD, CORRECTION>
|
||||
&rhs )
|
||||
{
|
||||
return rhs.output( lhs );
|
||||
}
|
||||
|
||||
} // namespace ezpwd
|
||||
|
||||
#endif // _EZPWD_BCH
|
||||
219
src/ezpwd/bch_base
Normal file
219
src/ezpwd/bch_base
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
|
||||
*
|
||||
* Copyright (c) 2017, Hard Consulting Corporation.
|
||||
*
|
||||
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
|
||||
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. See the LICENSE file at the top of the
|
||||
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. The Djelic BCH code
|
||||
* under djelic/ and the c++/ezpwd/bch_base wrapper is redistributed under the terms of the GPLv2+,
|
||||
* regardless of the overall licensing terms.
|
||||
*
|
||||
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||
* the GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _EZPWD_BCH_BASE
|
||||
#define _EZPWD_BCH_BASE
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
|
||||
//
|
||||
// Presently, we simply import the Linux Kernel's "C" BCH API directly into the ezpwd:: namespace In
|
||||
// order to compile, you must (at least) run "make djelic" in the base directory of the
|
||||
// https://github.com/pjkundert/bch.git repo.
|
||||
//
|
||||
namespace ezpwd {
|
||||
/**
|
||||
* struct bch_control - BCH control structure
|
||||
* @m: Galois field order
|
||||
* @n: maximum codeword size in bits (= 2^m-1)
|
||||
* @t: error correction capability in bits
|
||||
* @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t)
|
||||
* @ecc_bytes: ecc max size (m*t bits) in bytes
|
||||
* @a_pow_tab: Galois field GF(2^m) exponentiation lookup table
|
||||
* @a_log_tab: Galois field GF(2^m) log lookup table
|
||||
* @mod8_tab: remainder generator polynomial lookup tables
|
||||
* @ecc_buf: ecc parity words buffer
|
||||
* @ecc_buf2: ecc parity words buffer
|
||||
* @xi_tab: GF(2^m) base for solving degree 2 polynomial roots
|
||||
* @syn: syndrome buffer
|
||||
* @cache: log-based polynomial representation buffer
|
||||
* @elp: error locator polynomial
|
||||
* @poly_2t: temporary polynomials of degree 2t
|
||||
*/
|
||||
|
||||
/**
|
||||
* init_bch - initialize a BCH encoder/decoder
|
||||
* @m: Galois field order, should be in the range 5-15
|
||||
* @t: maximum error correction capability, in bits
|
||||
* @prim_poly: user-provided primitive polynomial (or 0 to use default)
|
||||
*
|
||||
* Returns:
|
||||
* a newly allocated BCH control structure if successful, NULL otherwise
|
||||
*
|
||||
* This initialization can take some time, as lookup tables are built for fast
|
||||
* encoding/decoding; make sure not to call this function from a time critical
|
||||
* path. Usually, init_bch() should be called on module/driver init and
|
||||
* free_bch() should be called to release memory on exit.
|
||||
*
|
||||
* You may provide your own primitive polynomial of degree @m in argument
|
||||
* @prim_poly, or let init_bch() use its default polynomial.
|
||||
*
|
||||
* Once init_bch() has successfully returned a pointer to a newly allocated
|
||||
* BCH control structure, ecc length in bytes is given by member @ecc_bytes of
|
||||
* the structure.
|
||||
*/
|
||||
|
||||
/**
|
||||
* encode_bch - calculate BCH ecc parity of data
|
||||
* @bch: BCH control structure
|
||||
* @data: data to encode
|
||||
* @len: data length in bytes
|
||||
* @ecc: ecc parity data, must be initialized by caller
|
||||
*
|
||||
* The @ecc parity array is used both as input and output parameter, in order to
|
||||
* allow incremental computations. It should be of the size indicated by member
|
||||
* @ecc_bytes of @bch, and should be initialized to 0 before the first call.
|
||||
*
|
||||
* The exact number of computed ecc parity bits is given by member @ecc_bits of
|
||||
* @bch; it may be less than m*t for large values of t.
|
||||
*/
|
||||
|
||||
/**
|
||||
* decode_bch - decode received codeword and find bit error locations
|
||||
* @bch: BCH control structure
|
||||
* @data: received data, ignored if @calc_ecc is provided
|
||||
* @len: data length in bytes, must always be provided
|
||||
* @recv_ecc: received ecc, if NULL then assume it was XORed in @calc_ecc
|
||||
* @calc_ecc: calculated ecc, if NULL then calc_ecc is computed from @data
|
||||
* @syn: hw computed syndrome data (if NULL, syndrome is calculated)
|
||||
* @errloc: output array of error locations
|
||||
*
|
||||
* Returns:
|
||||
* The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if
|
||||
* invalid parameters were provided
|
||||
*
|
||||
* Depending on the available hw BCH support and the need to compute @calc_ecc
|
||||
* separately (using encode_bch()), this function should be called with one of
|
||||
* the following parameter configurations -
|
||||
*
|
||||
* by providing @data and @recv_ecc only:
|
||||
* decode_bch(@bch, @data, @len, @recv_ecc, NULL, NULL, @errloc)
|
||||
*
|
||||
* by providing @recv_ecc and @calc_ecc:
|
||||
* decode_bch(@bch, NULL, @len, @recv_ecc, @calc_ecc, NULL, @errloc)
|
||||
*
|
||||
* by providing ecc = recv_ecc XOR calc_ecc:
|
||||
* decode_bch(@bch, NULL, @len, NULL, ecc, NULL, @errloc)
|
||||
*
|
||||
* by providing syndrome results @syn:
|
||||
* decode_bch(@bch, NULL, @len, NULL, NULL, @syn, @errloc)
|
||||
*
|
||||
* Once decode_bch() has successfully returned with a positive value, error
|
||||
* locations returned in array @errloc should be interpreted as follows -
|
||||
*
|
||||
* if (errloc[n] >= 8*len), then n-th error is located in ecc (no need for
|
||||
* data correction)
|
||||
*
|
||||
* if (errloc[n] < 8*len), then n-th error is located in data and can be
|
||||
* corrected with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8);
|
||||
*
|
||||
* Note that this function does not perform any data correction by itself, it
|
||||
* merely indicates error locations.
|
||||
*/
|
||||
|
||||
/**
|
||||
* init_bch - initialize a BCH encoder/decoder
|
||||
* @m: Galois field order, should be in the range 5-15
|
||||
* @t: maximum error correction capability, in bits
|
||||
* @prim_poly: user-provided primitive polynomial (or 0 to use default)
|
||||
*
|
||||
* Returns:
|
||||
* a newly allocated BCH control structure if successful, NULL otherwise
|
||||
*
|
||||
* This initialization can take some time, as lookup tables are built for fast
|
||||
* encoding/decoding; make sure not to call this function from a time critical
|
||||
* path. Usually, init_bch() should be called on module/driver init and
|
||||
* free_bch() should be called to release memory on exit.
|
||||
*
|
||||
* You may provide your own primitive polynomial of degree @m in argument
|
||||
* @prim_poly, or let init_bch() use its default polynomial.
|
||||
*
|
||||
* Once init_bch() has successfully returned a pointer to a newly allocated
|
||||
* BCH control structure, ecc length in bytes is given by member @ecc_bytes of
|
||||
* the structure.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include "../../djelic_bch.h"
|
||||
}
|
||||
|
||||
//
|
||||
// correct_bch -- corrects data (but not parity!), as suggested by decode_bch, above
|
||||
//
|
||||
// A convenience interface that defaults all of the not strictly required parameters, and
|
||||
// automatically corrects bit-errors in data *and* the supplied parity. Does not attempt to
|
||||
// correct bit errors found in the parity data. If not supplied, 'errloc' is allocated
|
||||
// internally; otherwise, it is assumed to be of at least size bch->t (the minimum error
|
||||
// correction capacity of the BCH codec).
|
||||
//
|
||||
// However, beware -- at larger values of T, the actual correction capacity of the BCH codec
|
||||
// could be greater than the requested T. Therefore, it is recommended that you always supply a
|
||||
// larger than required errloc array; recommend T*2?
|
||||
//
|
||||
inline
|
||||
int correct_bch(
|
||||
struct bch_control *bch,
|
||||
uint8_t *data,
|
||||
unsigned int len,
|
||||
uint8_t *recv_ecc,
|
||||
const uint8_t *calc_ecc= 0,
|
||||
const unsigned int *syn = 0,
|
||||
unsigned int *errloc = 0 ) // must be sized at least bch->t; often, greater!
|
||||
{
|
||||
unsigned int _errloc[511]; // much larger than the correction capacity of largest supported BCH codec
|
||||
if ( ! errloc )
|
||||
errloc = _errloc;
|
||||
int err = decode_bch( bch, data, len, recv_ecc, calc_ecc, syn, errloc );
|
||||
if ( err > 0 ) {
|
||||
// A +'ve number of bit-error correction locations were found
|
||||
for ( int n = 0; n < err; ++n ) {
|
||||
/**
|
||||
* if (errloc[n] < 8*len), then n-th error is located in data and can be corrected
|
||||
* with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8). If in the parity, it
|
||||
* is assumed to be located at the end of the data, so offset by 'len' bytes.
|
||||
*/
|
||||
if ( errloc[n] < 8*len ) {
|
||||
data[errloc[n] / 8] ^= 1 << ( errloc[n] % 8 );
|
||||
} else if ( recv_ecc && errloc[n] < 8 * len + 8 * bch->ecc_bytes ) {
|
||||
recv_ecc[errloc[n] / 8 - len]
|
||||
^= 1 << ( errloc[n] % 8 );
|
||||
}
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
//
|
||||
// <ostream> << <ezpwd::bch_control> -- output codec in standard BCH( N, N-ECC, T ) form
|
||||
//
|
||||
inline
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const ezpwd::bch_control
|
||||
&bch )
|
||||
{
|
||||
return lhs
|
||||
<< "BCH( " << std::setw( 3 ) << bch.n
|
||||
<< ", " << std::setw( 3 ) << bch.n - bch.ecc_bits
|
||||
<< ", " << std::setw( 3 ) << bch.t
|
||||
<< " )";
|
||||
}
|
||||
|
||||
} // namespace ezpwd
|
||||
|
||||
#endif // _EZPWD_BCH_BASE
|
||||
506
src/ezpwd/corrector
Normal file
506
src/ezpwd/corrector
Normal file
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
|
||||
*
|
||||
* Copyright (c) 2014, Hard Consulting Corporation.
|
||||
*
|
||||
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
|
||||
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. See the LICENSE file at the top of the
|
||||
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. c++/ezpwd/rs_base
|
||||
* is redistributed under the terms of the LGPL, regardless of the overall licensing terms.
|
||||
*
|
||||
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||
* the GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _EZPWD_CORRECTOR
|
||||
#define _EZPWD_CORRECTOR
|
||||
|
||||
#include "rs"
|
||||
#include "serialize"
|
||||
|
||||
namespace ezpwd {
|
||||
|
||||
//
|
||||
// best_avg -- collect <password>,<confidence> guesses, and return the unambiguous best one
|
||||
//
|
||||
typedef std::map<std::string, std::pair<int, int>> // (<password>, (<count>, <avgsum>))
|
||||
best_avg_base_t;
|
||||
class best_avg
|
||||
: public best_avg_base_t
|
||||
{
|
||||
public:
|
||||
using best_avg_base_t::begin;
|
||||
using best_avg_base_t::end;
|
||||
using best_avg_base_t::insert;
|
||||
using best_avg_base_t::find;
|
||||
using best_avg_base_t::iterator;
|
||||
using best_avg_base_t::const_iterator;
|
||||
using best_avg_base_t::value_type;
|
||||
using best_avg_base_t::mapped_type;
|
||||
//
|
||||
// add -- add the given pct to the current average for <string> str
|
||||
//
|
||||
iterator add(
|
||||
const std::string &str,
|
||||
int pct )
|
||||
{
|
||||
iterator i = find( str );
|
||||
if ( i == end() )
|
||||
i = insert( i, value_type( str, mapped_type() ));
|
||||
i->second.first += 1;
|
||||
i->second.second += pct;
|
||||
return i;
|
||||
}
|
||||
|
||||
//
|
||||
// best -- return the unambiguously best value (average is >, or == but longer), or end()
|
||||
//
|
||||
const_iterator best()
|
||||
const
|
||||
{
|
||||
const_iterator top = end();
|
||||
bool uni = false;
|
||||
for ( const_iterator i = begin(); i != end(); ++i ) {
|
||||
if ( top == end()
|
||||
or i->second.second/i->second.first > top->second.second/top->second.first
|
||||
or ( i->second.second/i->second.first == top->second.second/top->second.first
|
||||
and i->first.size() > top->first.size())) {
|
||||
top = i;
|
||||
uni = true;
|
||||
} else if ( i->second.second/i->second.first == top->second.second/top->second.first
|
||||
and i->first.size() == top->first.size()) {
|
||||
uni = false;
|
||||
}
|
||||
}
|
||||
return uni ? top : end();
|
||||
}
|
||||
|
||||
//
|
||||
// evaluation -- process a (<password>,(<count>,<avgsum>)) into (<average>,<password>)
|
||||
// sort -- return a multimap indexed by <average> --> <string>
|
||||
// output -- output the <string>: <average>, sorted by average
|
||||
//
|
||||
static std::pair<const int,const std::string &>
|
||||
evaluation( const value_type &val )
|
||||
{
|
||||
return std::pair<const int,const std::string &>( val.second.second/val.second.first, val.first );
|
||||
}
|
||||
typedef std::multimap<const int,const std::string &>
|
||||
sorted_t;
|
||||
sorted_t sort()
|
||||
const
|
||||
{
|
||||
sorted_t dst;
|
||||
std::transform( begin(), end(), std::inserter( dst, dst.begin() ), evaluation );
|
||||
return dst;
|
||||
}
|
||||
std::ostream &output(
|
||||
std::ostream &lhs )
|
||||
const
|
||||
{
|
||||
for ( auto i : sort() )
|
||||
lhs << std::setw( 16 ) << i.second
|
||||
<< ": " << std::setw( 3 ) << i.first
|
||||
<< std::endl;
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
} // namespace ezpwd
|
||||
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const ezpwd::best_avg &rhs )
|
||||
{
|
||||
return rhs.output( lhs );
|
||||
}
|
||||
|
||||
namespace ezpwd {
|
||||
//
|
||||
// ezpwd::corrector -- Apply statistical corrections to a string, returning the confidence
|
||||
//
|
||||
// All methods are static; no instance is required, as this is primarily used to create
|
||||
// external language APIs.
|
||||
//
|
||||
template <
|
||||
size_t PARITY,
|
||||
size_t N = 64,
|
||||
typename SERIAL = serialize::base< N, serialize::ezpwd< N >>>
|
||||
class corrector {
|
||||
public:
|
||||
static
|
||||
std::ostream &output(
|
||||
std::ostream &lhs )
|
||||
{
|
||||
lhs << "corrector<PARITY=" << PARITY << ",N=" << N << ",SERIAL=" << SERIAL() << ">";
|
||||
return lhs;
|
||||
}
|
||||
|
||||
//
|
||||
// parity(<string>) -- Returns 'PARITY' base-N symbols of R-S parity to the supplied password
|
||||
//
|
||||
static std::string parity(
|
||||
const std::string &password )
|
||||
{
|
||||
std::string parity;
|
||||
rscodec.encode( password, parity );
|
||||
SERIAL::encode( parity );
|
||||
return parity;
|
||||
}
|
||||
|
||||
//
|
||||
// encode(<string>) -- append PARITY base-N parity symbols to password
|
||||
//
|
||||
// The supplied password buffer size must be sufficient to contain PARITY additional
|
||||
// symbols, plus the terminating NUL. Returns the resultant encoded password size
|
||||
// (excluding the NUL).
|
||||
//
|
||||
static size_t encode(
|
||||
std::string &password )
|
||||
{
|
||||
password += parity( password );
|
||||
return password.size();
|
||||
}
|
||||
|
||||
static size_t encode(
|
||||
char *password,
|
||||
size_t size ) // maximum available size
|
||||
{
|
||||
size_t len = ::strlen( password ); // length w/o terminating NUL
|
||||
if ( len + PARITY + 1 > size )
|
||||
throw std::runtime_error( "ezpwd::rspwd::encode password buffer has insufficient capacity" );
|
||||
std::string par = parity( std::string( password, password + len ));
|
||||
if ( par.size() != PARITY )
|
||||
throw std::runtime_error( "ezpwd::rspwd::encode computed parity with incorrect size" );
|
||||
std::copy( par.begin(), par.end(), password + len );
|
||||
len += PARITY;
|
||||
password[len] = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
//
|
||||
// decode(<string>[,...]) -- Applies R-S error correction on the encoded string, removing parity
|
||||
//
|
||||
// Up to 'PARITY' Reed-Solomon parity symbols are examined, to determine if the supplied
|
||||
// string is a valid R-S codeword and hence very likely to be correct. Optionally supply a
|
||||
// vector of erasure positions.
|
||||
//
|
||||
// An optional 'minimum' final password length may be provided; no R-S parity is assumed
|
||||
// to exist in the first 'minimum' password characters (default: PARITY). This prevents
|
||||
// accidentally finding valid R-S codewords in passwords of known minimum length; validation
|
||||
// codes, for example. Likewise, the optional 'maximum' allows us to limit the number of
|
||||
// parity symbols that may be assumed to be missing from the end of the codeword.
|
||||
//
|
||||
// Returns a confidence strength rating, which is the ratio:
|
||||
//
|
||||
// 100 - ( errors * 2 + erasures ) * 100 / parity
|
||||
//
|
||||
// if an R-S codeword was solved, and 0 otherwise. If a codeword is solved, but the number
|
||||
// of errors and erasures corrected indicates that all parity was consumed, the caller may
|
||||
// opt to not use the corrected string, because there is a chance that our R-S polynomial
|
||||
// was overwhelmed with errors and actually returned an incorrect codeword. Therefore,
|
||||
// solving a codeword using all available parity results in 100 - PARITY * 100 / PARITY ==
|
||||
// 0, which indicates that there is no certainty of correctness; all R-S parity resources
|
||||
// were used in error/erasure recover, with none left to confirm that the result is actually
|
||||
// correct. If only zero-strength results are achieved, the longest will be returned (the
|
||||
// full, original string).
|
||||
//
|
||||
// Supports the following forms of error/erasure:
|
||||
//
|
||||
// 0) Full parity. All data and parity supplied, and an R-S codeword is solved.
|
||||
//
|
||||
// 1) Partial parity. All data and some parity supplied; remainder are deemed erasures.
|
||||
//
|
||||
// If PARITY > 2, then up to PARITY/2-1 trailing parity terms are marked as erasures.
|
||||
// If the R-S codeword is solved and a safe number of errors are found, then we can have
|
||||
// reasonable confidence that the string is correct.
|
||||
//
|
||||
// 1a) Erase errors. Permute the combinations of up to PARITY-1 erasures.
|
||||
//
|
||||
// o) Raw password. No parity terms supplied; not an R-S codeword
|
||||
//
|
||||
// If none of the error/erasure forms succeed, the password is returned unmodified.
|
||||
//
|
||||
// If a non-zero 'minimum' or 'maximum' are provided, they constrain the possible
|
||||
// resultant password sizes that will be attempted.
|
||||
//
|
||||
static
|
||||
int decode(
|
||||
std::string &password,
|
||||
const std::vector<int>
|
||||
&erasures,
|
||||
size_t minimum = PARITY,//always deemed at least 1
|
||||
size_t maximum = 0 ) // if 0, no limit
|
||||
{
|
||||
int confidence;
|
||||
best_avg best;
|
||||
|
||||
// Full/Partial parity. Apply some parity erasure if we have some erasure/correction
|
||||
// capability while maintaining at least one excess parity symbol for verification.
|
||||
// This can potentially result in longer password being returned, if the R-S decoder
|
||||
// accidentally solves a codeword.
|
||||
//
|
||||
// For example, if PARITY=3 (or 4) then (PARITY+1)/2 == 2, and we would only attempt up
|
||||
// to 1 parity erasure. This would leave 1 parity symbol to replace the 1 erasure, and
|
||||
// 1 remaining to validate the integrity of the password.
|
||||
//
|
||||
// The password must be long enough to contain at least 1 non-parity symbol, and the
|
||||
// designated number of non-erased parity symbols! However, by convention we'll demand
|
||||
// that the password contain at least PARITY symbols -- any less, and we can
|
||||
// accidentally correct the few remaining password symbols.
|
||||
//
|
||||
// Also, if any parity symbols won't decode (eg. were entered in error), we must deem
|
||||
// them to be erasures, too, and if the number of erasures exceeds the capacity of the
|
||||
// R-S codec, it'll fail (throw an exception, or at best solve with 0 confidence).
|
||||
for ( size_t era = 0 // how many parity symbols to deem erased
|
||||
; era < (PARITY+1)/2
|
||||
; ++era ) {
|
||||
if ( password.size() < ( minimum ? minimum : 1 ) + PARITY - era ) {
|
||||
#if defined( DEBUG ) && DEBUG >= 1
|
||||
output( std::cout )
|
||||
<< " Rejected too short password \""
|
||||
<< password << std::string( era, '_' )
|
||||
<< "\"" << " (" << era << " parity skipped)"
|
||||
<< std::endl;
|
||||
#endif
|
||||
continue; // too few password symbols to start checking parity
|
||||
}
|
||||
|
||||
if ( maximum and password.size() > maximum + PARITY - era ) {
|
||||
#if defined( DEBUG ) && DEBUG >= 1
|
||||
output( std::cout )
|
||||
<< " Rejected too long password \""
|
||||
<< password << std::string( era, '_' )
|
||||
<< "\"" << " (" << era << " parity skipped)"
|
||||
<< std::endl;
|
||||
#endif
|
||||
continue; // too few parity symbols erased to start checking parity
|
||||
}
|
||||
|
||||
// Copy password, adding 'era' additional NULs
|
||||
std::string fixed( password.size() + era, 0 );
|
||||
std::copy( password.begin(), password.end(), fixed.begin() );
|
||||
|
||||
// Decode the base-N parity, denoting any invalid (mistyped or trailing NUL) symbols
|
||||
// as erasures (adjust erasure offsets to be from start of password, not start of
|
||||
// parity). All newly added 'era' symbols will be NUL, and will be invalid. After
|
||||
// decoding parity, if we've slipped below our minimum R-S capacity threshold
|
||||
// (ie. because of mistyped parity symbols), don't attempt.
|
||||
std::vector<int> all_era;
|
||||
SERIAL::decode( fixed.begin() + fixed.size() - PARITY,
|
||||
fixed.begin() + fixed.size(), &all_era, 0,
|
||||
serialize::ws_invalid, serialize::pd_invalid );
|
||||
if ( all_era.size() >= (PARITY+1)/2 ) {
|
||||
#if defined( DEBUG ) && DEBUG >= 1
|
||||
output( std::cout )
|
||||
<< " Rejected low parity password \""
|
||||
<< password << std::string( era, '_' )
|
||||
<< "\"" << " (" << all_era.size() << " parity erasures + "
|
||||
<< era << " skipped)"
|
||||
<< std::endl;
|
||||
#endif
|
||||
continue; // Too many missing parity symbols
|
||||
}
|
||||
if ( all_era.size() + erasures.size() > PARITY ) {
|
||||
#if defined( DEBUG ) && DEBUG >= 1
|
||||
output( std::cout )
|
||||
<< " Rejected hi erasure password \""
|
||||
<< password << std::string( era, '_' )
|
||||
<< "\"" << " (" << all_era.size() + erasures.size() << " total erasures + "
|
||||
<< era << " skipped)"
|
||||
<< std::endl;
|
||||
#endif
|
||||
continue; // Total erasures beyond capacity
|
||||
}
|
||||
for ( auto &o : all_era )
|
||||
o += fixed.size() - PARITY;
|
||||
std::copy( erasures.begin(), erasures.end(), std::back_inserter( all_era ));
|
||||
|
||||
// Enough parity to try to decode. A successful R-S decode with 0 (remaining)
|
||||
// confidence indicates a successfully validated R-S codeword! Use it (ex. parity).
|
||||
try {
|
||||
std::vector<int> position;
|
||||
int corrects= rscodec.decode( fixed, all_era, &position );
|
||||
confidence = strength<PARITY>( corrects, all_era, position );
|
||||
fixed.resize( fixed.size() - PARITY );
|
||||
if ( confidence >= 0 )
|
||||
best.add( fixed, confidence );
|
||||
#if defined( DEBUG ) && DEBUG >= 1
|
||||
output( std::cout )
|
||||
<< " Reed-Solomon w/ " << era << " of " << PARITY
|
||||
<< " parity erasures " << std::setw( 3 ) << confidence
|
||||
<< "% confidence: \"" << password
|
||||
<< "\" ==> \"" << fixed
|
||||
<< "\" (corrects: " << corrects
|
||||
<< ", erasures at " << all_era
|
||||
<< ", fixed at " << position << "): "
|
||||
<< std::endl
|
||||
<< best;
|
||||
#endif
|
||||
} catch ( std::exception &exc ) {
|
||||
#if defined( DEBUG ) && DEBUG >= 2 // should see only when ezpwd::reed_solomon<...>::decode fails
|
||||
output( std::cout ) << " invalid part parity password: " << exc.what() << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Partial parity, but below threshold for usable error detection. For the first 1 to
|
||||
// (PARITY+1)/2 parity symbols (eg. for PARITY == 3, (PARITY+1)/2 == 1 ), we cannot
|
||||
// perform meaningful error or erasure detection. However, if we see that the terminal
|
||||
// symbols match the R-S symbols we expect from a correct password, we'll ascribe a
|
||||
// partial confidence due to the matching parity symbols.
|
||||
//
|
||||
// password: sock1t
|
||||
// w/ 3 parity: sock1tkeB
|
||||
// password ----^^^^^^
|
||||
// ^^^--- parity
|
||||
//
|
||||
for ( size_t era = (PARITY+1)/2 // how many parity symbols are not present
|
||||
; era < PARITY
|
||||
; ++era ) {
|
||||
if ( password.size() < ( minimum ? minimum : 1 ) + PARITY - era ) {
|
||||
#if defined( DEBUG ) && DEBUG >= 1
|
||||
output( std::cout )
|
||||
<< " Rejected too short password \""
|
||||
<< password << std::string( era, '_' )
|
||||
<< "\""
|
||||
<< std::endl;
|
||||
#endif
|
||||
continue; // too few password symbols to start checking parity
|
||||
}
|
||||
if ( maximum and password.size() > maximum + PARITY - era ) {
|
||||
#if defined( DEBUG ) && DEBUG >= 1
|
||||
output( std::cout )
|
||||
<< " Rejected too long password \""
|
||||
<< password << std::string( era, '_' )
|
||||
<< "\"" << " (" << era << " parity skipped)"
|
||||
<< std::endl;
|
||||
#endif
|
||||
continue; // too few parity symbols erased to start checking parity
|
||||
}
|
||||
std::string fixed = password;
|
||||
size_t len = password.size() - ( PARITY - era );
|
||||
fixed.resize( len );
|
||||
encode( fixed );
|
||||
auto differs = std::mismatch( fixed.begin(), fixed.end(), password.begin() );
|
||||
size_t par_equ = differs.second - password.begin();
|
||||
if ( par_equ < len || par_equ > len + PARITY )
|
||||
throw std::runtime_error( "miscomputed R-S parity matching length" );
|
||||
par_equ -= len;
|
||||
|
||||
// At least one parity symbol is requires to give any confidence
|
||||
if ( par_equ > 0 ) {
|
||||
std::string basic( fixed.begin(), fixed.begin() + len );
|
||||
confidence = par_equ * 100 / PARITY; // each worth a normal parity symbol
|
||||
best.add( basic, confidence );
|
||||
#if defined( DEBUG ) && DEBUG >= 1
|
||||
output( std::cout )
|
||||
<< " Check Chars. w/ " << era << " of " << PARITY
|
||||
<< " parity missing " << std::setw( 3 ) << confidence
|
||||
<< "% confidence: \"" << password
|
||||
<< "\" ==> \"" << basic
|
||||
<< " (from computed: \"" << fixed << "\")"
|
||||
<< ": "
|
||||
<< std::endl
|
||||
<< best;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Select the best guess and return its confidence. Otherwise, use raw password? If no
|
||||
// error/erasure attempts succeeded (if no 'best' w/ confidence >= 0), then we'll use
|
||||
// the raw password w/ 0 confidence, if it meets the minimum/maximum length
|
||||
// requirements.
|
||||
confidence = -1;
|
||||
if ( password.size() >= ( minimum ? minimum : 1 )
|
||||
and ( maximum == 0 or password.size() <= maximum ))
|
||||
confidence = 0;
|
||||
|
||||
typename best_avg::const_iterator
|
||||
bi = best.best();
|
||||
#if defined( DEBUG )
|
||||
output( std::cout )
|
||||
<< " Selected " << ( bi != best.end() ? "corrected" : "unmodified" )
|
||||
<< " password \"" << ( bi != best.end() ? bi->first : password )
|
||||
<< "\" of length " << ( minimum ? minimum : 1) << "-" << maximum
|
||||
<< " (vs. \"" << password
|
||||
<< "\") w/ confidence " << (bi != best.end() ? bi->second.second : confidence )
|
||||
<< "%, from: "
|
||||
<< std::endl
|
||||
<< best;
|
||||
#endif
|
||||
if ( bi != best.end() ) {
|
||||
auto better = best.evaluation( *bi ); // --> (<average>,<password>)
|
||||
password = better.second;
|
||||
confidence = better.first;
|
||||
}
|
||||
return confidence;
|
||||
}
|
||||
|
||||
static
|
||||
int decode(
|
||||
std::string &password,
|
||||
size_t minimum = PARITY,
|
||||
size_t maximum = 0 )
|
||||
{
|
||||
return decode( password, std::vector<int>(), minimum, maximum );
|
||||
}
|
||||
|
||||
//
|
||||
// decode(<char*>,<size_t>,<size_t>,<size_t>) -- C interface to decode(<string>)
|
||||
//
|
||||
// Traditional C interface. The provided NUL-terminated password+parity is decoded
|
||||
// (parity removed), and the confidence % is returned.
|
||||
//
|
||||
// If any failure occurs, a -'ve value will be returned, and the supplied password
|
||||
// buffer will be used to contain an error description.
|
||||
//
|
||||
static int decode(
|
||||
char *password, // NUL terminated
|
||||
size_t siz, // available size
|
||||
size_t minimum = PARITY,//minimum resultant password length
|
||||
size_t maximum = 0 ) // maximum ''
|
||||
{
|
||||
std::string corrected( password );
|
||||
int confidence;
|
||||
try {
|
||||
confidence = decode( corrected, minimum, maximum );
|
||||
if ( corrected.size() + 1 > siz )
|
||||
throw std::runtime_error( "password buffer has insufficient capacity" );
|
||||
std::copy( corrected.begin(), corrected.end(), password );
|
||||
password[corrected.size()] = 0;
|
||||
} catch ( std::exception &exc ) {
|
||||
confidence = -1;
|
||||
ezpwd::streambuf_to_buffer sbf( password, siz );
|
||||
std::ostream( &sbf ) << "corrector<" << PARITY << "> failed: " << exc.what();
|
||||
}
|
||||
return confidence;
|
||||
}
|
||||
|
||||
//
|
||||
// rscodec -- A ?-bit RS(N-1,N-1-PARITY) Reed-Solomon codec
|
||||
//
|
||||
// Encodes and decodes R-S symbols over the lower 6 bits of the supplied data. Requires
|
||||
// that the last N (parity) symbols of the data are in the range [0,63]. The excess bits on
|
||||
// the data symbols are masked and restored during decoding.
|
||||
//
|
||||
static const ezpwd::RS<N-1,N-1-PARITY>
|
||||
rscodec;
|
||||
};
|
||||
|
||||
template < size_t PARITY, size_t N, typename SERIAL >
|
||||
const ezpwd::RS<N-1,N-1-PARITY>
|
||||
corrector<PARITY,N,SERIAL>::rscodec;
|
||||
|
||||
} // namespace ezpwd
|
||||
|
||||
template < size_t PARITY, size_t N, typename SERIAL >
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const ezpwd::corrector<PARITY,N,SERIAL>
|
||||
&rhs )
|
||||
{
|
||||
return rhs.output( lhs );
|
||||
}
|
||||
|
||||
#endif // _EZPWD_CORRECTOR
|
||||
9
src/ezpwd/definitions
Normal file
9
src/ezpwd/definitions
Normal file
@@ -0,0 +1,9 @@
|
||||
//
|
||||
// C++ Definitions -- include once in a single C++ compilation unit
|
||||
//
|
||||
#ifndef _EZPWD_DEFINITIONS
|
||||
#define _EZPWD_DEFINITIONS
|
||||
|
||||
#include "serialize_definitions"
|
||||
|
||||
#endif // _EZPWD_DEFINITIONS
|
||||
725
src/ezpwd/ezcod
Normal file
725
src/ezpwd/ezcod
Normal file
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
|
||||
*
|
||||
* Copyright (c) 2014, Hard Consulting Corporation.
|
||||
*
|
||||
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
|
||||
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. See the LICENSE file at the top of the
|
||||
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. c++/ezpwd/rs_base
|
||||
* is redistributed under the terms of the LGPL, regardless of the overall licensing terms.
|
||||
*
|
||||
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||
* the GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _EZPWD_EZCOD
|
||||
#define _EZPWD_EZCOD
|
||||
|
||||
#include <math.h> // M_PI
|
||||
#include <cmath>
|
||||
#include <cctype>
|
||||
|
||||
#include <cstdint>
|
||||
#include <ezpwd/rs>
|
||||
#include <ezpwd/output>
|
||||
#include <ezpwd/serialize>
|
||||
|
||||
//
|
||||
// EZCOD 3:10 location code w/ Reed-Solomon Error Correction, and average 3m accuracy
|
||||
//
|
||||
// - each successive symbol provides greater precision
|
||||
// - codes nearby each-other are identical in leading characters
|
||||
// - average 3m precision achieved in 9 symbols
|
||||
// - more than 4 base-10 digits of precision in both lat and lon after the decimal
|
||||
// - from 1 to 3 symbols of Reed-Solomon parity
|
||||
// - 1 parity symbol supplies validation w/ strength equivalent to a check character
|
||||
// - 2 parity symbols provides correction of 1 lost symbol (no errors)
|
||||
// - 3 parity symbols provides correction of any 1 error, with verification,
|
||||
// or recovery of up to any 3 lost symbols (with no other errors)
|
||||
//
|
||||
|
||||
//
|
||||
// To achieve at least 4 decimal digits of precision after the decimal point, we must have
|
||||
// defined lat to within 1 part in 1,800,000, and lon to within 1 part in 3,600,000. As each symbol
|
||||
// supplies bits, we'll refine the computed lat/lon further, reducing the outstanding fraction of
|
||||
// "parts" yet to be defined.
|
||||
//
|
||||
// bits
|
||||
// symbols latitude longitude
|
||||
// bits mul parts bits mul parts
|
||||
// 1 2 4 4 3 8 8
|
||||
// 2 2 4 16 3 8 64
|
||||
// 3 3 8 128 2 4 256 // not quite integer lat/lon accuracy
|
||||
//
|
||||
// 4 2 4 512 3 8 2,048
|
||||
// 5 3 8 4,096 2 4 8,192
|
||||
// 6 2 4 16,384 3 8 65,536
|
||||
//
|
||||
// 7 3 8 131,072 2 4 262,144
|
||||
// 8 2 4 524,288 3 8 2,097,152
|
||||
// 9 3 8 4,194,304 2 4 8,388,608 parts resolution in 3:10 code
|
||||
// over [-90, 90] over [-180,180] yields ~3m resolution
|
||||
//
|
||||
// vs. 1,800,000 3,600,000 parts resolution in 10:10 code
|
||||
// over [-90, 90] over [-180,180] yields ~10m resolution
|
||||
//
|
||||
// Therefore, within 9 symbols we define lat and lon with better than double the precision of
|
||||
// 10:10 code's 4 decimal digits after the decimal point. This yields an approximate lineal
|
||||
// precision of 40,075,000m / 8,388,608 == ~5m in both dimensions at the equator, vs. 40,075,000m /
|
||||
// 3,600,000 == ~11m for 10:10 codes.
|
||||
//
|
||||
// The 10:10 code provides a single check character, which provides about P(1-1/32) certainty
|
||||
// that the provided code is correct. With EZCOD 3:10/11/12 codes, we provide varying levels of
|
||||
// detection/correction strength.
|
||||
//
|
||||
// - 1 parity symbol: act as a check character (like 10:10 codes), or provide 1 symbol of erasure
|
||||
// (lost symbol) recovery with no excess parity for validation.
|
||||
//
|
||||
// - 2 parity symbols: provide 1 symbol of erasure correction (w/ no other errors) with 1 excess parity
|
||||
// symbol for validation, or 1 symbol of error detection with no excess parity for validation.
|
||||
//
|
||||
// - 3 parity symbols: correct 1 error anywhere w/ 1 excess parity symbol for validation, or up
|
||||
// to 3 erasures with no excess parity for validation.
|
||||
//
|
||||
// Therefore, we'll provide Reed-Solomon RS(31,28-30) error correction (5 bit symbols,
|
||||
// indicating 31 symbols in the field, and from 1 to 3 roots, therefore up to 28 data symbols in the
|
||||
// field) over the 9 lat/lon data symbols.
|
||||
//
|
||||
//
|
||||
// MINIMIZING ERROR
|
||||
//
|
||||
// Each input lat/lon coordinate will be effectively truncated by the encoding procedure to the
|
||||
// level of precision (parts) encoded by each symbol. Subsequent symbols then add their (smaller)
|
||||
// parts to increase precision.
|
||||
//
|
||||
// After the last symbol, we know that the actual input coordinates where somewhere
|
||||
// within the rectangle:
|
||||
//
|
||||
// [0,0] -> [0,lon_precision] -> [lat_precision,lon_precision] -> [lat_precision,0]
|
||||
//
|
||||
// At first glance, the best way is to perform rounding instead of truncation on ecoding, by
|
||||
// simply adding 1/2 of the precision. Then, the unmodified output lat/lon decoded represents the
|
||||
// point nearest actual input coordinate. However, this is NOT ideal. Remember -- the decoding may
|
||||
// not have access to all the symbols! We want to minimize the error even if only some of the
|
||||
// symbols are available. Thus, we must apply a correction on decoding.
|
||||
//
|
||||
// One way gain rounding instead of truncation on decoding is, after adding the last symbol's
|
||||
// precision, to add 50% of the value represented by the first bit of the next (missing) symbol's
|
||||
// precision parts. This would be analogous to receiving the first 2 digits of a 3 digit number:
|
||||
//
|
||||
// original: 123
|
||||
// received: 12_
|
||||
// range: [120,130)
|
||||
// guessed: 125 (add 1/2 of the parts represented by the missing digit)
|
||||
//
|
||||
// If this is done, then the resulting coordinate would be in the middle of the rectangle of
|
||||
// possible input lat/lon values that could have resulted in the encoded value. This also works if
|
||||
// we don't receive and decode all of the symbols; We'll end up with a lat/lon in the middle of the
|
||||
// (large) rectangle of possible input coordinates.
|
||||
//
|
||||
|
||||
|
||||
namespace ezpwd {
|
||||
|
||||
class ezcod_base {
|
||||
public:
|
||||
double latitude; // [-90,+90] angle, degrees
|
||||
double latitude_error; // total error bar, in meters
|
||||
double longitude; // [-180,180]
|
||||
double longitude_error; // total error bar, in meters
|
||||
double accuracy; // linear accuracy radius, in meters
|
||||
int confidence; // % parity in excess of last decode
|
||||
double certainty; // and the associated probability
|
||||
|
||||
explicit ezcod_base(
|
||||
double _lat = 0,
|
||||
double _lon = 0 )
|
||||
: latitude( _lat )
|
||||
, latitude_error( 0 )
|
||||
, longitude( _lon )
|
||||
, longitude_error( 0 )
|
||||
, accuracy( 0 )
|
||||
, confidence( 100 )
|
||||
, certainty( 1 )
|
||||
{
|
||||
;
|
||||
}
|
||||
virtual ~ezcod_base()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
typedef std::pair<unsigned char, unsigned char>
|
||||
symbols_t;
|
||||
virtual symbols_t symbols()
|
||||
const
|
||||
= 0;
|
||||
virtual std::ostream &output(
|
||||
std::ostream &lhs )
|
||||
const
|
||||
= 0;
|
||||
virtual std::string encode(
|
||||
unsigned _preci = 0 ) // override precision
|
||||
const
|
||||
= 0;
|
||||
virtual int decode(
|
||||
const std::string &_ezcod )
|
||||
= 0;
|
||||
};
|
||||
} // namespace ezpwd
|
||||
|
||||
inline std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const ezpwd::ezcod_base
|
||||
&rhs )
|
||||
{
|
||||
return rhs.output( lhs );
|
||||
}
|
||||
|
||||
namespace ezpwd {
|
||||
|
||||
//
|
||||
// ezcod<P,L> -- defaults to 1 PARITY and 9 location symbols (3m) of PRECISION
|
||||
//
|
||||
template < unsigned P=1, unsigned L=9 >
|
||||
class ezcod
|
||||
: public ezcod_base {
|
||||
|
||||
private:
|
||||
typedef std::array<symbols_t, 12>
|
||||
bits_t;
|
||||
static const bits_t bits;
|
||||
typedef std::array<std::pair<uint32_t, uint32_t>, 12>
|
||||
parts_t;
|
||||
static const parts_t parts;
|
||||
|
||||
#if defined( DEBUG )
|
||||
public:
|
||||
#endif
|
||||
static const ezpwd::RS<31,31-P>
|
||||
rscodec;
|
||||
|
||||
public:
|
||||
static constexpr const unsigned PARITY = P; // specified symbols of R-S parity
|
||||
static constexpr const unsigned PRECISION = L; // default symbols of location precision
|
||||
static constexpr const unsigned CHUNK = 3; // default chunk size
|
||||
|
||||
static constexpr const char SEP_NONE = -1;
|
||||
static constexpr const char SEP_DEFAULT = 0;
|
||||
static constexpr const char SEP_DOT = '.';
|
||||
static constexpr const char SEP_BANG = '!';
|
||||
static constexpr const char SEP_SPACE = ' ';
|
||||
|
||||
static constexpr const char CHK_NONE = -1;
|
||||
static constexpr const char CHK_DEFAULT = 0;
|
||||
static constexpr const char CHK_DASH = '-';
|
||||
static constexpr const char CHK_SPACE = ' ';
|
||||
|
||||
unsigned precision;
|
||||
unsigned chunk; // Location symbol chunk sizes
|
||||
char separator; // Separator between location and parity symbols
|
||||
char space; // Fill space between location symbol chunks
|
||||
|
||||
//
|
||||
// ezcod<P,L>() -- supply non-defaults for location precision, chunk size, etc.
|
||||
//
|
||||
explicit ezcod(
|
||||
double _lat = 0,
|
||||
double _lon = 0,
|
||||
unsigned _preci = 0,
|
||||
unsigned _chunk = 0,
|
||||
char _seper = 0,
|
||||
char _space = 0 )
|
||||
: ezcod_base( _lat, _lon )
|
||||
, precision( _preci ? _preci : PRECISION )
|
||||
, chunk( _chunk ? _chunk : CHUNK )
|
||||
, separator( _seper )
|
||||
, space( _space )
|
||||
{
|
||||
if ( P < 1 )
|
||||
throw std::runtime_error( "ezpwd::ezcod:: At least one parity symbol must be specified" );
|
||||
if ( precision < 1 || precision > bits.max_size() )
|
||||
throw std::runtime_error( std::string( "ezpwd::ezcod:: Only 1-" )
|
||||
+ std::to_string( bits.max_size() )
|
||||
+ " location symbol may be specified" );
|
||||
}
|
||||
explicit ezcod(
|
||||
const std::string &_ezcod,
|
||||
unsigned _preci = 0,
|
||||
unsigned _chunk = 0,
|
||||
char _seper = 0,
|
||||
char _space = 0 )
|
||||
: ezcod( 0, 0, _preci, _chunk, _seper, _space )
|
||||
{
|
||||
decode( _ezcod );
|
||||
}
|
||||
virtual ~ezcod()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
//
|
||||
// symbols -- return working parity and location precision
|
||||
//
|
||||
virtual ezcod_base::symbols_t
|
||||
symbols()
|
||||
const
|
||||
{
|
||||
return ezcod_base::symbols_t( P, precision );
|
||||
}
|
||||
|
||||
virtual std::ostream &output(
|
||||
std::ostream &lhs )
|
||||
const
|
||||
{
|
||||
std::streamsize prec = lhs.precision();
|
||||
std::ios_base::fmtflags
|
||||
flg = lhs.flags();
|
||||
lhs.precision( 10 );
|
||||
std::string uni = "m ";
|
||||
double acc = accuracy;
|
||||
double dec = 2;
|
||||
if ( acc > 1000 ) {
|
||||
uni = "km";
|
||||
acc /= 1000;
|
||||
} else if ( acc < 1 ) {
|
||||
uni = "mm";
|
||||
acc *= 1000;
|
||||
}
|
||||
if ( acc >= 100 )
|
||||
dec = 0;
|
||||
else if ( acc >= 10 )
|
||||
dec = 1;
|
||||
|
||||
lhs << encode( precision )
|
||||
<< " (" << std::setw( 3 ) << confidence
|
||||
<< "%) == " << std::showpos << std::fixed << std::setprecision( 10 ) << std::setw( 15 ) << latitude
|
||||
<< ", " << std::showpos << std::fixed << std::setprecision( 10 ) << std::setw( 15 ) << longitude
|
||||
<< " +/- " << std::noshowpos << std::fixed << std::setprecision( dec ) << std::setw( 6 ) << acc << uni;
|
||||
lhs.precision( prec );
|
||||
lhs.flags( flg );
|
||||
return lhs;
|
||||
}
|
||||
|
||||
//
|
||||
// encode() -- encode the lat/lon to 'precision' symbols EZCOD representation
|
||||
//
|
||||
virtual std::string encode(
|
||||
unsigned _preci = 0 ) // override precision
|
||||
const
|
||||
{
|
||||
// Convert lat/lon into a fraction of number of parts assigned to each
|
||||
double lat_frac= ( latitude + 90 ) / 180;
|
||||
if ( lat_frac < 0 || lat_frac > 1 )
|
||||
throw std::runtime_error( "ezpwd::ezcod::encode: Latitude not in range [-90,90]" );
|
||||
double lon_frac= ( longitude + 180 ) / 360;
|
||||
if ( lon_frac < 0 || lon_frac > 1 )
|
||||
throw std::runtime_error( "ezpwd::ezcod::encode: Longitude not in range [-180,180]" );
|
||||
if ( _preci == 0 )
|
||||
_preci = precision;
|
||||
if ( _preci < 1 || _preci > bits.max_size() )
|
||||
throw std::runtime_error( std::string( "ezpwd::ezcod:: Only 1-" )
|
||||
+ std::to_string( bits.max_size() )
|
||||
+ " location symbol may be specified" );
|
||||
|
||||
// Compute the integer number of lat/lon parts represented by each coordinate, for the
|
||||
// specified level of precision, and then truncate to the range [0,..._parts),
|
||||
// eg. Latitude 90 --> 89.999...
|
||||
uint32_t lat_parts = parts[_preci-1].first; // [ -90,90 ] / 4,194,304 parts in 9 symbols
|
||||
uint32_t lon_parts = parts[_preci-1].second; // [-180,180] / 8,388,608 parts ''
|
||||
|
||||
uint32_t lat_rem = std::min( lat_parts-1, uint32_t( lat_parts * lat_frac ));
|
||||
uint32_t lon_rem = std::min( lon_parts-1, uint32_t( lon_parts * lon_frac ));
|
||||
|
||||
// Initial loop condition; lat/lon multiplier is left at the base multiplier of the
|
||||
// previous loop. Then, loop computing the units multiplier, and hten removing the most
|
||||
// significant bits (multiples of the units multiplier). They will both reach 1
|
||||
unsigned int lat_mult= lat_parts;
|
||||
unsigned int lon_mult= lon_parts;
|
||||
|
||||
std::string res;
|
||||
res.reserve( _preci // approximate result length
|
||||
+ ( chunk && chunk < _preci
|
||||
? _preci / chunk - 1
|
||||
: 0 )
|
||||
+ 1 + P );
|
||||
for ( auto &b : bits ) {
|
||||
unsigned char lat_bits= b.first;
|
||||
unsigned char lon_bits= b.second;
|
||||
lat_mult >>= lat_bits;
|
||||
lon_mult >>= lon_bits;
|
||||
if ( ! lat_mult || ! lon_mult )
|
||||
break;
|
||||
|
||||
// Each set of bits represents the number of times the current multiplier (after
|
||||
// division by the number of bits we're outputting) would go into the remainder.
|
||||
// Eg. If _mult was 1024, and _rem is 123 and _bits is 3, we're going to put out
|
||||
// the next 3 bits of the value 199. The last value ended removing all multiples of
|
||||
// 1024. So, we first get the new multiplier: 1024 >> 3 == 128. So, we're
|
||||
// indicating, as a 3-bit value, how many multiples of 128 there are in the value
|
||||
// 199: 199 / 128 == 1, so the 3-bit value we output is 001
|
||||
uint32_t lat_val = lat_rem / lat_mult;
|
||||
lat_rem -= lat_val * lat_mult;
|
||||
|
||||
uint32_t lon_val = lon_rem / lon_mult;
|
||||
lon_rem -= lon_val * lon_mult;
|
||||
|
||||
res += char( ( lat_val << lon_bits ) | lon_val );
|
||||
}
|
||||
|
||||
// Add the R-S parity symbols and base-32 encode, add parity separator and chunk
|
||||
rscodec.encode( res );
|
||||
serialize::base32::encode( res );
|
||||
switch( separator ) {
|
||||
case SEP_NONE:
|
||||
break;
|
||||
case SEP_DOT: default:
|
||||
res.insert( _preci, 1, SEP_DOT );
|
||||
break;
|
||||
case SEP_BANG:
|
||||
case SEP_SPACE:
|
||||
res.insert( _preci, 1, separator );
|
||||
break;
|
||||
}
|
||||
if ( space != CHK_NONE && chunk && chunk < _preci ) {
|
||||
for ( unsigned c = _preci / chunk - 1; c > 0; --c ) {
|
||||
switch ( space ) {
|
||||
case CHK_NONE:
|
||||
break;
|
||||
case CHK_SPACE: default:
|
||||
res.insert( c * chunk, 1, CHK_SPACE );
|
||||
break;
|
||||
case CHK_DASH:
|
||||
res.insert( c * chunk, 1, space );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//
|
||||
// deserialize -- Extract base-32, skip whitespace, mark invalid symbols as erasures
|
||||
// validate -- Remove base-32 encoding, validate and remove parity, returning confidence
|
||||
// decode -- Attempt to decode a lat/lon, returning the confidence percentage
|
||||
//
|
||||
// If data but no parity symbols are supplied, no error checking is performed, and the
|
||||
// confidence returned will be 0%. No erasures within the supplied data are allowed (as
|
||||
// there is no capacity to correct them), and an exception will be thrown.
|
||||
//
|
||||
// If parity is supplied, then erasures are allowed. So long as the total number of
|
||||
// erasures is <= the supplied parity symbols, then the decode will proceed (using the
|
||||
// parity symbols to fill in the erasures), and the returned confidence will reflect the
|
||||
// amount of unused parity capacity. Each erasure consumes one parity symbol to repair.
|
||||
//
|
||||
// We'll allow question-mark or any of the slash characters: "_/\?" to indicate an
|
||||
// erasure. Either of the "!." symbol may be used to indicates the split between location
|
||||
// symbols and parity symbols, and must be in a position that corresponds to the indicated
|
||||
// number of location (this->precision) and parity 'P' symbols. Whitespace symbols and dash
|
||||
// are ignored: " -".
|
||||
//
|
||||
// Thus, an EZCOD like "R3U 1JU QUY!0" may only be decoded by an ezcod<P=1>. Without
|
||||
// the "!" or ".", it could be an ezcod<P=2> w/precision == 8 -- there's no way to know for
|
||||
// sure. If no explicit position-parity separator is given, then we assume the default:
|
||||
// this->precision location symbols, then up to P parity symbols. If additional parity
|
||||
// symbols are supplied after the separator, then However, an ezcod...<P=3>
|
||||
//
|
||||
// If an explicit "!" or "." separator IS provided, then we will attempt to decode the
|
||||
// position with the given number of position symbols, and up to P parity symbols.
|
||||
//
|
||||
// NOTE
|
||||
//
|
||||
// Due to a perhaps unexpected feature of R-S codewords, a codeword with MORE parity
|
||||
// can be successfully decoded by an R-S codec specifying LESS parity symbols. It so
|
||||
// happens that the data plus (excess) parity + (remaining) parity is STILL a valid codeword
|
||||
// (so long as the R-S Galois parameters are identical).
|
||||
//
|
||||
// Therefore, EZCODs with more parity are accepted by EZCOD parsers configured for less
|
||||
// parity. Of course, they will have less error/erasure correction strength -- using the
|
||||
// correctly configured EZCOD codec expecting more R-S parity will maximize the value of all
|
||||
// the supplied parity.
|
||||
//
|
||||
// The full amount of parity (ie. everything after the location/parity separator) is
|
||||
// discarded in all cases, before the EZCOD location is decoded.
|
||||
//
|
||||
private:
|
||||
|
||||
unsigned deserialize(
|
||||
std::string &dec,
|
||||
std::vector<int> &erasure,
|
||||
std::vector<char> &invalid )
|
||||
const
|
||||
{
|
||||
serialize::base32::decode( dec, &erasure, &invalid );
|
||||
|
||||
// The special symbol '!' or '.' indicates the end of the EZCOD location symbols and the
|
||||
// beginning of parity: ensure the symbol counts are consistent with the encoding. By
|
||||
// default the parity symbols begin at offset precision. If we see more than precision
|
||||
// symbols, we assume that the Lth and subsequent symbols are parity. If a
|
||||
// location/parity separator is provided, it must be at position this->precision!
|
||||
// Return offset of start of parity in codeword.
|
||||
unsigned parbeg = this->PRECISION; // Parity begins after Location, by default
|
||||
for ( unsigned i = 0; i < invalid.size(); ++i ) {
|
||||
switch ( invalid[i] ) {
|
||||
case '!': case '.':
|
||||
// Remember the offset of the first parity symbol (it'll be in the position of
|
||||
// the last '!' or '.' symbol we're about to erase), and adjust the indices of
|
||||
// any erasures following.
|
||||
parbeg = erasure[i];
|
||||
dec.erase( parbeg, 1 );
|
||||
invalid.erase( invalid.begin() + i );
|
||||
erasure.erase( erasure.begin() + i );
|
||||
for ( unsigned j = i; j < erasure.size(); ++j )
|
||||
erasure[j] -= 1;
|
||||
break;
|
||||
case '_': case '/': case '\\': case '?':
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error( std::string( "ezpwd::ezcod::decode: invalid symbol presented: '" )
|
||||
+ invalid[i] + "'" );
|
||||
}
|
||||
}
|
||||
#if defined( DEBUG ) && DEBUG >= 1
|
||||
std::cout << " --> 0x" << std::vector<uint8_t>( dec.begin(), dec.begin() + std::min( size_t( parbeg ), dec.length()) )
|
||||
<< " + 0x" << std::vector<uint8_t>( dec.begin() + std::min( size_t( parbeg ), dec.length()),
|
||||
dec.begin() + dec.length() )
|
||||
<< " parity" << std::endl;
|
||||
#endif
|
||||
return parbeg;
|
||||
}
|
||||
|
||||
int validate(
|
||||
std::string &dec )
|
||||
const
|
||||
{
|
||||
// Compute and return validity (which may later be assigned to this->confidence)
|
||||
int validity = 0; // if no R-S parity provided
|
||||
|
||||
#if defined( DEBUG ) && DEBUG >= 1
|
||||
std::cout << *this << " validate( " << dec << " ) ";
|
||||
#endif
|
||||
std::vector<int> erasure;
|
||||
std::vector<char> invalid;
|
||||
unsigned parbeg = deserialize( dec, erasure, invalid );
|
||||
|
||||
if ( dec.size() > parbeg || erasure.size() > 0 ) {
|
||||
// Some R-S parity symbol(s) were provided (or erasures were marked). See if we can
|
||||
// successfully decode/correct, or (at least) use one parity symbol as a check
|
||||
// character. If we identify more erasures than R-S parity, we must fail; we can't
|
||||
// recover the data. This will of course be the case if we have *any* erasures in
|
||||
// the data, and no parity.
|
||||
unsigned parity = 0;
|
||||
if ( dec.size() > parbeg )
|
||||
parity = dec.size() - parbeg;
|
||||
while ( dec.size() < parbeg + P ) {
|
||||
erasure.push_back( dec.size() );
|
||||
dec.resize( dec.size() + 1 );
|
||||
}
|
||||
#if defined( DEBUG ) && DEBUG >= 2
|
||||
std::cout << " --> erasures: " << erasure.size() << " vs. parity: " << parity
|
||||
<< ": " << std::vector<uint8_t>( dec.begin(), dec.end() ) << std::endl;
|
||||
#endif
|
||||
if ( erasure.size() > parity ) {
|
||||
// We cannot do R-S decoding; not enough parity symbols to even cover erasures.
|
||||
// If parity symbol(s) were provided ('parity' > 0), and all erasures were due the
|
||||
// missing remaining parity symbols, we can use the existing parity symbol(s) as
|
||||
// "check character(s)", by simply re-encoding the supplied non-parity data, and
|
||||
// see if the generated parity symbol(s) match the supplied parity. This has
|
||||
// basically the same strength as the 10:10 code's check character.
|
||||
if ( parity + erasure.size() == P ) {
|
||||
// All erasures must be at end, in remaining parity symbols!
|
||||
std::string chk( dec.begin(), dec.begin() + parbeg );
|
||||
rscodec.encode( chk );
|
||||
// each parity symbol provided must match the corresponding encoded chk symbol
|
||||
for ( unsigned i = 0; i < parity; ++i )
|
||||
if ( dec[parbeg+i] != chk[parbeg+i] )
|
||||
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; check character mismatch" );
|
||||
// Check character(s) matched; erasure.size()/P of confidence gone
|
||||
validity = ezpwd::strength<P>( erasure.size(), erasure, erasure );
|
||||
} else
|
||||
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; too many erasures" );
|
||||
} else {
|
||||
// We can try R-S decoding; we have (at least) enough parity to try to recover
|
||||
// any missing symbol(s).
|
||||
std::vector<int>position;
|
||||
int corrects= rscodec.decode( dec, erasure, &position );
|
||||
if ( corrects < 0 )
|
||||
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; R-S decode failed" );
|
||||
// Compute confidence, from spare parity capacity. Since R-S decode will not
|
||||
// return the position of erasures that turn out (by accident) to be correct,
|
||||
// but they have consumed parity capacity, we re-add them into the correction
|
||||
// position vector. If the R-S correction reports more corrections than the
|
||||
// parity can possibly have handled correctly, (eg. 2 reported erasures and an
|
||||
// unexpected error), then the decode is almost certainly incorrect; fail.
|
||||
validity = ezpwd::strength<P>( corrects, erasure, position );
|
||||
if ( validity < 0 )
|
||||
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; R-S decode overwhelmed" );
|
||||
}
|
||||
if ( dec.size() > parbeg )
|
||||
dec.resize( parbeg ); // Discard any parity symbols
|
||||
}
|
||||
return validity;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual int decode( const std::string &str )
|
||||
{
|
||||
// Decode the R-S encoding, computing the confidence. Will raise an exception on any
|
||||
// error. Don't change this->confidence, this->latitude, ... until there is no longer a
|
||||
// chance of exception.
|
||||
std::string decoded( str );
|
||||
int validity= validate( decoded );
|
||||
|
||||
// Unpack the supplied location data; we'll take as much as we are given, up to the
|
||||
// maximum possible 12 symbols supported (9 symbols yielding ~3m resolution).
|
||||
uint32_t lat_tot = 0;
|
||||
uint32_t lon_tot = 0;
|
||||
|
||||
uint32_t lat_mult= 1;
|
||||
uint32_t lon_mult= 1;
|
||||
|
||||
auto di = decoded.begin();
|
||||
for ( auto &b : bits ) {
|
||||
if ( di == decoded.end() )
|
||||
break;
|
||||
unsigned char c = *di++;
|
||||
|
||||
unsigned char lat_bits= b.first;
|
||||
unsigned char lon_bits= b.second;
|
||||
|
||||
uint32_t lat_val = c >> lon_bits;
|
||||
uint32_t lon_val = c & (( 1 << lon_bits ) - 1 );
|
||||
|
||||
lat_mult <<= lat_bits;
|
||||
lat_tot <<= lat_bits;
|
||||
lat_tot += lat_val;
|
||||
|
||||
lon_mult <<= lon_bits;
|
||||
lon_tot <<= lon_bits;
|
||||
lon_tot += lon_val;
|
||||
}
|
||||
|
||||
// Convert the sum of lat/lon parts back into degrees, and round the (truncated) value
|
||||
// to the middle of the error rectangle. This allows us to minimize error even if we
|
||||
// didn't have access to all of the origin symbols to decode. The absolute error bar as
|
||||
// a proportional factor [0,1) for lat/lon is at most the scale of the last parts
|
||||
// multiplier used. We'll use this later to compute the error in meters; for example,
|
||||
// if the last value we added worked out to be worth units of 25m of the circumference,
|
||||
// then we must now be within [0,25m) of the original point.
|
||||
double lat_err = 1.0 / lat_mult;
|
||||
double lon_err = 1.0 / lon_mult;
|
||||
latitude = 180 * ( double( lat_tot ) / lat_mult + lat_err / 2 ) - 90;
|
||||
longitude = 360 * ( double( lon_tot ) / lon_mult + lon_err / 2 ) - 180;
|
||||
|
||||
// Remember the decoded location precision for future encoding (overrides the default).
|
||||
// Compute the certainty probability (0.0,1.0] given the number of parity symbols in
|
||||
// excess: Given a base-32 symbol: 1 - 1 / ( 32 ^ P ) where P is the number of
|
||||
// unconsumed parity.
|
||||
precision = decoded.size();
|
||||
confidence = validity;
|
||||
certainty = 0.0;
|
||||
if ( PARITY * confidence / 100 )
|
||||
certainty = 1.0 - 1.0 / std::pow( double( 32.0 ),
|
||||
double( PARITY * confidence / 100 ));
|
||||
|
||||
// Compute the resolution error (in m.) of the decoded lat/lon and compute the minimum
|
||||
// accuracy -- the radius of the circle around the computed latitude/longitude, inside
|
||||
// which the original latitude/longitude must have been.
|
||||
//
|
||||
// original latitude error bar
|
||||
// \ /
|
||||
// o -
|
||||
// | longitude error bar
|
||||
// | /
|
||||
// |--x--|
|
||||
// /|
|
||||
// / |
|
||||
// computed -
|
||||
//
|
||||
// The maximum distance is the length of the diagonal of the error rectangle defined by
|
||||
// 1/2 the latitude/longitude error bars.
|
||||
//
|
||||
double lon_circ= 1 * M_PI * 6371000;
|
||||
double lat_circ= 2 * M_PI * 6371000 * std::cos( latitude * M_PI / 180 );
|
||||
latitude_error = lat_err * lon_circ;
|
||||
longitude_error = lon_err * lat_circ;
|
||||
|
||||
accuracy = sqrt( latitude_error / 2 * latitude_error / 2
|
||||
+ longitude_error / 2 * longitude_error / 2 );
|
||||
return confidence;
|
||||
}
|
||||
}; // class ezcod
|
||||
|
||||
|
||||
//
|
||||
// ezcod::rscodec -- Reed-Solomon parity codec
|
||||
// ezcod::bits -- distribution of lat/lon precision in each code symbol
|
||||
//
|
||||
// Quickly establishes an extra bit of precision for Longitude, and then evenly distributes
|
||||
// future precision between lat/lon, always maintaining extra precision for Longitude.
|
||||
//
|
||||
template < unsigned P, unsigned L >
|
||||
const ezpwd::RS<31,31-P> ezcod<P,L>::rscodec;
|
||||
|
||||
// Number of lat/lon bits represented for each location symbol
|
||||
template < unsigned P, unsigned L >
|
||||
const typename ezcod<P,L>::bits_t
|
||||
ezcod<P,L>::bits = {
|
||||
{
|
||||
// bits per symbol lat lon
|
||||
ezcod<P,L>::bits_t::value_type( 2, 3 ),
|
||||
ezcod<P,L>::bits_t::value_type( 2, 3 ),
|
||||
ezcod<P,L>::bits_t::value_type( 3, 2 ),
|
||||
// -- --
|
||||
// 7 8
|
||||
ezcod<P,L>::bits_t::value_type( 2, 3 ),
|
||||
ezcod<P,L>::bits_t::value_type( 3, 2 ),
|
||||
ezcod<P,L>::bits_t::value_type( 2, 3 ),
|
||||
// -- --
|
||||
// 14 16
|
||||
ezcod<P,L>::bits_t::value_type( 3, 2 ),
|
||||
ezcod<P,L>::bits_t::value_type( 2, 3 ),
|
||||
ezcod<P,L>::bits_t::value_type( 3, 2 ),
|
||||
// -- --
|
||||
// 22 23
|
||||
ezcod<P,L>::bits_t::value_type( 2, 3 ),
|
||||
ezcod<P,L>::bits_t::value_type( 3, 2 ),
|
||||
ezcod<P,L>::bits_t::value_type( 2, 3 ),
|
||||
// -- --
|
||||
// 29 31
|
||||
}
|
||||
};
|
||||
|
||||
// Total number of parts that lat/lon is subdivided into, for that number of location symbols.
|
||||
template < unsigned P, unsigned L >
|
||||
const typename ezcod<P,L>::parts_t
|
||||
ezcod<P,L>::parts = {
|
||||
{
|
||||
// parts per symbol lat parts lon parts lat lon bits
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 2, 1UL << 3 ), // 2, 3
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 4, 1UL << 6 ), // 2, 3
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 7, 1UL << 8 ), // 3, 2
|
||||
// -- --
|
||||
// 7 8
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 9, 1UL << 11 ), // 2, 3
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 12, 1UL << 13 ), // 3, 2
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 14, 1UL << 16 ), // 2, 3
|
||||
// -- --
|
||||
// 14 16
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 17, 1UL << 18 ), // 3, 2
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 19, 1UL << 21 ), // 2, 3
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 22, 1UL << 23 ), // 3, 2
|
||||
// -- --
|
||||
// 22 23
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 24, 1UL << 26 ), // 2, 3
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 27, 1UL << 28 ), // 3, 2
|
||||
ezcod<P,L>::parts_t::value_type( 1UL << 29, 1UL << 31 ), // 2, 3
|
||||
// -- --
|
||||
// 29 31
|
||||
}
|
||||
};
|
||||
} // namespace ezpwd
|
||||
|
||||
#endif // _EZPWD_EZCOD
|
||||
344
src/ezpwd/output
Normal file
344
src/ezpwd/output
Normal file
@@ -0,0 +1,344 @@
|
||||
#ifndef _EZPWD_OUTPUT
|
||||
#define _EZPWD_OUTPUT
|
||||
|
||||
#include <cstdio>
|
||||
#include <streambuf>
|
||||
#include <iostream>
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
//
|
||||
// ezpwd::hexchr -- escape/hexify char c, output using func/meth f, in width w >= 2
|
||||
// ezpwd::hexify -- hexify something that can be converted to an unsigned char
|
||||
// ezpwd::hexout -- hexify each element in the range (beg,end], limited by stream's width
|
||||
//
|
||||
// std::ostream << ezpwd::hexify( c ) // output any char escaped/hex
|
||||
// std::ostream << ezpwd::hexout( beg, end ) // output any char iterator to ostream
|
||||
// std::ostream << std::vector<unsigend char>
|
||||
// std::ostream << std::array<unsigend char, N>
|
||||
// ezpwd::hexchr( c, [](unsigned char c){...;} )// output escaped/hex char via functor
|
||||
// ezpwd::hexout( beg, end, FILE* ) // output any char iterator to FILE*
|
||||
//
|
||||
// Output unprintable unsigned char data in hex, escape printable/whitespace data.
|
||||
//
|
||||
namespace ezpwd {
|
||||
|
||||
struct hexify {
|
||||
unsigned char c;
|
||||
std::streamsize w;
|
||||
explicit hexify(
|
||||
unsigned char _c,
|
||||
std::streamsize _w = 2 )
|
||||
: c( _c )
|
||||
, w( _w )
|
||||
{ ; }
|
||||
explicit hexify(
|
||||
char _c,
|
||||
std::streamsize _w = 2 )
|
||||
: c( (unsigned char)_c )
|
||||
, w( _w )
|
||||
{ ; }
|
||||
};
|
||||
struct hexstr {
|
||||
const std::string &s;
|
||||
explicit hexstr(
|
||||
const std::string &_s )
|
||||
: s( _s )
|
||||
{ ; }
|
||||
};
|
||||
|
||||
template <typename F> // a functor taking a char
|
||||
void
|
||||
hexchr( unsigned char c, F f = []( unsigned char c ) { std::cout.put( c );}, size_t w = 2 )
|
||||
{
|
||||
for ( ; w > 2; --w )
|
||||
f( ' ' );
|
||||
if ( std::isprint( c ) || std::isspace( c )
|
||||
|| c == '\0' || c == '\a' || c == '\b' || c == 0x1B ) { // '\e' is not standard
|
||||
switch ( c ) {
|
||||
case 0x00: f( '\\' ); f( '0' ); break; // NUL
|
||||
case 0x07: f( '\\' ); f( 'a' ); break; // BEL
|
||||
case 0x08: f( '\\' ); f( 'b' ); break; // BS
|
||||
case 0x09: f( '\\' ); f( 't' ); break; // HT
|
||||
case 0x0A: f( '\\' ); f( 'n' ); break; // LF
|
||||
case 0x0B: f( '\\' ); f( 'v' ); break; // VT
|
||||
case 0x0C: f( '\\' ); f( 'f' ); break; // FF
|
||||
case 0x0D: f( '\\' ); f( 'r' ); break; // CR
|
||||
case 0x1B: f( '\\' ); f( 'e' ); break; // ESC
|
||||
case '\"': f( '\\' ); f( '"' ); break; // "
|
||||
case '\'': f( '\\' ); f( '\''); break; // '
|
||||
case '\\': f( '\\' ); f( '\\'); break; // '\'
|
||||
default: f( ' ' ); f( c ); // space, any other printable character
|
||||
}
|
||||
} else {
|
||||
f( "0123456789ABCDEF"[( c >> 4 ) & 0x0f ] );
|
||||
f( "0123456789ABCDEF"[( c >> 0 ) & 0x0f ] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const ezpwd::hexify&rhs )
|
||||
{
|
||||
ezpwd::hexchr( rhs.c, [ &lhs ]( unsigned char c ) { lhs.put( c ); }, rhs.w );
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template < typename iter_t >
|
||||
inline
|
||||
std::ostream &hexout(
|
||||
std::ostream &lhs,
|
||||
const iter_t &beg,
|
||||
const iter_t &end )
|
||||
{
|
||||
std::streamsize wid = lhs.width( 0 );
|
||||
int col = 0;
|
||||
for ( auto i = beg; i != end; ++i ) {
|
||||
if ( wid && col == wid ) {
|
||||
lhs << std::endl;
|
||||
col = 0;
|
||||
}
|
||||
lhs << hexify( *i );
|
||||
++col;
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template < typename iter_t >
|
||||
inline
|
||||
std::FILE *hexout(
|
||||
const iter_t &beg,
|
||||
const iter_t &end,
|
||||
std::FILE *lhs )
|
||||
{
|
||||
for ( auto i = beg; i != end; ++i ) {
|
||||
ezpwd::hexchr( *i, [ lhs ]( unsigned char c ) { std::fputc( c, lhs ); } );
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const ezpwd::hexstr&rhs )
|
||||
{
|
||||
return ezpwd::hexout( lhs, rhs.s.begin(), rhs.s.end() );
|
||||
}
|
||||
} // namespace ezpwd
|
||||
|
||||
namespace std {
|
||||
template < size_t S >
|
||||
inline
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const std::array<unsigned char,S>
|
||||
&rhs )
|
||||
{
|
||||
return ezpwd::hexout( lhs, rhs.begin(), rhs.end() );
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const std::vector<unsigned char>
|
||||
&rhs )
|
||||
{
|
||||
return ezpwd::hexout( lhs, rhs.begin(), rhs.end() );
|
||||
}
|
||||
|
||||
//
|
||||
// <ostream&> << pair<T,U>
|
||||
// <ostream&> << set<T> -- sorted by T
|
||||
// <ostream&> << map<T,U> -- sorted by T (key)
|
||||
// <ostream&> << vector<T>
|
||||
//
|
||||
// Handle output of various container types.
|
||||
//
|
||||
// Output pairs and sets of pairs, respecting specified widths (as appropriate). For example
|
||||
// a set of pairs of integeters 's', if output as "... << std::setw( 13 ) << s;", would yield:
|
||||
//
|
||||
// ( 1, 2) ( 3, 4) ...
|
||||
//
|
||||
|
||||
template <class T, class U>
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const std::pair<T,U> &rhs )
|
||||
{
|
||||
std::streamsize w = std::max( std::streamsize( 0 ),
|
||||
std::streamsize( lhs.width() - 3 ));
|
||||
lhs << std::setw( 0 )
|
||||
<< '(' << std::setw( w / 2 ) << rhs.first
|
||||
<< ',' << std::setw( w - w / 2 ) << rhs.second
|
||||
<< ')';
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const std::set<T> &rhs )
|
||||
{
|
||||
std::streamsize w = lhs.width(); // If width is set, use if for each item
|
||||
for ( typename std::set<T>::const_iterator
|
||||
si = rhs.begin()
|
||||
; si != rhs.end()
|
||||
; ++si ) {
|
||||
if ( si != rhs.begin())
|
||||
lhs << ' ';
|
||||
lhs << std::setw( w ) << *si;
|
||||
}
|
||||
lhs << std::setw( 0 ); // If container empty, must clear
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const std::map<T,U>&rhs )
|
||||
{
|
||||
std::streamsize w = lhs.width(); // If width is set, use if for each item
|
||||
std::vector<T> key;
|
||||
for ( typename std::map<T,U>::const_iterator
|
||||
mi = rhs.begin()
|
||||
; mi != rhs.end()
|
||||
; ++mi )
|
||||
key.push_back( mi->first );
|
||||
std::sort( key.begin(), key.end() );
|
||||
for ( typename std::vector<T>::const_iterator
|
||||
ki = key.begin()
|
||||
; ki != key.end()
|
||||
; ++ki ) {
|
||||
if ( ki != key.begin())
|
||||
lhs << ' ';
|
||||
lhs << std::setw( w ) << *rhs.find( *ki );
|
||||
}
|
||||
lhs << std::setw( 0 ); // If container empty, must clear
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::ostream &operator<<(
|
||||
std::ostream &lhs,
|
||||
const std::vector<T> &rhs )
|
||||
{
|
||||
for( size_t i = 0; i < rhs.size(); ++i ) {
|
||||
if ( i )
|
||||
lhs << ", ";
|
||||
lhs << rhs[i];
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
//
|
||||
// ezpwd::buf_t -- describe a C string buffer, to allow C++ output operations
|
||||
// ezpwd::streambuf_to_buf_t -- output charcters, always NUL terminated
|
||||
//
|
||||
// <buf_t> << ... -- Copy the <string> into the C <char*,size_t> buffer, always NUL terminating
|
||||
//
|
||||
// Copies <string> contents into buffer, and always NUL-terminates. Returns advanced buf_t (NOT
|
||||
// including the terminating NUL, suitable for repeating ... << <string> operations.
|
||||
//
|
||||
// std::ostream( &<streambuf_to_buf_t> ) << ...
|
||||
//
|
||||
// Use standard ostream operations to send output to a C buffer, always NUL
|
||||
// terminating, and never exceeding capacity.
|
||||
//
|
||||
namespace ezpwd {
|
||||
|
||||
typedef std::pair<char *,size_t>
|
||||
buf_t;
|
||||
|
||||
class streambuf_to_buffer
|
||||
: public std::streambuf {
|
||||
private:
|
||||
char *_buf;
|
||||
size_t _siz;
|
||||
public:
|
||||
//
|
||||
// streambuf_to_buf_t -- remember buf_t details
|
||||
// ~streambuf_to_buf_t -- no virtual destructor required; nothing to clean up
|
||||
//
|
||||
streambuf_to_buffer(
|
||||
char *buf,
|
||||
size_t siz )
|
||||
: _buf( buf )
|
||||
, _siz( siz )
|
||||
{
|
||||
if ( _siz > 0 )
|
||||
*_buf = 0;
|
||||
}
|
||||
explicit streambuf_to_buffer(
|
||||
const buf_t &buf )
|
||||
: streambuf_to_buffer( buf.first, buf.second )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
//
|
||||
// overflow -- Append c, always NUL terminating
|
||||
//
|
||||
virtual int overflow(
|
||||
int c )
|
||||
{
|
||||
if ( _siz <= 1 )
|
||||
return EOF; // No room for c and NUL; EOF
|
||||
if ( EOF == c )
|
||||
return 0; // EOF provided; do nothing
|
||||
--_siz;
|
||||
*_buf++ = char( c );
|
||||
*_buf = 0;
|
||||
return c;
|
||||
}
|
||||
}; // class streambuf_to_buffer
|
||||
|
||||
} // namespace ezpwd
|
||||
|
||||
namespace std {
|
||||
|
||||
inline
|
||||
ezpwd::buf_t operator<<(
|
||||
const ezpwd::buf_t &buf,
|
||||
const std::string &str )
|
||||
{
|
||||
if ( buf.first && str.size() + 1 <= buf.second ) {
|
||||
std::copy( str.begin(), str.end(), buf.first );
|
||||
buf.first[str.size()] = 0;
|
||||
return ezpwd::buf_t( buf.first + str.size(), buf.second - str.size() );
|
||||
} else if ( buf.first && buf.second ) {
|
||||
std::copy( str.begin(), str.begin() + buf.second - 1, buf.first );
|
||||
buf.first[buf.second-1] = 0;
|
||||
return ezpwd::buf_t( buf.first + buf.second - 1, 1 );
|
||||
}
|
||||
return buf; // NULL pointer or 0 size.
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// <std::string> << ...
|
||||
//
|
||||
// Useful (but inefficient) standard output formatting directly to a std::string. Use only for
|
||||
// testing code, for efficiency reasons...
|
||||
//
|
||||
template < typename T >
|
||||
inline
|
||||
std::string operator<<(
|
||||
const std::string &lhs,
|
||||
const T &rhs )
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << rhs;
|
||||
return std::string( lhs ).append( oss.str() );
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _EZPWD_OUTPUT
|
||||
168
src/ezpwd/rs
Normal file
168
src/ezpwd/rs
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
|
||||
*
|
||||
* Copyright (c) 2014, Hard Consulting Corporation.
|
||||
*
|
||||
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
|
||||
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. See the LICENSE file at the top of the
|
||||
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. c++/ezpwd/rs_base
|
||||
* is redistributed under the terms of the LGPL, regardless of the overall licensing terms.
|
||||
*
|
||||
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||
* the GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _EZPWD_RS
|
||||
#define _EZPWD_RS
|
||||
|
||||
#include "rs_base"
|
||||
|
||||
//
|
||||
// ezpwd::RS<SYMBOLS,PAYLOAD> -- Implements an RS(SYMBOLS,PAYLOAD) codec
|
||||
// ezpwd::RS_CCSDS<...> -- CCSDS standard 8-bit R-S codec
|
||||
//
|
||||
// Support for Reed-Solomon codecs for symbols of 2 to 16 bits is supported. The R-S "codeword"
|
||||
// for an N-bit symbol is defined to be 2^N-1 symbols in size. For example, for 5-bit symbols,
|
||||
// 2^5-1 == 31, so the notation for defining an Reed-Solomon codec for 5-bit symbols is always:
|
||||
// RS(31,PAYLOAD), where PAYLOAD is always some value less than 31. The difference is the number of
|
||||
// "parity" symbols.
|
||||
//
|
||||
// For example, to define an RS codeword of 31 symbols w/ 4 symbols of parity and up to 27
|
||||
// symbols of data, you would say: RS(31,27). Of course, you can supply smaller amounts of data;
|
||||
// the balance is assumed to be NUL (zero) symbols.
|
||||
//
|
||||
namespace ezpwd {
|
||||
|
||||
//
|
||||
// __RS( ... ) -- Define a reed-solomon codec
|
||||
//
|
||||
// @SYMBOLS: Total number of symbols; must be a power of 2 minus 1, eg 2^8-1 == 255
|
||||
// @PAYLOAD: The maximum number of non-parity symbols, eg 253 ==> 2 parity symbols
|
||||
// @POLY: A primitive polynomial appropriate to the SYMBOLS size
|
||||
// @FCR: The first consecutive root of the Reed-Solomon generator polynomial
|
||||
// @PRIM: The primitive root of the generator polynomial
|
||||
//
|
||||
# define __RS_TYP( TYPE, SYMBOLS, PAYLOAD, POLY, FCR, PRIM ) \
|
||||
ezpwd::reed_solomon< \
|
||||
TYPE, \
|
||||
ezpwd::log_< (SYMBOLS) + 1 >::value, \
|
||||
(SYMBOLS) - (PAYLOAD), FCR, PRIM, \
|
||||
ezpwd::gfpoly< \
|
||||
ezpwd::log_< (SYMBOLS) + 1 >::value, \
|
||||
POLY >>
|
||||
|
||||
# define __RS( NAME, TYPE, SYMBOLS, PAYLOAD, POLY, FCR, PRIM ) \
|
||||
__RS_TYP( TYPE, SYMBOLS, PAYLOAD, POLY, FCR, PRIM ) { \
|
||||
NAME() \
|
||||
: __RS_TYP( TYPE, SYMBOLS, PAYLOAD, POLY, FCR, PRIM )() \
|
||||
{;} \
|
||||
}
|
||||
|
||||
//
|
||||
// RS<SYMBOLS, PAYLOAD> -- Standard partial specializations for Reed-Solomon codec type access
|
||||
//
|
||||
// Normally, Reed-Solomon codecs are described with terms like RS(255,252). Obtain various
|
||||
// standard Reed-Solomon codecs using macros of a similar form, eg. RS<255, 252>. Standard PLY,
|
||||
// FCR and PRM values are provided for various SYMBOL sizes, along with appropriate basic types
|
||||
// capable of holding all internal Reed-Solomon tabular data.
|
||||
//
|
||||
// In order to provide "default initialization" of const RS<...> types, a user-provided
|
||||
// default constructor must be provided.
|
||||
//
|
||||
template < size_t SYMBOLS, size_t PAYLOAD > struct RS;
|
||||
template < size_t PAYLOAD > struct RS< 3, PAYLOAD> : public __RS( RS, uint8_t, 3, PAYLOAD, 0x7, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 7, PAYLOAD> : public __RS( RS, uint8_t, 7, PAYLOAD, 0xb, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 15, PAYLOAD> : public __RS( RS, uint8_t, 15, PAYLOAD, 0x13, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 31, PAYLOAD> : public __RS( RS, uint8_t, 31, PAYLOAD, 0x25, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 63, PAYLOAD> : public __RS( RS, uint8_t, 63, PAYLOAD, 0x43, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 127, PAYLOAD> : public __RS( RS, uint8_t, 127, PAYLOAD, 0x89, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 255, PAYLOAD> : public __RS( RS, uint8_t, 255, PAYLOAD, 0x11d, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 511, PAYLOAD> : public __RS( RS, uint16_t, 511, PAYLOAD, 0x211, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 1023, PAYLOAD> : public __RS( RS, uint16_t, 1023, PAYLOAD, 0x409, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 2047, PAYLOAD> : public __RS( RS, uint16_t, 2047, PAYLOAD, 0x805, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 4095, PAYLOAD> : public __RS( RS, uint16_t, 4095, PAYLOAD, 0x1053, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS< 8191, PAYLOAD> : public __RS( RS, uint16_t, 8191, PAYLOAD, 0x201b, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS<16383, PAYLOAD> : public __RS( RS, uint16_t, 16383, PAYLOAD, 0x4443, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS<32767, PAYLOAD> : public __RS( RS, uint16_t, 32767, PAYLOAD, 0x8003, 1, 1 );
|
||||
template < size_t PAYLOAD > struct RS<65535, PAYLOAD> : public __RS( RS, uint16_t, 65535, PAYLOAD, 0x1100b, 1, 1 );
|
||||
|
||||
template < size_t SYMBOLS, size_t PAYLOAD > struct RS_CCSDS;
|
||||
template < size_t PAYLOAD > struct RS_CCSDS<255, PAYLOAD> : public __RS( RS_CCSDS, uint8_t, 255, PAYLOAD, 0x187, 112, 11 );
|
||||
|
||||
|
||||
//
|
||||
// strength<PARITY> -- compute strength (given N parity symbols) of R-S correction
|
||||
//
|
||||
// Returns a confidence strength rating, which is the ratio:
|
||||
//
|
||||
// 100 - ( errors * 2 + erasures ) * 100 / parity
|
||||
//
|
||||
// which is proportional to the number of parity symbols unused by the reported number of
|
||||
// corrected symbols. If 0, then all parity resources were consumed to recover the R-S
|
||||
// codeword, and we can have no confidence in the result. If -'ve, indicates more parity
|
||||
// resources were consumed than available, indicating that the result is likely incorrect.
|
||||
//
|
||||
// Accounts for the fact that a signalled erasure may not be reported in the corrected
|
||||
// position vector, if the symbol happens to match the computed value. Note that even if the
|
||||
// error or erasure occurs within the "parity" portion of the codeword, this doesn't reduce the
|
||||
// effective strength -- all symbols in the R-S complete codeword are equally effective in
|
||||
// recovering any other symbol in error/erasure.
|
||||
//
|
||||
template < size_t PARITY >
|
||||
int strength(
|
||||
int corrected,
|
||||
const std::vector<int>&erasures, // original erasures positions
|
||||
const std::vector<int>&positions ) // all reported correction positions
|
||||
{
|
||||
// -'ve indicates R-S failure; all parity consumed, but insufficient to correct the R-S
|
||||
// codeword. Missing an unknown number of additional required parity symbols, so just
|
||||
// return -1 as the strength.
|
||||
if ( corrected < 0 ) {
|
||||
#if defined( DEBUG ) && DEBUG >= 2
|
||||
std::cout
|
||||
<< corrected << " corrections (R-S decode failure) == -1 confidence"
|
||||
<< std::endl;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
if ( corrected != int( positions.size() ))
|
||||
EZPWD_RAISE_OR_RETURN( std::runtime_error, "inconsistent R-S decode results", -1 );
|
||||
|
||||
// Any erasures that don't turn out to contain errors are not returned as fixed positions.
|
||||
// However, they have consumed parity resources. Search for each erasure location in
|
||||
// positions, and if not reflected, add to the corrected/missed counters.
|
||||
int missed = 0;
|
||||
for ( auto e : erasures ) {
|
||||
if ( std::find( positions.begin(), positions.end(), e ) == positions.end() ) {
|
||||
++corrected;
|
||||
++missed;
|
||||
#if defined( DEBUG ) && DEBUG >= 2
|
||||
std::cout
|
||||
<< corrected << " corrections (R-S erasure missed): " << e
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
int errors = corrected - erasures.size();
|
||||
int consumed= errors * 2 + erasures.size();
|
||||
int confidence= 100 - consumed * 100 / PARITY;
|
||||
#if defined( DEBUG ) && DEBUG >= 2
|
||||
std::cout
|
||||
<< corrected << " corrections (R-S decode success)"
|
||||
<< " at: " << positions
|
||||
<< ", " << erasures.size() + missed
|
||||
<< " erasures (" << missed
|
||||
<< " unreported) at: " << erasures
|
||||
<< ") ==> " << errors
|
||||
<< " errors, and " << consumed << " / " << PARITY
|
||||
<< " parity used == " << confidence
|
||||
<< "% confidence"
|
||||
<< std::endl;
|
||||
#endif
|
||||
return confidence;
|
||||
}
|
||||
|
||||
} // namespace ezpwd
|
||||
|
||||
#endif // _EZPWD_RS
|
||||
1344
src/ezpwd/rs_base
Normal file
1344
src/ezpwd/rs_base
Normal file
File diff suppressed because it is too large
Load Diff
1188
src/ezpwd/serialize
Normal file
1188
src/ezpwd/serialize
Normal file
File diff suppressed because it is too large
Load Diff
57
src/ezpwd/serialize_definitions
Normal file
57
src/ezpwd/serialize_definitions
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
//
|
||||
// The encoder/decoder tables for all ezpwd::serialize::... base<N> codecs
|
||||
//
|
||||
// Must be included in exactly one C++ compilation unit.
|
||||
//
|
||||
|
||||
#ifndef _EZPWD_SERIALIZE_DEFINITIONS
|
||||
#define _EZPWD_SERIALIZE_DEFINITIONS
|
||||
|
||||
#include "serialize"
|
||||
|
||||
//
|
||||
// base<16> tables for RFC4864 standard
|
||||
//
|
||||
const constexpr std::array<char,16>
|
||||
ezpwd::serialize::hex<16>::encoder;
|
||||
const constexpr std::array<char,127>
|
||||
ezpwd::serialize::hex<16>::decoder;
|
||||
|
||||
//
|
||||
// base<32> tables for RFC4864 standard, and the Hex32, EZPWD and Crockford codecs
|
||||
//
|
||||
const constexpr std::array<char,32>
|
||||
ezpwd::serialize::hex<32>::encoder;
|
||||
const constexpr std::array<char,127>
|
||||
ezpwd::serialize::hex<32>::decoder;
|
||||
const constexpr std::array<char,32>
|
||||
ezpwd::serialize::standard<32>::encoder;
|
||||
const constexpr std::array<char,127>
|
||||
ezpwd::serialize::standard<32>::decoder;
|
||||
const constexpr std::array<char,32>
|
||||
ezpwd::serialize::ezpwd<32>::encoder;
|
||||
const constexpr std::array<char,127>
|
||||
ezpwd::serialize::ezpwd<32>::decoder;
|
||||
const constexpr std::array<char,32>
|
||||
ezpwd::serialize::crockford<32>::encoder;
|
||||
const constexpr std::array<char,127>
|
||||
ezpwd::serialize::crockford<32>::decoder;
|
||||
|
||||
//
|
||||
// base<64> tables for RFC4864 standard (regular and url), and the EZPWD codecs
|
||||
//
|
||||
const constexpr std::array<char,64>
|
||||
ezpwd::serialize::standard<64>::encoder;
|
||||
const constexpr std::array<char,127>
|
||||
ezpwd::serialize::standard<64>::decoder;
|
||||
const constexpr std::array<char,64>
|
||||
ezpwd::serialize::standard_url<64>::encoder;
|
||||
const constexpr std::array<char,127>
|
||||
ezpwd::serialize::standard_url<64>::decoder;
|
||||
const constexpr std::array<char,64>
|
||||
ezpwd::serialize::ezpwd<64>::encoder;
|
||||
const constexpr std::array<char,127>
|
||||
ezpwd::serialize::ezpwd<64>::decoder;
|
||||
|
||||
#endif // _EZPWD_SERIALIZE_DEFINITIONS
|
||||
73
src/ezpwd/timeofday
Normal file
73
src/ezpwd/timeofday
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef _EZPWD_TIMEOFDAY
|
||||
#define _EZPWD_TIMEOFDAY
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
//
|
||||
// ezpwd::timeofday -- Return current time.
|
||||
// ezpwd::epoch -- The UNIX epoch.
|
||||
// ezpwd::seconds -- convert timeval to a real-valued seconds
|
||||
// <timeval> < <timeval>-- less-than comparison on timevals
|
||||
// <timeval> - <timeval>-- difference on timevals
|
||||
//
|
||||
namespace ezpwd {
|
||||
inline
|
||||
timeval timeofday()
|
||||
{
|
||||
timeval tv;
|
||||
::gettimeofday( &tv, NULL );
|
||||
return tv;
|
||||
}
|
||||
|
||||
timeval epoch()
|
||||
{
|
||||
timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
return tv;
|
||||
}
|
||||
|
||||
inline
|
||||
double seconds( const timeval &rhs )
|
||||
{
|
||||
return rhs.tv_usec / 1000000.0 + rhs.tv_sec;
|
||||
}
|
||||
} // namespace ezpwd
|
||||
|
||||
inline
|
||||
bool operator<(
|
||||
const timeval &lhs,
|
||||
const timeval &rhs )
|
||||
{
|
||||
return ( lhs.tv_sec < rhs.tv_sec
|
||||
|| (( lhs.tv_sec == rhs.tv_sec )
|
||||
&& ( lhs.tv_usec < rhs.tv_usec )));
|
||||
}
|
||||
|
||||
inline
|
||||
timeval operator-(
|
||||
const timeval &lhs,
|
||||
timeval rhs ) // copy; adjusted...
|
||||
{
|
||||
timeval result;
|
||||
if ( lhs < rhs ) {
|
||||
result = ezpwd::epoch();
|
||||
} else {
|
||||
// See http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
|
||||
if ( lhs.tv_usec < rhs.tv_usec ) {
|
||||
int sec = ( rhs.tv_usec - lhs.tv_usec ) / 1000000 + 1;
|
||||
rhs.tv_usec -= sec * 1000000;
|
||||
rhs.tv_sec += sec;
|
||||
}
|
||||
if ( lhs.tv_usec - rhs.tv_usec > 1000000 ) {
|
||||
int sec = ( lhs.tv_usec - rhs.tv_usec ) / 1000000;
|
||||
rhs.tv_usec += sec * 1000000;
|
||||
rhs.tv_sec -= sec;
|
||||
}
|
||||
result.tv_sec = lhs.tv_sec - rhs.tv_sec;
|
||||
result.tv_usec = lhs.tv_usec - rhs.tv_usec;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // _EZPWD_TIMEOFDAY
|
||||
1576
src/p25_p2.c
1576
src/p25_p2.c
File diff suppressed because it is too large
Load Diff
276
src/p25p1_block.cpp
Normal file
276
src/p25p1_block.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* p25p1_block.c
|
||||
* P25 Block Bridge for TSBK Deinterleaving, CRC12 and CRC16 checks
|
||||
*
|
||||
* original copyrights for portions used below (OP25, Ossman, and LEH)
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-09 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include <vector>
|
||||
|
||||
typedef std::vector<bool> bit_vector;
|
||||
|
||||
/* adapted from wireshark/plugins/p25/packet-p25cai.c */
|
||||
/* Copyright 2008, Michael Ossmann <mike@ossmann.com> */
|
||||
/* deinterleave and trellis1_2 decoding, count_bits, and find_min*/
|
||||
/* buf is assumed to be a buffer of 12 bytes */
|
||||
|
||||
typedef std::vector<bool> bit_vector;
|
||||
|
||||
//ugly copy and paste below
|
||||
static int count_bits(unsigned int n) {
|
||||
int i = 0;
|
||||
for (i = 0; n != 0; i++)
|
||||
n &= n - 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int find_min(uint8_t list[], int len) {
|
||||
int min = list[0];
|
||||
int index = 0;
|
||||
int unique = 1;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < len; i++) {
|
||||
if (list[i] < min) {
|
||||
min = list[i];
|
||||
index = i;
|
||||
unique = 1;
|
||||
} else if (list[i] == min) {
|
||||
unique = 0;
|
||||
}
|
||||
}
|
||||
/* return -1 if a minimum can't be found */
|
||||
if (!unique)
|
||||
return -1;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int block_deinterleave(bit_vector& bv, unsigned int start, uint8_t* buf)
|
||||
{
|
||||
static const uint16_t deinterleave_tb[] = {
|
||||
0, 1, 2, 3, 52, 53, 54, 55, 100,101,102,103, 148,149,150,151,
|
||||
4, 5, 6, 7, 56, 57, 58, 59, 104,105,106,107, 152,153,154,155,
|
||||
8, 9, 10, 11, 60, 61, 62, 63, 108,109,110,111, 156,157,158,159,
|
||||
12, 13, 14, 15, 64, 65, 66, 67, 112,113,114,115, 160,161,162,163,
|
||||
16, 17, 18, 19, 68, 69, 70, 71, 116,117,118,119, 164,165,166,167,
|
||||
20, 21, 22, 23, 72, 73, 74, 75, 120,121,122,123, 168,169,170,171,
|
||||
24, 25, 26, 27, 76, 77, 78, 79, 124,125,126,127, 172,173,174,175,
|
||||
28, 29, 30, 31, 80, 81, 82, 83, 128,129,130,131, 176,177,178,179,
|
||||
32, 33, 34, 35, 84, 85, 86, 87, 132,133,134,135, 180,181,182,183,
|
||||
36, 37, 38, 39, 88, 89, 90, 91, 136,137,138,139, 184,185,186,187,
|
||||
40, 41, 42, 43, 92, 93, 94, 95, 140,141,142,143, 188,189,190,191,
|
||||
44, 45, 46, 47, 96, 97, 98, 99, 144,145,146,147, 192,193,194,195,
|
||||
48, 49, 50, 51 };
|
||||
|
||||
uint8_t hd[4];
|
||||
int b, d, j;
|
||||
int state = 0;
|
||||
uint8_t codeword;
|
||||
|
||||
static const uint8_t next_words[4][4] = {
|
||||
{0x2, 0xC, 0x1, 0xF},
|
||||
{0xE, 0x0, 0xD, 0x3},
|
||||
{0x9, 0x7, 0xA, 0x4},
|
||||
{0x5, 0xB, 0x6, 0x8}
|
||||
};
|
||||
|
||||
memset(buf, 0, 12);
|
||||
|
||||
for (b=0; b < 98*2; b += 4) {
|
||||
codeword = (bv[start+deinterleave_tb[b+0]] << 3) +
|
||||
(bv[start+deinterleave_tb[b+1]] << 2) +
|
||||
(bv[start+deinterleave_tb[b+2]] << 1) +
|
||||
bv[start+deinterleave_tb[b+3]] ;
|
||||
|
||||
/* try each codeword in a row of the state transition table */
|
||||
for (j = 0; j < 4; j++) {
|
||||
/* find Hamming distance for candidate */
|
||||
hd[j] = count_bits(codeword ^ next_words[state][j]);
|
||||
}
|
||||
/* find the dibit that matches the most codeword bits (minimum Hamming distance) */
|
||||
state = find_min(hd, 4);
|
||||
/* error if minimum can't be found */
|
||||
if(state == -1)
|
||||
return -1; // decode error, return failure
|
||||
/* It also might be nice to report a condition where the minimum is
|
||||
* non-zero, i.e. an error has been corrected. It probably shouldn't
|
||||
* be a permanent failure, though.
|
||||
*
|
||||
* DISSECTOR_ASSERT(hd[state] == 0);
|
||||
*/
|
||||
|
||||
/* append dibit onto output buffer */
|
||||
d = b >> 2; // dibit ctr
|
||||
if (d < 48) {
|
||||
buf[d >> 2] |= state << (6 - ((d%4) * 2));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bd_bridge (int payload[196], uint8_t decoded[12])
|
||||
{
|
||||
std::vector<bool> vc(196,0);
|
||||
int ec = 69; //initialize error value
|
||||
|
||||
//gather payload from tsbk handler and pass it to the block_deinterleave function
|
||||
//and then return its payload back to the tsbk handler
|
||||
|
||||
//initialize our decoded byte buffer with zeroes
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
decoded[i] = 0;
|
||||
}
|
||||
|
||||
//convert/load payload into a vector vc to pass to block_deinterleave
|
||||
for (int i = 0; i < 196; i++)
|
||||
{
|
||||
vc[i] = payload[i];
|
||||
}
|
||||
int block_count = 0; //
|
||||
unsigned int start = block_count*196;
|
||||
|
||||
ec = block_deinterleave(vc, start, decoded);
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
//modified from the LEH ComputeCrcCCITT to accept variable len buffer bits
|
||||
uint16_t ComputeCrcCCITT16b(const uint8_t buf[], unsigned int len)
|
||||
{
|
||||
uint32_t i;
|
||||
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
||||
/* Polynomial x^16 + x^12 + x^5 + 1
|
||||
* Normal = 0x1021
|
||||
* Reciprocal = 0x0811
|
||||
* Reversed = 0x8408
|
||||
* Reversed reciprocal = 0x8810 */
|
||||
uint16_t Polynome = 0x1021;
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
if(((CRC >> 15) & 1) ^ (buf[i] & 1))
|
||||
{
|
||||
CRC = (CRC << 1) ^ Polynome;
|
||||
}
|
||||
else
|
||||
{
|
||||
CRC <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Invert the CRC */
|
||||
CRC ^= 0xFFFF;
|
||||
|
||||
/* Return the CRC */
|
||||
return CRC;
|
||||
} /* End ComputeCrcCCITT() */
|
||||
|
||||
|
||||
//modified from crc12_ok to run a quickie on 16 instead
|
||||
static uint16_t crc16_ok(const uint8_t bits[], unsigned int len) {
|
||||
uint16_t crc = 0;
|
||||
// fprintf (stderr, "\n LEN = %d", len);
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
crc = crc << 1;
|
||||
crc = crc | bits[i+len];
|
||||
}
|
||||
// uint16_t check = crc16b(bits,len);
|
||||
uint16_t check = ComputeCrcCCITT16b(bits, len);
|
||||
|
||||
if (crc == check)
|
||||
{
|
||||
//fprintf (stderr, " CRC = %04X %04X", crc, check);
|
||||
return (0);
|
||||
}
|
||||
else
|
||||
{
|
||||
//fprintf (stderr, " CRC = %04X %04X", crc, check);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TSBK/LCCH CRC16 x-bit bridge to crc16b and crc16b_okay, wip, may need multi pdu format as well
|
||||
int crc16_lb_bridge (int payload[190], int len)
|
||||
{
|
||||
int err = -2;
|
||||
uint16_t crc = -2;
|
||||
uint8_t buf[190] = {0};
|
||||
|
||||
for (int i = 0; i < len+16; i++) //add +16 here so we load the entire frame but only run crc on the len portion
|
||||
{
|
||||
buf[i] = payload[i];
|
||||
}
|
||||
err = crc16_ok(buf, len);
|
||||
return (err);
|
||||
}
|
||||
|
||||
//borrowing crc12 from OP25
|
||||
static uint16_t crc12(const uint8_t bits[], unsigned int len) {
|
||||
uint16_t crc=0;
|
||||
static const unsigned int K = 12;
|
||||
//g12(x) = x12 + x11 + x7 + x4 + x2 + x + 1
|
||||
static const uint8_t poly[K+1] = {1,1,0,0,0,1,0,0,1,0,1,1,1}; // p25 p2 crc 12 poly
|
||||
uint8_t buf[256];
|
||||
if (len+K > sizeof(buf)) {
|
||||
//fprintf (stderr, "crc12: buffer length %u exceeds maximum %u\n", len+K, sizeof(buf));
|
||||
return 0;
|
||||
}
|
||||
memset (buf, 0, sizeof(buf));
|
||||
for (unsigned int i=0; i<len; i++){
|
||||
buf[i] = bits[i];
|
||||
}
|
||||
for (unsigned int i=0; i<len; i++)
|
||||
if (buf[i])
|
||||
for (unsigned int j=0; j<K+1; j++)
|
||||
buf[i+j] ^= poly[j];
|
||||
for (unsigned int i=0; i<K; i++){
|
||||
crc = (crc << 1) + buf[len + i];
|
||||
}
|
||||
return crc ^ 0xfff;
|
||||
}
|
||||
|
||||
//borrowing crc12_ok from OP25
|
||||
static uint16_t crc12_ok(const uint8_t bits[], unsigned int len) {
|
||||
uint16_t crc = 0;
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
crc = crc << 1;
|
||||
crc = crc | bits[i+len];
|
||||
}
|
||||
uint16_t check = crc12(bits,len);
|
||||
|
||||
if (crc == check)
|
||||
{
|
||||
//fprintf (stderr, " CRC = %03X %03X", crc, check);
|
||||
return (0);
|
||||
}
|
||||
else
|
||||
{
|
||||
//fprintf (stderr, " CRC = %03X %03X", crc, check);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//xCCH CRC12 x-bit bridge to crc12b and crc12b_okay
|
||||
int crc12_xb_bridge (int payload[190], int len)
|
||||
{
|
||||
int err = -5;
|
||||
uint16_t crc = -2;
|
||||
uint8_t buf[190] = {0};
|
||||
|
||||
//need to load up the 12 crc bits as well, else it will fail on the check, but not on the crc calc
|
||||
for (int i = 0; i < len+12; i++)
|
||||
{
|
||||
buf[i] = payload[i];
|
||||
}
|
||||
err = crc12_ok(buf, len);
|
||||
return (err);
|
||||
}
|
||||
@@ -1,3 +1,8 @@
|
||||
//remove below ifdef if problems arise
|
||||
#if __cplusplus > 199711L
|
||||
#define register // Deprecated in C++11.
|
||||
#endif // #if __cplusplus > 199711L
|
||||
//end ifdef to fix compiler warnings
|
||||
|
||||
#include "p25p1_check_hdu.h"
|
||||
#include "ReedSolomon.hpp"
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
//remove below ifdef if problems arise
|
||||
#if __cplusplus > 199711L
|
||||
#define register // Deprecated in C++11.
|
||||
#endif // #if __cplusplus > 199711L
|
||||
//end ifdef to fix compiler warnings
|
||||
|
||||
#include "p25p1_check_ldu.h"
|
||||
|
||||
|
||||
@@ -474,14 +474,14 @@ processHDU(dsd_opts* opts, dsd_state* state)
|
||||
//set vc counter to 0
|
||||
state->p25vc = 0;
|
||||
|
||||
if (state->payload_algid != 0x80) //print on payload == 1
|
||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " HDU ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX MFID: 0x%02X", algidhex, kidhex, mihex1, mihex2, mihex3, state->payload_mfid);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (state->payload_algid != 0x80) //print on payload == 1
|
||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " ENC \n");
|
||||
|
||||
@@ -376,14 +376,14 @@ processLDU2 (dsd_opts * opts, dsd_state * state)
|
||||
|
||||
}
|
||||
|
||||
if (state->payload_algid != 0x80) //print on payload == 1
|
||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " LDU2 ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX", algidhex, kidhex, mihex1, mihex2, mihex3);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (state->payload_algid != 0x80) //print on payload == 1
|
||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " ENC \n");
|
||||
|
||||
122
src/p25p1_tsbk.c
Normal file
122
src/p25p1_tsbk.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* p25p1_tsbk.c
|
||||
* P25 TSBK Handler for Network Status Broadcast (WACN, SYSID, NAC/CC), etc.
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-09 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
void processTSBK(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
//process TSDU/TSBK and look for relevant data (WACN, SYSID, other goodies later on)
|
||||
int tsbkbit[196] = {0}; //tsbk bit array, 196 trellis encoded bits
|
||||
int tsbk_dibit[98] = {0};
|
||||
|
||||
int dibit = 0;
|
||||
uint8_t tsbk_byte[12] = {0}; //12 byte return from bd_bridge (block_deinterleave)
|
||||
int tsbk_decoded_bits[190] = {0}; //decoded bits from tsbk_bytes for sending to crc16_lb_bridge
|
||||
int i, j, k, b;
|
||||
int ec = -2; //error value returned from (block_deinterleave)
|
||||
int checksum = -2; //checksum returned from crc16, 0 is valid, anything else is invalid
|
||||
int skipdibit = 14; //initial status dibit will occur at 14, then add 35 each time it occurs
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
k = 0;
|
||||
//originally skipped 303 dibits, instead we collect three reps of 101 (98 valid dibits)
|
||||
for (i = 0; i < 101; i++)
|
||||
{
|
||||
//rip dibits directly from the symbol capture file
|
||||
//this method causes issues for some reason, probably to do with mixing reading types? don't know why?
|
||||
// if (opts->audio_in_type == 4) //4
|
||||
// {
|
||||
// dibit = fgetc(opts->symbolfile);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// dibit = getDibit(opts, state);
|
||||
// if (opts->inverted_p2 == 1)
|
||||
// {
|
||||
// dibit = (dibit ^ 2);
|
||||
// }
|
||||
// }
|
||||
|
||||
dibit = getDibit(opts, state);
|
||||
|
||||
if (i+(j*101) != skipdibit) //
|
||||
{
|
||||
tsbk_dibit[k] = dibit;
|
||||
tsbkbit[k*2] = (dibit >> 1) & 1;
|
||||
tsbkbit[k*2+1] = (dibit & 1);
|
||||
k++;
|
||||
}
|
||||
|
||||
if (i+(j*101) == skipdibit) //
|
||||
{
|
||||
skipdibit += 35;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//send tsbkbit to block_deinterleave and return tsbk_byte
|
||||
ec = bd_bridge(tsbkbit, tsbk_byte);
|
||||
|
||||
//too many bit manipulations!
|
||||
k = 0;
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
tsbk_decoded_bits[k] = ((tsbk_byte[i] << j) & 0x80) >> 7;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
//crc check works now using the ComputeCrcCCITT method and not the OP25 method (bug?)
|
||||
int err = -2;
|
||||
err = crc16_lb_bridge(tsbk_decoded_bits, 80);
|
||||
if (ec != 0)
|
||||
{
|
||||
//fprintf (stderr, "BAD BLOCK DEINTERLEAVE");
|
||||
}
|
||||
if (err != 0)
|
||||
{
|
||||
//fprintf (stderr, "BAD CRC16");
|
||||
}
|
||||
|
||||
//set our WACN and SYSID here now that we have valid ec and crc/checksum
|
||||
if (err == 0 && ec == 0 && tsbk_byte[0] == 0x3B)
|
||||
{
|
||||
//only set IF these values aren't already hard set by the user
|
||||
if (state->p2_hardset == 0)
|
||||
{
|
||||
state->p2_wacn = (tsbk_byte[3] << 12) | (tsbk_byte[4] << 4) | (tsbk_byte[5] >> 4);
|
||||
state->p2_sysid = ((tsbk_byte[5] & 0xF) << 8) | tsbk_byte[6];
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
fprintf (stderr, "\n Network Status Broadcast TSBK \n");
|
||||
fprintf (stderr, " WACN [%05llX] SYSID [%03llX] NAC [%03llX]", state->p2_wacn, state->p2_sysid, state->p2_cc);
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
//state->p2_cc = state->nac; //set in frame_sync line 450 instead after valid bch check
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// print dump for debug eval
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%s",KCYN);
|
||||
fprintf (stderr, "\n P25 TSBK Byte Payload ");
|
||||
fprintf (stderr, " BD = %d CRC = %d \n ", ec, err);
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", tsbk_byte[i]);
|
||||
}
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
|
||||
//reset for next rep
|
||||
ec = -2;
|
||||
checksum = -2;
|
||||
}
|
||||
fprintf (stderr, "\n"); //line break on loop end
|
||||
}
|
||||
Reference in New Issue
Block a user