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:
lwvmobile 2022-09-17 11:44:45 -04:00 committed by GitHub
parent 0e9c2fc3dc
commit 90700e7d28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 8521 additions and 487 deletions

View File

@ -1,9 +1,19 @@
# Digital Speech Decoder - Florida Man Edition
This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch as well. This code also borrows snippets, inspiration, and ideas from other open source works including [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25"), [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), and [LFSR](https://github.com/mattames/LFSR "LFSR"). This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu and hrh17 for the many hours of wav samples submitted by them.
This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch as well. This code also borrows snippets, inspiration, and ideas from other open source works including [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25"), [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"). This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu and hrh17 for the many hours of wav samples submitted by them.
## 2022.09.17 Update ##
P25 Phase 2 Audio decoding has been implemented into DSD-FME. Currently, Phase 2 will only work from OP25 symbol capture bin files, or from wav files/SDR applications/RTL input that are sourced from FSK4 sources. CQPSK (LSM/H-D8PSK) will not work from audio sources. With the addition of Phases 2 audio, a new default decoder class has been implemented, XDMA, which is P25 Phase 1, Phase 2, DMR BS and MS (DMR Stereo). Furthermore, very limited Phase 1 TSBK support and Phase 2 FAcch/SaCCH/LCCH has been worked in just to get Network Status Broadcasts, which can fill in the required P2 parameters for WACN, SYSID, and CC for Phase 2 frame de-scrambling, and active voice call information.
With the implementation of the XDMA decoder class (default decoder), the CLI switches for DMR Stereo has been repurposed.
-T option will now open a per call wav file saving when used with NCurses Terminal, otherwise it will write wav files to slot 1 and slot 2 wav files.
Anybody using dsd-fme -fr -T ...... should now just use dsd-fme -ft instead!
To see the up to date CLI options, please look at the help options with the -h CLI option.
## 2022.08.12 Update ##
A new menu system has been introduced in the NCURSES Terminal. While running DSD-FME, a user can simply use the ESC or arrow keys to open the menu. This is extremely beneficial when wanting to change settings on the fly, and also to alleviate the need to use complex start up command line arguments. All of the previous command line arguments still work and will get you up faster, but if you would rather configure after start up, or change settings while running, the menu system is very helpful in that regard.
A new menu system has been introduced in the NCURSES Terminal. Also includes support for LRRP to text file type of choice and reading OP25 symbol capture bin files.
[![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/pulseaudio/dsd-fme.png)](https://www.youtube.com/watch?v=TqTfAfaPJ4s "DSD-FME Update 2022.08.12")
@ -11,14 +21,6 @@ To get started with the new menu system, simply launch with:
`dsd-fme -N 2> log.txt`
Virtually all the examples listed below can now be set up on the fly with the menu system, no need to check the help or refer to this page.
This release also has provisional support for playing back OP25 capture bin files, and the creation and playback of its own capture bin files as well (most likely not OP25 compatible). Mileage may vary. Be sure to choose the decoder option required when playing a bin file back, do not rely on auto-detect. Capture Bin files can be used to reliably replay P25, DMR BS, and NXDN decodings, and can replace the need for MBE file saving in the case of DMR Stereo, allowing for a complete replay of all events on a system, rather than just voice only.
Per Call wav file creation has been implemented, currently only for DMR Stereo. Complete audio decode wav is available for all other decoder types. LRRP has been revamped, working more consistently, and an option to dump LRRP data to ~/lrrp.txt in the user home directory now exists, and can be imported into open-source QGIS to provide mapping data for LRRP output. Simply open the newly included map file in QGIS and point it at your geographic region. (Be sure the lrrp.txt file exists, and has data populated in it prior to opening the map file in QGIS, otherwise, the map layer may not want to show back up!)
DMR Data Header decoding has been revamped, and most data burst decoding types tightened up and code cleaned. FEC error checking now implemented on CACH and Burst types, cleaning up some erroneous slot and burst errors.
## 2022.06.20 Update - Current Users Please Read!! ##
The executable for this project has been changed from `dsd` to `dsd-fme` so after pulling or cloning the lastest version, make sure to call the software with `dsd-fme`.
@ -28,20 +30,22 @@ DMR Stereo method added for listening to voice audio in both TDMA channels/slots
### Example Usage and Note!
`dsd-fme` or `./dsd-fme` may need to be used in your usage, depending on if you are running it from the build folder, or if you have run make install. You will need to tweak the examples to your particular usage. Users can always check `dsd-fme -h` for help and all command line switches to use.
`dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR, P25P1, D-STAR, and X2-TDMA decoding. To use other decoding methods which cannot be auto detected, please use the following command line switches. Make sure to route audio into and out of DSD-FME using pavucontrol and virtual sinks as needed.
`dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR BS/MS, P25 (1 and 2) and X2-TDMA decoding. To use other decoding methods which cannot be auto detected, please use the following command line switches. Make sure to route audio into and out of DSD-FME using pavucontrol and virtual sinks as needed.
```
-ft XDMA decoder class (P25 1 and 2, DMR BS/MS, X2-TDMA)
-fa Legacy Auto
-fi NXDN48
-fn NXDN96
-fp ProVoice
-fm dPMR, also may need to use -xd if inverted dPMR.
-fr DMR, also may need to use -xr if inverted DMR, use -T for Stereo, and -F for passive sync
-fr DMR, also may need to use -xr if inverted DMR.
-f1 P25P1
-fx X2-TDMA
```
## Example Usage - New DMR Stereo, Ncurses Terminal, Pulse Input/Output, and Log Console to file
`dsd-fme -fr -T -N 2> voice.log`
## Example Usage - New XDMA Decoder, Ncurses Terminal, Pulse Input/Output, and Log Console to file
`dsd-fme -ft -N 2> voice.log`
and in a second terminal tab, same folder, run
@ -90,12 +94,12 @@ and in a second terminal tab, same folder, run
## Roadmap
The Current list of objectives include:
1. Include P25 P2 Audio decoding with capture bin files, then RTL input and/or disc tap input.
1. ~~Include P25 P2 Audio decoding with capture bin files~~, new demodulators and input method(s) (considering liquid-dsp).
~~Random Tinkering~~ ~~More Random Tinkering~~
2. ~~Implemented Pulse Audio~~ ~~Remove remaining PortAudio code,~~ ~~improved Pulse Audio for stereo output and channel/slot separation~~ ~~and reimplement wav file saving using DMR Stereo method for stereo wav files, or seperate as per call wav files.~~
3. ~~Improve NXDN and DMR support~~ Continue to improve ~~NXDN and DMR~~ all data and voice decoding.
3. ~~Improve NXDN and DMR support~~ Continue to improve all data and voice decoding.
4. ~~More Concise Printouts - Ncurses~~

View File

@ -32,7 +32,7 @@
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#define __USE_XOPEN //compiler warning on this, needed for strptime, seems benign
//#define __USE_XOPEN //compiler warning on this, need to rewrite dsd_file so it doesn't use strptime
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
@ -279,6 +279,7 @@ typedef struct
char symbol_out_file[1024];
char lrrp_out_file[1024];
short int symbol_out;
short int mbe_out; //flag for mbe out, don't attempt fclose more than once
SNDFILE *wav_out_f;
SNDFILE *wav_out_fR;
SNDFILE *wav_out_raw;
@ -291,6 +292,8 @@ typedef struct
int frame_x2tdma;
int frame_p25p1;
int frame_p25p2;
int inverted_p2;
int p2counter;
int frame_nxdn48;
int frame_nxdn96;
int frame_dmr;
@ -464,6 +467,7 @@ typedef struct
unsigned long long int K2;
unsigned long long int K3;
unsigned long long int K4;
int M;
int menuopen;
unsigned int debug_audio_errors;
@ -567,15 +571,27 @@ typedef struct
unsigned int dmrburstL;
unsigned int dmrburstR;
unsigned long long int R;
unsigned long long int RR;
unsigned long long int H;
unsigned long long int HYTL;
unsigned long long int HYTR;
int DMRvcL;
int DMRvcR;
// int block_count;
short int dmr_encL;
short int dmr_encR;
//P2 variables
unsigned long long int p2_wacn;
unsigned long long int p2_sysid;
unsigned long long int p2_cc; //p1 NAC
int p2_hardset; //flag for checking whether or not P2 wacn and sysid are hard set by user
int p2_scramble_offset; //offset counter for scrambling application
int p2_vch_chan_num; //vch channel number (0 or 1, not the 0-11 TS)
int ess_b[2][96]; //external storage for ESS_B fragments
int fourv_counter[2]; //external reference counter for ESS_B fragment collection
int p2_is_lcch; //flag to tell us when a frame is lcch and not sacch
//dstar header for ncurses
unsigned char dstarradioheader[41];
@ -602,20 +618,8 @@ typedef struct
#define INV_P25P1_SYNC "333331331133111131311111"
#define P25P1_SYNC "111113113311333313133333"
//preliminary P2 sync
// static const uint64_t P25P2_FRAME_SYNC_MAGIC = 0x575D57F7FFLL; // 40 bits
// static const uint64_t P25P2_FRAME_SYNC_REV_P = 0x575D57F7FFLL ^ 0xAAAAAAAAAALL;
#define P25P2_SYNC "11131131111333133333"
#define INV_P25P2_SYNC "33313313333111311111"
// static const uint64_t P25P2_FRAME_SYNC_MASK = 0xFFFFFFFFFFLL; //wonder what the mask was for if its all F
//
// static const uint64_t P25P2_FRAME_SYNC_N1200 = 0x0104015155LL;
//#define P25P2_SYNC_N ""
// static const uint64_t P25P2_FRAME_SYNC_X2400 = 0xa8a2a8d800LL;
//#define P25P2_SYNC_X ""
// static const uint64_t P25P2_FRAME_SYNC_P1200 = 0xfefbfeaeaaLL;
//#define P25P2_SYNC_P ""
#define X2TDMA_BS_VOICE_SYNC "113131333331313331113311"
#define X2TDMA_BS_DATA_SYNC "331313111113131113331133"
@ -750,6 +754,7 @@ void processX2TDMAvoice (dsd_opts * opts, dsd_state * state);
void processDSTAR_HD (dsd_opts * opts, dsd_state * state);
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
void processP2(dsd_opts * opts, dsd_state * state); //P2
void processTSBK(dsd_opts * opts, dsd_state * state);
short dmr_filter(short sample);
short nxdn_filter(short sample);
@ -915,6 +920,16 @@ void CDMRTrellisPointsToDibits(const unsigned char* points, signed char* dibits)
void CDMRTrellisBitsToTribits(const unsigned char* payload, unsigned char* tribits);
bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload);
//Phase 2 Helper Functions
int ez_rs28_ess (int payload[96], int parity[168]); //ezpwd bridge for FME
int ez_rs28_facch (int payload[156], int parity[114]); //ezpwd bridge for FME
int ez_rs28_sacch (int payload[180], int parity[132]); //ezpwd bridge for FME
unsigned long long int isch_lookup (unsigned long long int isch); //isch map lookup
int bd_bridge (int payload[196], uint8_t decoded[12]); //bridge to Michael Ossmann Block De-interleaver and 1/2 rate trellis decoder
// uint16_t crc16(uint8_t buf[], int len);
int crc16_lb_bridge (int payload[190], int len);
int crc12_xb_bridge (int payload[190], int len);
#ifdef __cplusplus
}
#endif

View File

@ -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;

View File

@ -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;

View File

@ -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?

View File

@ -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++)
{

View File

@ -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);

View File

@ -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
}
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

1188
src/ezpwd/serialize Normal file

File diff suppressed because it is too large Load Diff

View 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
View 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

File diff suppressed because it is too large Load Diff

276
src/p25p1_block.cpp Normal file
View 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);
}

View File

@ -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"

View File

@ -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"

View File

@ -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");

View File

@ -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
View 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
}