diff --git a/include/dpmr_const.h b/include/dpmr_const.h new file mode 100644 index 0000000..cac7039 --- /dev/null +++ b/include/dpmr_const.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 DSD Author + * GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0) + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + + +//#ifdef __cplusplus +//extern "C" { +//#endif + + +#ifndef _MAIN +extern const int dPmrW[36]; +extern const int dPmrX[36]; +extern const int dPmrY[36]; +extern const int dPmrZ[36]; + +//#else +/* + * dPMR AMBE interleave schedule + */ +// bit 1 +const int dPmrW[36] = { + 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 2, + 0, 2, 0, 2, 0, 2, + 0, 2, 0, 2, 0, 2 +}; + +const int dPmrX[36] = { + 23, 10, 22, 9, 21, 8, + 20, 7, 19, 6, 18, 5, + 17, 4, 16, 3, 15, 2, + 14, 1, 13, 0, 12, 10, + 11, 9, 10, 8, 9, 7, + 8, 6, 7, 5, 6, 4 +}; + +// bit 0 +const int dPmrY[36] = { + 0, 2, 0, 2, 0, 2, + 0, 2, 0, 3, 0, 3, + 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3 +}; + +const int dPmrZ[36] = { + 5, 3, 4, 2, 3, 1, + 2, 0, 1, 13, 0, 12, + 22, 11, 21, 10, 20, 9, + 19, 8, 18, 7, 17, 6, + 16, 5, 15, 4, 14, 3, + 13, 2, 12, 1, 11, 0 +}; + +#endif + +//#ifdef __cplusplus +//} +//#endif diff --git a/include/dsd.h b/include/dsd.h index 9f423b3..e703fb3 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -147,9 +147,116 @@ typedef struct } NxdnElementsContent_t; +//LEH DMR + +enum +{ + MODE_UNENCRYPTED, + MODE_BASIC_PRIVACY, + MODE_ENHANCED_PRIVACY_ARC4, + MODE_ENHANCED_PRIVACY_AES256, + MODE_HYTERA_BASIC_40_BIT, + MODE_HYTERA_BASIC_128_BIT, + MODE_HYTERA_BASIC_256_BIT +}; + +//Read Solomon (12,9) constant +#define RS_12_9_DATASIZE 9 +#define RS_12_9_CHECKSUMSIZE 3 + +//Read Solomon (12,9) struct +typedef struct +{ + uint8_t data[RS_12_9_DATASIZE+RS_12_9_CHECKSUMSIZE]; +} rs_12_9_codeword_t; + +// Maximum degree of various polynomials. +#define RS_12_9_POLY_MAXDEG (RS_12_9_CHECKSUMSIZE*2) + +typedef struct +{ + uint8_t data[RS_12_9_POLY_MAXDEG]; +} rs_12_9_poly_t; + +#define RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND 0 +#define RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CORRECTED 1 +#define RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CANT_BE_CORRECTED 2 +typedef uint8_t rs_12_9_correct_errors_result_t; + +typedef struct +{ + uint8_t bytes[3]; +} rs_12_9_checksum_t; + +typedef struct +{ + char VoiceFrame[3][72]; /* 3 x 72 bit of voice per time slot frame */ + char Sync[48]; /* 48 bit of data or SYNC per time slot frame */ +}TimeSlotRawVoiceFrame_t; + + +typedef struct +{ + char DeInterleavedVoiceSample[3][4][24]; /* 3 x (4 x 24) deinterleaved bit of voice per time slot frame */ +}TimeSlotDeinterleavedVoiceFrame_t; + + +typedef struct +{ + int errs1[3]; /* 3 x errors #1 computed when demodulate the AMBE voice bit of the frame */ + int errs2[3]; /* 3 x errors #2 computed when demodulate the AMBE voice bit of the frame */ + char AmbeBit[3][49]; /* 3 x 49 bit of AMBE voice of the frame */ +}TimeSlotAmbeVoiceFrame_t; + +typedef struct +{ + unsigned int ProtectFlag; + unsigned int Reserved; + unsigned int FullLinkControlOpcode; + unsigned int FeatureSetID; + unsigned int ServiceOptions; + unsigned int GroupAddress; /* Destination ID or TG (in Air Interface format) */ + unsigned int SourceAddress; /* Source ID (in Air Interface format) */ + unsigned int DataValidity; /* 0 = All Full LC data are incorrect ; 1 = Full LC data are correct (CRC checked OK) */ +}FullLinkControlPDU_t; + +typedef struct +{ + TimeSlotRawVoiceFrame_t TimeSlotRawVoiceFrame[6]; /* A voice superframe contains 6 timeslot voice frame */ + TimeSlotDeinterleavedVoiceFrame_t TimeSlotDeinterleavedVoiceFrame[6]; + TimeSlotAmbeVoiceFrame_t TimeSlotAmbeVoiceFrame[6]; + FullLinkControlPDU_t FullLC; +} TimeSlotVoiceSuperFrame_t; //end borrow from LEH +//LEH dPMR +/* Could only be 2 or 4 */ +#define NB_OF_DPMR_VOICE_FRAME_TO_DECODE 2 +typedef struct +{ + unsigned char RawVoiceBit[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][72]; + unsigned int errs1[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4]; /* 8 x errors #1 computed when demodulate the AMBE voice bit of the frame */ + unsigned int errs2[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4]; /* 8 x errors #2 computed when demodulate the AMBE voice bit of the frame */ + unsigned char AmbeBit[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][49]; /* 8 x 49 bit of AMBE voice of the frame */ + unsigned char CCHData[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][48]; + unsigned int CCHDataHammingOk[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + unsigned char CCHDataCRC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + unsigned int CCHDataCrcOk[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + unsigned char CalledID[8]; + unsigned int CalledIDOk; + unsigned char CallingID[8]; + unsigned int CallingIDOk; + unsigned int FrameNumbering[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + unsigned int CommunicationMode[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + unsigned int Version[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + unsigned int CommsFormat[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + unsigned int EmergencyPriority[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + unsigned int Reserved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + unsigned char SlowData[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + unsigned int ColorCode[NB_OF_DPMR_VOICE_FRAME_TO_DECODE / 2]; +} dPMRVoiceFS2Frame_t; +// typedef struct { int onesymbol; @@ -241,6 +348,15 @@ typedef struct int reset_state; int payload; + //LEH DMR Stuff + int EncryptionMode; + + //LEH dPMR stuff + unsigned int dPMR_curr_frame_is_encrypted; + int dPMR_next_part_of_superframe; + int inverted_dpmr; + int frame_dpmr; + } dsd_opts; typedef struct @@ -295,7 +411,7 @@ typedef struct int numtdulc; int firstframe; char slot0light[8]; - char slot1light[8]; + //char slot1light[8]; float aout_gain; float aout_max_buf[200]; float *aout_max_buf_p; @@ -356,9 +472,47 @@ typedef struct char ambe_ciphered[49]; char ambe_deciphered[49]; //end borrow + //LEH DMR struff + unsigned int color_code; + unsigned int color_code_ok; + unsigned int PI; + unsigned int PI_ok; + unsigned int LCSS; + unsigned int LCSS_ok; + unsigned int dmr_fid; + unsigned int dmr_so; + char slot1light[8]; + char slot2light[8]; + int directmode; + TimeSlotVoiceSuperFrame_t TS1SuperFrame; + TimeSlotVoiceSuperFrame_t TS2SuperFrame; + //LEH dPMR stuff + dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame; + + //These flags are used to known the DMR frame + //printing format (hex or binary) + int printDMRRawVoiceFrameHex; + int printDMRRawVoiceFrameBin; + int printDMRRawDataFrameHex; + int printDMRRawDataFrameBin; + int printDMRAmbeVoiceSampleHex; + int printDMRAmbeVoiceSampleBin; + + //These flags are used to known the dPMR frame + //orinting format (hex or binary) + int printdPMRRawVoiceFrameHex; + int printdPMRRawVoiceFrameBin; + int printdPMRRawDataFrameHex; + int printdPMRRawDataFrameBin; + int printdPMRAmbeVoiceSampleHex; + int printdPMRAmbeVoiceSampleBin; + + unsigned char * dpmr_caller_id; + unsigned char * dpmr_target_id; + int dpmr_color_code; #ifdef TRACE_DSD char debug_prefix; @@ -421,11 +575,40 @@ typedef struct #define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311" #define PROVOICE_EA_SYNC "31131311331331111133131311311133" +//LEH dPMR +#define DPMR_FRAME_SYNC_1 "111333331133131131111313" + +/* dPMR Frame Sync 2 - 24 bits sequence + * HEX : 5F F7 7D + * Binary : 0101 1111 1111 0111 0111 1101 + * Dibit : 1 1 3 3 3 3 1 3 1 3 3 1 */ +#define DPMR_FRAME_SYNC_2 "113333131331" + +/* dPMR Frame Sync 3 - 24 bits sequence + * HEX : 7D DF F5 + * Binary : 0111 1101 1101 1111 1111 0101 + * Dibit : 1 3 3 1 3 1 3 3 3 3 1 1 */ +#define DPMR_FRAME_SYNC_3 "133131333311" + +/* dPMR Frame Sync 4 - 48 bits sequence + * HEX : FD 55 F5 DF 7F DD + * Binary : 1111 1101 0101 0101 1111 0101 1101 1111 0111 1111 1101 1101 + * Dibit : 3 3 3 1 1 1 1 1 3 3 1 1 3 1 3 3 1 3 3 3 3 1 3 1 */ +#define DPMR_FRAME_SYNC_4 "333111113311313313333131" + +/* dPMR Frame Sync 1 to 4 - Inverted */ +#define INV_DPMR_FRAME_SYNC_1 "333111113311313313333131" +#define INV_DPMR_FRAME_SYNC_2 "331111313113" +#define INV_DPMR_FRAME_SYNC_3 "311313111133" +#define INV_DPMR_FRAME_SYNC_4 "111333331133131131111313" + +// /* * function prototypes */ void processDMRdata (dsd_opts * opts, dsd_state * state); void processDMRvoice (dsd_opts * opts, dsd_state * state); +void processdPMRvoice (dsd_opts * opts, dsd_state * state); void processAudio (dsd_opts * opts, dsd_state * state); void playRawAudio (dsd_opts * opts, dsd_state * state); //added this one HERE HERE void openPulseInput (dsd_opts * opts); //not sure if we need to just pass opts, or opts and state yet @@ -538,6 +721,83 @@ void ScrambledNXDNVoiceBit(int * LfsrValue, char * BufferIn, char * BufferOut, i void NxdnEncryptionStreamGeneration (dsd_opts* opts, dsd_state* state, uint8_t KeyStream[1664]); void processMbeFrameEncrypted (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24], char ambe_keystream[49], char imbe_keystream[88]); + +//LEH dPMR +void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state); +void printdPMRAmbeVoiceSample(dsd_opts * opts, dsd_state * state); +void printdPMRRawVoiceFrame (dsd_opts * opts, dsd_state * state); +//dPMR functions +void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferOut, uint32_t NbOfBitToScramble); +void DeInterleave6x12DPmrBit(uint8_t * BufferIn, uint8_t * BufferOut); +uint8_t CRC7BitdPMR(uint8_t * BufferIn, uint32_t BitLength); +uint8_t CRC8BitdPMR(uint8_t * BufferIn, uint32_t BitLength); +void ConvertAirInterfaceID(uint32_t AI_ID, uint8_t ID[8]); +int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]); +//LEH DMR +void ProcessDMREncryption (dsd_opts * opts, dsd_state * state); //consider if this is needed +void DMRDataFrameProcess(dsd_opts * opts, dsd_state * state); +void DMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state); +//BPTC (Block Product Turbo Code) functions +void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output); +uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]); +uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]); +uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32]); + +//Read Solomon (12,9) functions +void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome); +uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome); +rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found); +rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword); + +//SYNC DMR data extraction functions +void ProcessDmrVoiceLcHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]); +void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]); +void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state); +uint16_t ComputeCrcCCITT(uint8_t * DMRData); +uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask); +uint8_t ComputeCrc5Bit(uint8_t * DMRData); +uint8_t * DmrAlgIdToStr(uint8_t AlgID); +uint8_t * DmrAlgPrivacyModeToStr(uint32_t PrivacyMode); + + +void Hamming_7_4_init(); +void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits); +bool Hamming_7_4_decode(unsigned char *rxBits); + +void Hamming_12_8_init(); +void Hamming_12_8_encode(unsigned char *origBits, unsigned char *encodedBits); +bool Hamming_12_8_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords); + +void Hamming_13_9_init(); +void Hamming_13_9_encode(unsigned char *origBits, unsigned char *encodedBits); +bool Hamming_13_9_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords); + +void Hamming_15_11_init(); +void Hamming_15_11_encode(unsigned char *origBits, unsigned char *encodedBits); +bool Hamming_15_11_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords); + +void Hamming_16_11_4_init(); +void Hamming_16_11_4_encode(unsigned char *origBits, unsigned char *encodedBits); +bool Hamming_16_11_4_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords); + +void Golay_20_8_init(); +void Golay_20_8_encode(unsigned char *origBits, unsigned char *encodedBits); +bool Golay_20_8_decode(unsigned char *rxBits); + +void Golay_23_12_init(); +void Golay_23_12_encode(unsigned char *origBits, unsigned char *encodedBits); +bool Golay_23_12_decode(unsigned char *rxBits); + +void Golay_24_12_init(); +void Golay_24_12_encode(unsigned char *origBits, unsigned char *encodedBits); +bool Golay_24_12_decode(unsigned char *rxBits); + +void QR_16_7_6_init(); +void QR_16_7_6_encode(unsigned char *origBits, unsigned char *encodedBits); +bool QR_16_7_6_decode(unsigned char *rxBits); + +void InitAllFecFunction(void); + //end borrow from LEH void resetState (dsd_state * state); diff --git a/include/fec.h b/include/fec.h new file mode 100644 index 0000000..6180f2d --- /dev/null +++ b/include/fec.h @@ -0,0 +1,1448 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB. // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "dsd.h" + +unsigned char Hamming_7_4_m_corr[8]; //!< single bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Hamming_7_4_m_G[7*4] = { + 1, 0, 0, 0, 1, 0, 1, + 0, 1, 0, 0, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 1, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Hamming_7_4_m_H[7*3] = { + 1, 1, 1, 0, 1, 0, 0, + 0, 1, 1, 1, 0, 1, 0, + 1, 1, 0, 1, 0, 0, 1 +// 0 1 2 3 <- correctable bit positions +}; + + +// ======================================================================================== + + +unsigned char Hamming_12_8_m_corr[16]; //!< single bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Hamming_12_8_m_G[12*8] = { + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Hamming_12_8_m_H[12*4] = { + 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, + 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, + 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1 +// 0 1 2 3 4 5 6 7 <- correctable bit positions +}; + + +// ======================================================================================== + + +unsigned char Hamming_13_9_m_corr[16]; //!< single bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Hamming_13_9_m_G[13*9] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1 +}; + +//!< Parity check matrix of bits +const unsigned char Hamming_13_9_m_H[13*4] = { + 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, + 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, + 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, +// 0 1 2 3 4 5 6 7 8 <- correctable bit positions +}; + + +// ======================================================================================== + + +unsigned char Hamming_15_11_m_corr[16]; //!< single bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Hamming_15_11_m_G[15*11] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Hamming_15_11_m_H[15*4] = { + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, + 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, +// 0 1 2 3 4 5 6 7 8 9 10 <- correctable bit positions +}; + + +// ======================================================================================== + + +unsigned char Hamming_16_11_4_m_corr[32]; //!< single bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Hamming_16_11_4_m_G[16*11] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1 +}; + +//!< Parity check matrix of bits +const unsigned char Hamming_16_11_4_m_H[16*5] = { + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, + 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1 +}; + + +// ======================================================================================== + + +unsigned char Golay_20_8_m_corr[4096][3]; //!< up to 3 bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Golay_20_8_m_G[20*8] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Golay_20_8_m_H[20*12] = { + 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + + +// ======================================================================================== + + +unsigned char Golay_23_12_m_corr[2048][3]; //!< up to 3 bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Golay_23_12_m_G[23*12] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Golay_23_12_m_H[23*11] = { + 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + + +// ======================================================================================== + + +unsigned char Golay_24_12_m_corr[4096][3]; //!< up to 3 bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Golay_24_12_m_G[24*12] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Golay_24_12_m_H[24*12] = { + 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + + +// ======================================================================================== + + +unsigned char QR_16_7_6_m_corr[512][2]; //!< up to 2 bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char QR_16_7_6_m_G[16*7] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char QR_16_7_6_m_H[16*9] = { + 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + + +// ======================================================================================== + + +void Hamming_7_4_init() +{ + // correctable bit positions given syndrome bits as index (see above) + memset(Hamming_7_4_m_corr, 0xFF, 8); // initialize with all invalid positions + Hamming_7_4_m_corr[0b101] = 0; + Hamming_7_4_m_corr[0b111] = 1; + Hamming_7_4_m_corr[0b110] = 2; + Hamming_7_4_m_corr[0b011] = 3; + Hamming_7_4_m_corr[0b100] = 4; + Hamming_7_4_m_corr[0b010] = 5; + Hamming_7_4_m_corr[0b001] = 6; +} + +// Not very efficient but encode is used for unit testing only +void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 7); + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 7; j++) + { + encodedBits[j] += origBits[i] * Hamming_7_4_m_G[7*i + j]; + } + } + + for (i = 0; i < 7; i++) + { + encodedBits[i] %= 2; + } +} + +bool Hamming_7_4_decode(unsigned char *rxBits) // corrects in place +{ + unsigned int syndromeI = 0; // syndrome index + int is = 0; + + for (is = 0; is < 3; is++) + { + syndromeI += (((rxBits[0] * Hamming_7_4_m_H[7*is + 0]) + + (rxBits[1] * Hamming_7_4_m_H[7*is + 1]) + + (rxBits[2] * Hamming_7_4_m_H[7*is + 2]) + + (rxBits[3] * Hamming_7_4_m_H[7*is + 3]) + + (rxBits[4] * Hamming_7_4_m_H[7*is + 4]) + + (rxBits[5] * Hamming_7_4_m_H[7*is + 5]) + + (rxBits[6] * Hamming_7_4_m_H[7*is + 6])) % 2) << (2-is); + } + + if (syndromeI > 0) + { + if (Hamming_7_4_m_corr[syndromeI] == 0xFF) + { + return false; + } + else + { + rxBits[Hamming_7_4_m_corr[syndromeI]] ^= 1; // flip bit + } + } + + return true; +} + + +// ======================================================================================== + + +void Hamming_12_8_init() +{ + // correctable bit positions given syndrome bits as index (see above) + memset(Hamming_12_8_m_corr, 0xFF, 16); // initialize with all invalid positions + Hamming_12_8_m_corr[0b1110] = 0; + Hamming_12_8_m_corr[0b0111] = 1; + Hamming_12_8_m_corr[0b1010] = 2; + Hamming_12_8_m_corr[0b0101] = 3; + Hamming_12_8_m_corr[0b1011] = 4; + Hamming_12_8_m_corr[0b1100] = 5; + Hamming_12_8_m_corr[0b0110] = 6; + Hamming_12_8_m_corr[0b0011] = 7; + Hamming_12_8_m_corr[0b1000] = 8; + Hamming_12_8_m_corr[0b0100] = 9; + Hamming_12_8_m_corr[0b0010] = 10; + Hamming_12_8_m_corr[0b0001] = 11; +} + +// Not very efficient but encode is used for unit testing only +void Hamming_12_8_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 12); + + for (i = 0; i < 8; i++) + { + for (j = 0; j < 12; j++) + { + encodedBits[j] += origBits[i] * Hamming_12_8_m_G[12*i + j]; + } + } + + for (i = 0; i < 12; i++) + { + encodedBits[i] %= 2; + } +} + +bool Hamming_12_8_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords) +{ + bool correctable = true; + int ic = 0; + int is = 0; + int syndromeI = 0; // syndrome index + + for (ic = 0; ic < nbCodewords; ic++) + { + // calculate syndrome + + syndromeI = 0; // syndrome index + + for (is = 0; is < 4; is++) + { + syndromeI += (((rxBits[12*ic + 0] * Hamming_12_8_m_H[12*is + 0]) + + (rxBits[12*ic + 1] * Hamming_12_8_m_H[12*is + 1]) + + (rxBits[12*ic + 2] * Hamming_12_8_m_H[12*is + 2]) + + (rxBits[12*ic + 3] * Hamming_12_8_m_H[12*is + 3]) + + (rxBits[12*ic + 4] * Hamming_12_8_m_H[12*is + 4]) + + (rxBits[12*ic + 5] * Hamming_12_8_m_H[12*is + 5]) + + (rxBits[12*ic + 6] * Hamming_12_8_m_H[12*is + 6]) + + (rxBits[12*ic + 7] * Hamming_12_8_m_H[12*is + 7]) + + (rxBits[12*ic + 8] * Hamming_12_8_m_H[12*is + 8]) + + (rxBits[12*ic + 9] * Hamming_12_8_m_H[12*is + 9]) + + (rxBits[12*ic + 10] * Hamming_12_8_m_H[12*is + 10]) + + (rxBits[12*ic + 11] * Hamming_12_8_m_H[12*is + 11])) % 2) << (3-is); + } + + // correct bit + + if (syndromeI > 0) // single bit error correction + { + if (Hamming_12_8_m_corr[syndromeI] == 0xFF) // uncorrectable error + { + correctable = false; + } + else + { + rxBits[Hamming_12_8_m_corr[syndromeI]] ^= 1; // flip bit + } + } + + // move information bits + memcpy(&decodedBits[8*ic], &rxBits[12*ic], 8); + } + + return correctable; +} + + +// ======================================================================================== + + +void Hamming_13_9_init() +{ + // correctable bit positions given syndrome bits as index (see above) + memset(Hamming_13_9_m_corr, 0xFF, 16); // initialize with all invalid positions + Hamming_13_9_m_corr[0b1111] = 0; + Hamming_13_9_m_corr[0b1110] = 1; + Hamming_13_9_m_corr[0b0111] = 2; + Hamming_13_9_m_corr[0b1010] = 3; + Hamming_13_9_m_corr[0b0101] = 4; + Hamming_13_9_m_corr[0b1011] = 5; + Hamming_13_9_m_corr[0b1100] = 6; + Hamming_13_9_m_corr[0b0110] = 7; + Hamming_13_9_m_corr[0b0011] = 8; + Hamming_13_9_m_corr[0b1000] = 9; + Hamming_13_9_m_corr[0b0100] = 10; + Hamming_13_9_m_corr[0b0010] = 11; + Hamming_13_9_m_corr[0b0001] = 12; +} + +// Not very efficient but encode is used for unit testing only +void Hamming_13_9_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + memset(encodedBits, 0, 13); + + for (i = 0; i < 9; i++) + { + for (j = 0; j < 13; j++) + { + encodedBits[j] += origBits[i] * Hamming_13_9_m_G[13*i + j]; + } + } + + for (i = 0; i < 13; i++) + { + encodedBits[i] %= 2; + } +} + +bool Hamming_13_9_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords) +{ + bool correctable = true; + int ic = 0, is = 0; + int syndromeI = 0; // syndrome index + + for (ic = 0; ic < nbCodewords; ic++) + { + // calculate syndrome + + syndromeI = 0; // syndrome index + + for (is = 0; is < 4; is++) + { + syndromeI += (((rxBits[13*ic + 0] * Hamming_13_9_m_H[13*is + 0]) + + (rxBits[13*ic + 1] * Hamming_13_9_m_H[13*is + 1]) + + (rxBits[13*ic + 2] * Hamming_13_9_m_H[13*is + 2]) + + (rxBits[13*ic + 3] * Hamming_13_9_m_H[13*is + 3]) + + (rxBits[13*ic + 4] * Hamming_13_9_m_H[13*is + 4]) + + (rxBits[13*ic + 5] * Hamming_13_9_m_H[13*is + 5]) + + (rxBits[13*ic + 6] * Hamming_13_9_m_H[13*is + 6]) + + (rxBits[13*ic + 7] * Hamming_13_9_m_H[13*is + 7]) + + (rxBits[13*ic + 8] * Hamming_13_9_m_H[13*is + 8]) + + (rxBits[13*ic + 9] * Hamming_13_9_m_H[13*is + 9]) + + (rxBits[13*ic + 10] * Hamming_13_9_m_H[13*is + 10]) + + (rxBits[13*ic + 11] * Hamming_13_9_m_H[13*is + 11]) + + (rxBits[13*ic + 12] * Hamming_13_9_m_H[13*is + 12])) % 2) << (3-is); + } + + // correct bit + + if (syndromeI > 0) // single bit error correction + { + if (Hamming_13_9_m_corr[syndromeI] == 0xFF) // uncorrectable error + { + correctable = false; + break; + } + else + { + rxBits[Hamming_13_9_m_corr[syndromeI]] ^= 1; // flip bit + } + } + + // move information bits + if (decodedBits) + { + memcpy(&decodedBits[9*ic], &rxBits[13*ic], 9); + } + } + + return correctable; +} + + +// ======================================================================================== + + +void Hamming_15_11_init() +{ + // correctable bit positions given syndrome bits as index (see above) + memset(Hamming_15_11_m_corr, 0xFF, 16); // initialize with all invalid positions + Hamming_15_11_m_corr[0b1001] = 0; + Hamming_15_11_m_corr[0b1101] = 1; + Hamming_15_11_m_corr[0b1111] = 2; + Hamming_15_11_m_corr[0b1110] = 3; + Hamming_15_11_m_corr[0b0111] = 4; + Hamming_15_11_m_corr[0b1010] = 5; + Hamming_15_11_m_corr[0b0101] = 6; + Hamming_15_11_m_corr[0b1011] = 7; + Hamming_15_11_m_corr[0b1100] = 8; + Hamming_15_11_m_corr[0b0110] = 9; + Hamming_15_11_m_corr[0b0011] = 10; + Hamming_15_11_m_corr[0b1000] = 11; + Hamming_15_11_m_corr[0b0100] = 12; + Hamming_15_11_m_corr[0b0010] = 13; + Hamming_15_11_m_corr[0b0001] = 14; +} + +// Not very efficient but encode is used for unit testing only +void Hamming_15_11_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + memset(encodedBits, 0, 15); + + for (i = 0; i < 11; i++) + { + for (j = 0; j < 15; j++) + { + encodedBits[j] += origBits[i] * Hamming_15_11_m_G[15*i + j]; + } + } + + for (i = 0; i < 15; i++) + { + encodedBits[i] %= 2; + } +} + +bool Hamming_15_11_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords) +{ + bool correctable = true; + int ic = 0, is = 0; + int syndromeI = 0; // syndrome index + + for (ic = 0; ic < nbCodewords; ic++) + { + // calculate syndrome + + syndromeI = 0; // syndrome index + + for (is = 0; is < 4; is++) + { + syndromeI += (((rxBits[15*ic + 0] * Hamming_15_11_m_H[15*is + 0]) + + (rxBits[15*ic + 1] * Hamming_15_11_m_H[15*is + 1]) + + (rxBits[15*ic + 2] * Hamming_15_11_m_H[15*is + 2]) + + (rxBits[15*ic + 3] * Hamming_15_11_m_H[15*is + 3]) + + (rxBits[15*ic + 4] * Hamming_15_11_m_H[15*is + 4]) + + (rxBits[15*ic + 5] * Hamming_15_11_m_H[15*is + 5]) + + (rxBits[15*ic + 6] * Hamming_15_11_m_H[15*is + 6]) + + (rxBits[15*ic + 7] * Hamming_15_11_m_H[15*is + 7]) + + (rxBits[15*ic + 8] * Hamming_15_11_m_H[15*is + 8]) + + (rxBits[15*ic + 9] * Hamming_15_11_m_H[15*is + 9]) + + (rxBits[15*ic + 10] * Hamming_15_11_m_H[15*is + 10]) + + (rxBits[15*ic + 11] * Hamming_15_11_m_H[15*is + 11]) + + (rxBits[15*ic + 12] * Hamming_15_11_m_H[15*is + 12]) + + (rxBits[15*ic + 13] * Hamming_15_11_m_H[15*is + 13]) + + (rxBits[15*ic + 14] * Hamming_15_11_m_H[15*is + 14])) % 2) << (3-is); + } + + // correct bit + + if (syndromeI > 0) // single bit error correction + { + if (Hamming_15_11_m_corr[syndromeI] == 0xFF) // uncorrectable error + { + correctable = false; + break; + } + else + { + rxBits[Hamming_15_11_m_corr[syndromeI]] ^= 1; // flip bit + } + } + + // move information bits + if (decodedBits) + { + memcpy(&decodedBits[11*ic], &rxBits[15*ic], 11); + } + } + + return correctable; +} + + +// ======================================================================================== + + +void Hamming_16_11_4_init() +{ + // correctable bit positions given syndrome bits as index (see above) + memset(Hamming_16_11_4_m_corr, 0xFF, 32); // initialize with all invalid positions + Hamming_16_11_4_m_corr[0b10011] = 0; + Hamming_16_11_4_m_corr[0b11010] = 1; + Hamming_16_11_4_m_corr[0b11111] = 2; + Hamming_16_11_4_m_corr[0b11100] = 3; + Hamming_16_11_4_m_corr[0b01110] = 4; + Hamming_16_11_4_m_corr[0b10101] = 5; + Hamming_16_11_4_m_corr[0b01011] = 6; + Hamming_16_11_4_m_corr[0b10110] = 7; + Hamming_16_11_4_m_corr[0b11001] = 8; + Hamming_16_11_4_m_corr[0b01101] = 9; + Hamming_16_11_4_m_corr[0b00111] = 10; + Hamming_16_11_4_m_corr[0b10000] = 11; + Hamming_16_11_4_m_corr[0b01000] = 12; + Hamming_16_11_4_m_corr[0b00100] = 13; + Hamming_16_11_4_m_corr[0b00010] = 14; + Hamming_16_11_4_m_corr[0b00001] = 15; +} + +// Not very efficient but encode is used for unit testing only +void Hamming_16_11_4_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + memset(encodedBits, 0, 16); + + for (i = 0; i < 11; i++) + { + for (j = 0; j < 16; j++) + { + encodedBits[j] += origBits[i] * Hamming_16_11_4_m_G[16*i + j]; + } + } + + for (i = 0; i < 16; i++) + { + encodedBits[i] %= 2; + } +} + +bool Hamming_16_11_4_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords) +{ + bool correctable = true; + int ic = 0, is = 0; + int syndromeI = 0; // syndrome index + + for (ic = 0; ic < nbCodewords; ic++) + { + // calculate syndrome + syndromeI = 0; // syndrome index + for (is = 0; is < 5; is++) + { + syndromeI += (((rxBits[16*ic + 0] * Hamming_16_11_4_m_H[16*is + 0]) + + (rxBits[16*ic + 1] * Hamming_16_11_4_m_H[16*is + 1]) + + (rxBits[16*ic + 2] * Hamming_16_11_4_m_H[16*is + 2]) + + (rxBits[16*ic + 3] * Hamming_16_11_4_m_H[16*is + 3]) + + (rxBits[16*ic + 4] * Hamming_16_11_4_m_H[16*is + 4]) + + (rxBits[16*ic + 5] * Hamming_16_11_4_m_H[16*is + 5]) + + (rxBits[16*ic + 6] * Hamming_16_11_4_m_H[16*is + 6]) + + (rxBits[16*ic + 7] * Hamming_16_11_4_m_H[16*is + 7]) + + (rxBits[16*ic + 8] * Hamming_16_11_4_m_H[16*is + 8]) + + (rxBits[16*ic + 9] * Hamming_16_11_4_m_H[16*is + 9]) + + (rxBits[16*ic + 10] * Hamming_16_11_4_m_H[16*is + 10]) + + (rxBits[16*ic + 11] * Hamming_16_11_4_m_H[16*is + 11]) + + (rxBits[16*ic + 12] * Hamming_16_11_4_m_H[16*is + 12]) + + (rxBits[16*ic + 13] * Hamming_16_11_4_m_H[16*is + 13]) + + (rxBits[16*ic + 14] * Hamming_16_11_4_m_H[16*is + 14]) + + (rxBits[16*ic + 15] * Hamming_16_11_4_m_H[16*is + 15])) % 2) << (4-is); + } + + // correct bit + + if (syndromeI > 0) // single bit error correction + { + if (Hamming_16_11_4_m_corr[syndromeI] == 0xFF) // uncorrectable error + { + correctable = false; + break; + } + else + { + rxBits[Hamming_16_11_4_m_corr[syndromeI]] ^= 1; // flip bit + } + } + + // move information bits + if (decodedBits) + { + memcpy(&decodedBits[11*ic], &rxBits[16*ic], 11); + } + } + + return correctable; +} + + +// ======================================================================================== + + +void Golay_20_8_init() +{ + int i1 = 0, i2 = 0, i3 = 0, ir = 0, ip = 0; + int syndromeI = 0, syndromeIP = 0; + int ip1 = 0, ip2 = 0, ip3 = 0; + int syndromeIP1 = 0, syndromeIP2 = 0, syndromeIP3 = 0; + + memset(Golay_20_8_m_corr, 0xFF, 3*4096); + + for (i1 = 0; i1 < 8; i1++) + { + for (i2 = i1+1; i2 < 8; i2++) + { + for (i3 = i2+1; i3 < 8; i3++) + { + // 3 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += ((Golay_20_8_m_H[20*ir + i1] + Golay_20_8_m_H[20*ir + i2] + Golay_20_8_m_H[20*ir + i3]) % 2) << (11-ir); + } + + Golay_20_8_m_corr[syndromeI][0] = i1; + Golay_20_8_m_corr[syndromeI][1] = i2; + Golay_20_8_m_corr[syndromeI][2] = i3; + } + + // 2 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += ((Golay_20_8_m_H[20*ir + i1] + Golay_20_8_m_H[20*ir + i2]) % 2) << (11-ir); + } + + Golay_20_8_m_corr[syndromeI][0] = i1; + Golay_20_8_m_corr[syndromeI][1] = i2; + + // 1 possible bit flip left in the parity part + for (ip = 0; ip < 12; ip++) + { + syndromeIP = syndromeI ^ (1 << (11-ip)); + Golay_20_8_m_corr[syndromeIP][0] = i1; + Golay_20_8_m_corr[syndromeIP][1] = i2; + Golay_20_8_m_corr[syndromeIP][2] = 12 + ip; + } + } + + // single bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += Golay_20_8_m_H[20*ir + i1] << (11-ir); + } + + Golay_20_8_m_corr[syndromeI][0] = i1; + + for (ip1 = 0; ip1 < 12; ip1++) // 1 more bit flip in parity + { + syndromeIP1 = syndromeI ^ (1 << (11-ip1)); + Golay_20_8_m_corr[syndromeIP1][0] = i1; + Golay_20_8_m_corr[syndromeIP1][1] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2)); + Golay_20_8_m_corr[syndromeIP2][0] = i1; + Golay_20_8_m_corr[syndromeIP2][1] = 12 + ip1; + Golay_20_8_m_corr[syndromeIP2][2] = 12 + ip2; + } + } + } + + // no bit patterns (in message) -> all in parity + for (ip1 = 0; ip1 < 12; ip1++) // 1 bit flip in parity + { + syndromeIP1 = (1 << (11-ip1)); + Golay_20_8_m_corr[syndromeIP1][0] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2)); + Golay_20_8_m_corr[syndromeIP2][0] = 12 + ip1; + Golay_20_8_m_corr[syndromeIP2][1] = 12 + ip2; + + for (ip3 = ip2+1; ip3 < 12; ip3++) // 1 more bit flip in parity + { + syndromeIP3 = syndromeIP2 ^ (1 << (11-ip3)); + Golay_20_8_m_corr[syndromeIP3][0] = 12 + ip1; + Golay_20_8_m_corr[syndromeIP3][1] = 12 + ip2; + Golay_20_8_m_corr[syndromeIP3][2] = 12 + ip3; + } + } + } +} + +// Not very efficient but encode is used for unit testing only +void Golay_20_8_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 20); + + for (i = 0; i < 8; i++) + { + for (j = 0; j < 20; j++) + { + encodedBits[j] += origBits[i] * Golay_20_8_m_G[20*i + j]; + } + } + + for (i = 0; i < 20; i++) + { + encodedBits[i] %= 2; + } +} + +bool Golay_20_8_decode(unsigned char *rxBits) +{ + unsigned int syndromeI = 0; // syndrome index + int is = 0; + int i = 0; + + for (is = 0; is < 12; is++) + { + syndromeI += (((rxBits[0] * Golay_20_8_m_H[20*is + 0]) + + (rxBits[1] * Golay_20_8_m_H[20*is + 1]) + + (rxBits[2] * Golay_20_8_m_H[20*is + 2]) + + (rxBits[3] * Golay_20_8_m_H[20*is + 3]) + + (rxBits[4] * Golay_20_8_m_H[20*is + 4]) + + (rxBits[5] * Golay_20_8_m_H[20*is + 5]) + + (rxBits[6] * Golay_20_8_m_H[20*is + 6]) + + (rxBits[7] * Golay_20_8_m_H[20*is + 7]) + + (rxBits[8] * Golay_20_8_m_H[20*is + 8]) + + (rxBits[9] * Golay_20_8_m_H[20*is + 9]) + + (rxBits[10] * Golay_20_8_m_H[20*is + 10]) + + (rxBits[11] * Golay_20_8_m_H[20*is + 11]) + + (rxBits[12] * Golay_20_8_m_H[20*is + 12]) + + (rxBits[13] * Golay_20_8_m_H[20*is + 13]) + + (rxBits[14] * Golay_20_8_m_H[20*is + 14]) + + (rxBits[15] * Golay_20_8_m_H[20*is + 15]) + + (rxBits[16] * Golay_20_8_m_H[20*is + 16]) + + (rxBits[17] * Golay_20_8_m_H[20*is + 17]) + + (rxBits[18] * Golay_20_8_m_H[20*is + 18]) + + (rxBits[19] * Golay_20_8_m_H[20*is + 19])) % 2) << (11-is); + } + + if (syndromeI > 0) + { + i = 0; + + for (; i < 3; i++) + { + if (Golay_20_8_m_corr[syndromeI][i] == 0xFF) + { + break; + } + else + { + rxBits[Golay_20_8_m_corr[syndromeI][i]] ^= 1; // flip bit + } + } + + if (i == 0) + { + return false; + } + } + + return true; +} + + +// ======================================================================================== + + +void Golay_23_12_init() +{ + int i1 = 0, i2 = 0, i3 = 0, ir = 0, ip = 0; + int syndromeI = 0, syndromeIP = 0; + int ip1 = 0, ip2 = 0, ip3 = 0; + int syndromeIP1 = 0, syndromeIP2 = 0, syndromeIP3 = 0; + + memset(Golay_23_12_m_corr, 0xFF, 3*2048); + + for (i1 = 0; i1 < 12; i1++) + { + for (i2 = i1+1; i2 < 12; i2++) + { + for (i3 = i2+1; i3 < 12; i3++) + { + // 3 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 11; ir++) + { + syndromeI += ((Golay_23_12_m_H[23*ir + i1] + Golay_23_12_m_H[23*ir + i2] + Golay_23_12_m_H[23*ir + i3]) % 2) << (10-ir); + } + + Golay_23_12_m_corr[syndromeI][0] = i1; + Golay_23_12_m_corr[syndromeI][1] = i2; + Golay_23_12_m_corr[syndromeI][2] = i3; + } + + // 2 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 11; ir++) + { + syndromeI += ((Golay_23_12_m_H[23*ir + i1] + Golay_23_12_m_H[23*ir + i2]) % 2) << (10-ir); + } + + Golay_23_12_m_corr[syndromeI][0] = i1; + Golay_23_12_m_corr[syndromeI][1] = i2; + + // 1 possible bit flip left in the parity part + for (ip = 0; ip < 11; ip++) + { + syndromeIP = syndromeI ^ (1 << (10-ip)); + Golay_23_12_m_corr[syndromeIP][0] = i1; + Golay_23_12_m_corr[syndromeIP][1] = i2; + Golay_23_12_m_corr[syndromeIP][2] = 12 + ip; + } + } + + // single bit patterns + syndromeI = 0; + + for (ir = 0; ir < 11; ir++) + { + syndromeI += Golay_23_12_m_H[23*ir + i1] << (10-ir); + } + + Golay_23_12_m_corr[syndromeI][0] = i1; + + for (ip1 = 0; ip1 < 11; ip1++) // 1 more bit flip in parity + { + syndromeIP1 = syndromeI ^ (1 << (10-ip1)); + Golay_23_12_m_corr[syndromeIP1][0] = i1; + Golay_23_12_m_corr[syndromeIP1][1] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 11; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (10-ip2)); + Golay_23_12_m_corr[syndromeIP2][0] = i1; + Golay_23_12_m_corr[syndromeIP2][1] = 12 + ip1; + Golay_23_12_m_corr[syndromeIP2][2] = 12 + ip2; + } + } + } + + // no bit patterns (in message) -> all in parity + for (ip1 = 0; ip1 < 11; ip1++) // 1 bit flip in parity + { + syndromeIP1 = (1 << (10-ip1)); + Golay_23_12_m_corr[syndromeIP1][0] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 11; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (10-ip2)); + Golay_23_12_m_corr[syndromeIP2][0] = 12 + ip1; + Golay_23_12_m_corr[syndromeIP2][1] = 12 + ip2; + + for (ip3 = ip2+1; ip3 < 11; ip3++) // 1 more bit flip in parity + { + syndromeIP3 = syndromeIP2 ^ (1 << (10-ip3)); + Golay_23_12_m_corr[syndromeIP3][0] = 12 + ip1; + Golay_23_12_m_corr[syndromeIP3][1] = 12 + ip2; + Golay_23_12_m_corr[syndromeIP3][2] = 12 + ip3; + } + } + } +} + +// Not very efficient but encode is used for unit testing only +void Golay_23_12_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 23); + + for (i = 0; i < 12; i++) // orig bits + { + for (j = 0; j < 23; j++) // codeword bits + { + encodedBits[j] += origBits[i] * Golay_23_12_m_G[23*i + j]; + } + } + + for (i = 0; i < 23; i++) + { + encodedBits[i] %= 2; + } +} + +bool Golay_23_12_decode(unsigned char *rxBits) +{ + unsigned int syndromeI = 0; // syndrome index + int is = 0; + int i = 0; + + for (is = 0; is < 11; is++) + { + syndromeI += (((rxBits[0] * Golay_23_12_m_H[23*is + 0]) + + (rxBits[1] * Golay_23_12_m_H[23*is + 1]) + + (rxBits[2] * Golay_23_12_m_H[23*is + 2]) + + (rxBits[3] * Golay_23_12_m_H[23*is + 3]) + + (rxBits[4] * Golay_23_12_m_H[23*is + 4]) + + (rxBits[5] * Golay_23_12_m_H[23*is + 5]) + + (rxBits[6] * Golay_23_12_m_H[23*is + 6]) + + (rxBits[7] * Golay_23_12_m_H[23*is + 7]) + + (rxBits[8] * Golay_23_12_m_H[23*is + 8]) + + (rxBits[9] * Golay_23_12_m_H[23*is + 9]) + + (rxBits[10] * Golay_23_12_m_H[23*is + 10]) + + (rxBits[11] * Golay_23_12_m_H[23*is + 11]) + + (rxBits[12] * Golay_23_12_m_H[23*is + 12]) + + (rxBits[13] * Golay_23_12_m_H[23*is + 13]) + + (rxBits[14] * Golay_23_12_m_H[23*is + 14]) + + (rxBits[15] * Golay_23_12_m_H[23*is + 15]) + + (rxBits[16] * Golay_23_12_m_H[23*is + 16]) + + (rxBits[17] * Golay_23_12_m_H[23*is + 17]) + + (rxBits[18] * Golay_23_12_m_H[23*is + 18]) + + (rxBits[19] * Golay_23_12_m_H[23*is + 19]) + + (rxBits[20] * Golay_23_12_m_H[23*is + 20]) + + (rxBits[21] * Golay_23_12_m_H[23*is + 21]) + + (rxBits[22] * Golay_23_12_m_H[23*is + 22])) % 2) << (10-is); + } + + if (syndromeI > 0) + { + i = 0; + + for (; i < 3; i++) + { + if (Golay_23_12_m_corr[syndromeI][i] == 0xFF) + { + break; + } + else + { + rxBits[Golay_23_12_m_corr[syndromeI][i]] ^= 1; // flip bit + } + } + + if (i == 0) + { + return false; + } + } + + return true; +} + + +// ======================================================================================== + + +void Golay_24_12_init() +{ + int i1 = 0, i2 = 0, i3 = 0, ir = 0, ip = 0; + int syndromeI = 0, syndromeIP = 0; + int ip1 = 0, ip2 = 0, ip3 = 0; + int syndromeIP1 = 0, syndromeIP2 = 0, syndromeIP3 = 0; + + memset (Golay_24_12_m_corr, 0xFF, 3*4096); + + for (i1 = 0; i1 < 12; i1++) + { + for (i2 = i1+1; i2 < 12; i2++) + { + for (i3 = i2+1; i3 < 12; i3++) + { + // 3 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += ((Golay_24_12_m_H[24*ir + i1] + Golay_24_12_m_H[24*ir + i2] + Golay_24_12_m_H[24*ir + i3]) % 2) << (11-ir); + } + + Golay_24_12_m_corr[syndromeI][0] = i1; + Golay_24_12_m_corr[syndromeI][1] = i2; + Golay_24_12_m_corr[syndromeI][2] = i3; + } + + // 2 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += ((Golay_24_12_m_H[24*ir + i1] + Golay_24_12_m_H[24*ir + i2]) % 2) << (11-ir); + } + + Golay_24_12_m_corr[syndromeI][0] = i1; + Golay_24_12_m_corr[syndromeI][1] = i2; + + // 1 possible bit flip left in the parity part + for (ip = 0; ip < 12; ip++) + { + syndromeIP = syndromeI ^ (1 << (11-ip)); + Golay_24_12_m_corr[syndromeIP][0] = i1; + Golay_24_12_m_corr[syndromeIP][1] = i2; + Golay_24_12_m_corr[syndromeIP][2] = 12 + ip; + } + } + + // single bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += Golay_24_12_m_H[24*ir + i1] << (11-ir); + } + + Golay_24_12_m_corr[syndromeI][0] = i1; + + for (ip1 = 0; ip1 < 12; ip1++) // 1 more bit flip in parity + { + syndromeIP1 = syndromeI ^ (1 << (11-ip1)); + Golay_24_12_m_corr[syndromeIP1][0] = i1; + Golay_24_12_m_corr[syndromeIP1][1] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2)); + Golay_24_12_m_corr[syndromeIP2][0] = i1; + Golay_24_12_m_corr[syndromeIP2][1] = 12 + ip1; + Golay_24_12_m_corr[syndromeIP2][2] = 12 + ip2; + } + } + } + + // no bit patterns (in message) -> all in parity + for (ip1 = 0; ip1 < 12; ip1++) // 1 bit flip in parity + { + syndromeIP1 = (1 << (11-ip1)); + Golay_24_12_m_corr[syndromeIP1][0] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2)); + Golay_24_12_m_corr[syndromeIP2][0] = 12 + ip1; + Golay_24_12_m_corr[syndromeIP2][1] = 12 + ip2; + + for (ip3 = ip2+1; ip3 < 12; ip3++) // 1 more bit flip in parity + { + syndromeIP3 = syndromeIP2 ^ (1 << (11-ip3)); + Golay_24_12_m_corr[syndromeIP3][0] = 12 + ip1; + Golay_24_12_m_corr[syndromeIP3][1] = 12 + ip2; + Golay_24_12_m_corr[syndromeIP3][2] = 12 + ip3; + } + } + } +} + +// Not very efficient but encode is used for unit testing only +void Golay_24_12_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 24); + + for (i = 0; i < 12; i++) + { + for (j = 0; j < 24; j++) + { + encodedBits[j] += origBits[i] * Golay_24_12_m_G[24*i + j]; + } + } + + for (i = 0; i < 24; i++) + { + encodedBits[i] %= 2; + } +} + +bool Golay_24_12_decode(unsigned char *rxBits) +{ + unsigned int syndromeI = 0; // syndrome index + int is = 0; + int i = 0; + + for (is = 0; is < 12; is++) + { + syndromeI += (((rxBits[0] * Golay_24_12_m_H[24*is + 0]) + + (rxBits[1] * Golay_24_12_m_H[24*is + 1]) + + (rxBits[2] * Golay_24_12_m_H[24*is + 2]) + + (rxBits[3] * Golay_24_12_m_H[24*is + 3]) + + (rxBits[4] * Golay_24_12_m_H[24*is + 4]) + + (rxBits[5] * Golay_24_12_m_H[24*is + 5]) + + (rxBits[6] * Golay_24_12_m_H[24*is + 6]) + + (rxBits[7] * Golay_24_12_m_H[24*is + 7]) + + (rxBits[8] * Golay_24_12_m_H[24*is + 8]) + + (rxBits[9] * Golay_24_12_m_H[24*is + 9]) + + (rxBits[10] * Golay_24_12_m_H[24*is + 10]) + + (rxBits[11] * Golay_24_12_m_H[24*is + 11]) + + (rxBits[12] * Golay_24_12_m_H[24*is + 12]) + + (rxBits[13] * Golay_24_12_m_H[24*is + 13]) + + (rxBits[14] * Golay_24_12_m_H[24*is + 14]) + + (rxBits[15] * Golay_24_12_m_H[24*is + 15]) + + (rxBits[16] * Golay_24_12_m_H[24*is + 16]) + + (rxBits[17] * Golay_24_12_m_H[24*is + 17]) + + (rxBits[18] * Golay_24_12_m_H[24*is + 18]) + + (rxBits[19] * Golay_24_12_m_H[24*is + 19]) + + (rxBits[20] * Golay_24_12_m_H[24*is + 20]) + + (rxBits[21] * Golay_24_12_m_H[24*is + 21]) + + (rxBits[22] * Golay_24_12_m_H[24*is + 22]) + + (rxBits[23] * Golay_24_12_m_H[24*is + 23])) % 2) << (11-is); + } + + if (syndromeI > 0) + { + i = 0; + + for (; i < 3; i++) + { + if (Golay_24_12_m_corr[syndromeI][i] == 0xFF) + { + break; + } + else + { + rxBits[Golay_24_12_m_corr[syndromeI][i]] ^= 1; // flip bit + } + } + + if (i == 0) + { + return false; + } + } + + return true; +} + + +// ======================================================================================== + + +void QR_16_7_6_init() +{ + int i1 = 0, i2 = 0, ir = 0, ip = 0; + int syndromeI = 0, syndromeIP = 0; + int ip1 = 0, ip2 = 0; + int syndromeIP1 = 0, syndromeIP2 = 0; + + memset(QR_16_7_6_m_corr, 0xFF, 2*512); + + for (i1 = 0; i1 < 7; i1++) + { + for (i2 = i1+1; i2 < 7; i2++) + { + // 2 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 9; ir++) + { + syndromeI += ((QR_16_7_6_m_H[16*ir + i1] + QR_16_7_6_m_H[16*ir + i2]) % 2) << (8-ir); + } + + QR_16_7_6_m_corr[syndromeI][0] = i1; + QR_16_7_6_m_corr[syndromeI][1] = i2; + } + + // single bit patterns + syndromeI = 0; + + for (ir = 0; ir < 9; ir++) + { + syndromeI += QR_16_7_6_m_H[16*ir + i1] << (8-ir); + } + + QR_16_7_6_m_corr[syndromeI][0] = i1; + + // 1 possible bit flip left in the parity part + for (ip = 0; ip < 9; ip++) + { + syndromeIP = syndromeI ^ (1 << (8-ip)); + QR_16_7_6_m_corr[syndromeIP][0] = i1; + QR_16_7_6_m_corr[syndromeIP][1] = 7 + ip; + } + } + + // no bit patterns (in message) -> all in parity + for (ip1 = 0; ip1 < 9; ip1++) // 1 bit flip in parity + { + syndromeIP1 = (1 << (8-ip1)); + QR_16_7_6_m_corr[syndromeIP1][0] = 7 + ip1; + + for (ip2 = ip1+1; ip2 < 9; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (8-ip2)); + QR_16_7_6_m_corr[syndromeIP2][0] = 7 + ip1; + QR_16_7_6_m_corr[syndromeIP2][1] = 7 + ip2; + } + } +} + +// Not very efficient but encode is used for unit testing only +void QR_16_7_6_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 16); + + for (i = 0; i < 7; i++) + { + for (j = 0; j < 16; j++) + { + encodedBits[j] += origBits[i] * QR_16_7_6_m_G[16*i + j]; + } + } + + for (i = 0; i < 16; i++) + { + encodedBits[i] %= 2; + } +} + +bool QR_16_7_6_decode(unsigned char *rxBits) +{ + unsigned int syndromeI = 0; // syndrome index + int is = 0; + int i = 0; + + for (is = 0; is < 9; is++) + { + syndromeI += (((rxBits[0] * QR_16_7_6_m_H[16*is + 0]) + + (rxBits[1] * QR_16_7_6_m_H[16*is + 1]) + + (rxBits[2] * QR_16_7_6_m_H[16*is + 2]) + + (rxBits[3] * QR_16_7_6_m_H[16*is + 3]) + + (rxBits[4] * QR_16_7_6_m_H[16*is + 4]) + + (rxBits[5] * QR_16_7_6_m_H[16*is + 5]) + + (rxBits[6] * QR_16_7_6_m_H[16*is + 6]) + + (rxBits[7] * QR_16_7_6_m_H[16*is + 7]) + + (rxBits[8] * QR_16_7_6_m_H[16*is + 8]) + + (rxBits[9] * QR_16_7_6_m_H[16*is + 9]) + + (rxBits[10] * QR_16_7_6_m_H[16*is + 10]) + + (rxBits[11] * QR_16_7_6_m_H[16*is + 11]) + + (rxBits[12] * QR_16_7_6_m_H[16*is + 12]) + + (rxBits[13] * QR_16_7_6_m_H[16*is + 13]) + + (rxBits[14] * QR_16_7_6_m_H[16*is + 14]) + + (rxBits[15] * QR_16_7_6_m_H[16*is + 15])) % 2) << (8-is); + } + + if (syndromeI > 0) + { + i = 0; + + for (; i < 2; i++) + { + if (QR_16_7_6_m_corr[syndromeI][i] == 0xFF) + { + break; + } + else + { + rxBits[QR_16_7_6_m_corr[syndromeI][i]] ^= 1; // flip bit + } + } + + if (i == 0) + { + return false; + } + } + + return true; +} + + +// ======================================================================================== + + +/* This function init all FEC functions + * it must be called once at startup */ +void InitAllFecFunction(void) +{ + Hamming_7_4_init(); + Hamming_12_8_init(); + Hamming_13_9_init(); + Hamming_15_11_init(); + Hamming_16_11_4_init(); + Golay_20_8_init(); + Golay_23_12_init(); + Golay_24_12_init(); + QR_16_7_6_init(); +} /* End InitAllFEC() */ + + +/* End of file */ diff --git a/src/bptc.c b/src/bptc.c new file mode 100644 index 0000000..7f6ddff --- /dev/null +++ b/src/bptc.c @@ -0,0 +1,560 @@ +/* + ============================================================================ + Name : bptc.c + Author : MotoTRBO_Encrypted_DSD_Author + Version : 1.0 + Date : 2017 - December - 11 + Copyright : None + Description : BPTC library source + ============================================================================ + */ + + +/* Include ------------------------------------------------------------------*/ +#include "dsd.h" + + +/* Define -------------------------------------------------------------------*/ + + +/* Global variables ---------------------------------------------------------*/ +const uint8_t BPTCInterleavingIndex[196] = +{ + 0, 181, 166, 151, + 136, 121, 106, 91, + 76, 61, 46, 31, + 16, 1, 182, 167, + 152, 137, 122, 107, + 92, 77, 62, 47, + 32, 17, 2, 183, + 168, 153, 138, 123, + 108, 93, 78, 63, + 48, 33, 18, 3, + 184, 169, 154, 139, + 124, 109, 94, 79, + 64, 49, 34, 19, + 4, 185, 170, 155, + 140, 125, 110, 95, + 80, 65, 50, 35, + 20, 5, 186, 171, + 156, 141, 126, 111, + 96, 81, 66, 51, + 36, 21, 6, 187, + 172, 157, 142, 127, + 112, 97, 82, 67, + 52, 37, 22, 7, + 188, 173, 158, 143, + 128, 113, 98, 83, + 68, 53, 38, 23, + 8, 189, 174, 159, + 144, 129, 114, 99, + 84, 69, 54, 39, + 24, 9, 190, 175, + 160, 145, 130, 115, + 100, 85, 70, 55, + 40, 25, 10, 191, + 176, 161, 146, 131, + 116, 101, 86, 71, + 56, 41, 26, 11, + 192, 177, 162, 147, + 132, 117, 102, 87, + 72, 57, 42, 27, + 12, 193, 178, 163, + 148, 133, 118, 103, + 88, 73, 58, 43, + 28, 13, 194, 179, + 164, 149, 134, 119, + 104, 89, 74, 59, + 44, 29, 14, 195, + 180, 165, 150, 135, + 120, 105, 90, 75, + 60, 45, 30, 15 +}; + + +const uint8_t BPTCDeInterleavingIndex[196] = +{ + 0, 13, 26, 39, + 52, 65, 78, 91, + 104, 117, 130, 143, + 156, 169, 182, 195, + 12, 25, 38, 51, + 64, 77, 90, 103, + 116, 129, 142, 155, + 168, 181, 194, 11, + 24, 37, 50, 63, + 76, 89, 102, 115, + 128, 141, 154, 167, + 180, 193, 10, 23, + 36, 49, 62, 75, + 88, 101, 114, 127, + 140, 153, 166, 179, + 192, 9, 22, 35, + 48, 61, 74, 87, + 100, 113, 126, 139, + 152, 165, 178, 191, + 8, 21, 34, 47, + 60, 73, 86, 99, + 112, 125, 138, 151, + 164, 177, 190, 7, + 20, 33, 46, 59, + 72, 85, 98, 111, + 124, 137, 150, 163, + 176, 189, 6, 19, + 32, 45, 58, 71, + 84, 97, 110, 123, + 136, 149, 162, 175, + 188, 5, 18, 31, + 44, 57, 70, 83, + 96, 109, 122, 135, + 148, 161, 174, 187, + 4, 17, 30, 43, + 56, 69, 82, 95, + 108, 121, 134, 147, + 160, 173, 186, 3, + 16, 29, 42, 55, + 68, 81, 94, 107, + 120, 133, 146, 159, + 172, 185, 2, 15, + 28, 41, 54, 67, + 80, 93, 106, 119, + 132, 145, 158, 171, + 184, 1, 14, 27, + 40, 53, 66, 79, + 92, 105, 118, 131, + 144, 157, 170, 183 +}; + + +const uint8_t DeInterleaveReverseChannelBptc[32] = +{ + 0, 17, 2, 19, 4, 21, 6, 23, + 8, 25, 10, 27, 12, 29, 14, 31, + 16, 1, 18, 3, 20, 5, 22, 7, + 24, 9, 26, 11, 28, 13, 30, 15 +}; + + +/* Functions ----------------------------------------------------------------*/ + + +/* + * @brief : This function deinterleave the DMR data by using BPTC (196,96) + * + * @param Input : Pointer of DMR input data interleaved (196 bytes) + * + * @param Output : Pointer where DMR deinterleaved data will be written (196 bytes) + * + * @return None + */ +void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output) +{ + uint32_t i, DeInterleaveIndex; + + for(i = 0; i < 196; i++) + { + DeInterleaveIndex = BPTCDeInterleavingIndex[i]; + Output[DeInterleaveIndex] = (Input[i] & 1); + } +} /* End BPTCDeInterleaveDMRData() */ + + +/* + * @brief : This function extract the 96 bits of a deinteleaved 196 bits + * buffer using BPTC (196,96) + * + * @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (196 bytes) + * + * @param DMRDataExtracted : Pointer where the DMR data will be written (96 bytes) + * + * @return The total number of irrecoverable Hamming check errors + */ +uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]) +{ + uint32_t i, j, k; + uint8_t DataMatrix[13][15]; + uint8_t LineUncorrected[15]; + uint8_t LineCorrected[11]; + uint8_t ColumnUncorrected[13]; + uint8_t ColumnCorrected[9]; + uint32_t HammingIrrecoverableErrorNb = 0; + + /* First step : Reconstitute the BPTC 15x11 matrix + * Note : Input data shall be deinterleaved */ + k = 1; /* Discard R(3) bit - See DMR standard chapter B1.1 BPTC (196,96) */ + for(i = 0; i < 13; i++) + { + for(j = 0; j < 15; j++) + { + /* Only the LSBit of the byte is stored */ + DataMatrix[i][j] = (InputDeInteleavedData[k] & 1); + k++; + } + } + + /* R(0) to R(2) may be used to transport some + * Restricted Access System (RAS) information, + * So save these three bytes before reset all of them + * See patent US 2013/0288643 A1 */ + R[0] = DataMatrix[0][2]; /* Save R(0) */ + R[1] = DataMatrix[0][1]; /* Save R(1) */ + R[2] = DataMatrix[0][0]; /* Save R(2) */ + + /* Set to 0 R(0) to R(2) - See DMR standard chapter B1.1 BPTC (196,96) */ + DataMatrix[0][0] = 0; /* R(2) */ + DataMatrix[0][1] = 0; /* R(1) */ + DataMatrix[0][2] = 0; /* R(0) */ + + /* Init the Hamming (15,11,3) library + * Not needed because it has already been done + * in the "InitAllFecFunction()" function */ + //Hamming_15_11_init(); + + /* Init the Hamming (13,9,3) library + * Not needed because it has already been done + * in the "InitAllFecFunction()" function */ + //Hamming_13_9_init(); + + /* Init the Hamming error counter */ + HammingIrrecoverableErrorNb = 0; + + /* Process the the Hamming (15,11,3) code + * check on each line (first time). + * Do not check the 4th last lines */ + for(i = 0; i < 9; i++) + { + /* Get a full line */ + for(j = 0; j < 15; j++) + { + LineUncorrected[j] = DataMatrix[i][j]; + } + + /* Apply Hamming (15,11,3) code correction */ + if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false) + { + HammingIrrecoverableErrorNb++; + } + + /* Re-inject the line in the matrix (only the util data [11 bit], + * not the Hamming part [4 bit]) */ + for(j = 0; j < 11; j++) + { + DataMatrix[i][j] = LineCorrected[j]; + } + } + + + /* Process the the Hamming (15,11,3) code + * check on each column/row (first time) */ + for(i = 0; i < 15; i++) + { + /* Get a full column */ + for(j = 0; j < 13; j++) + { + ColumnUncorrected[j] = DataMatrix[j][i]; + } + + /* Apply Hamming (15,11,3) code correction */ + if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false) + { + HammingIrrecoverableErrorNb++; + } + + /* Re-inject the column in the matrix (only the util data [11 bit], + * not the Hamming part [4 bit]) */ + for(j = 0; j < 9; j++) + { + DataMatrix[j][i] = ColumnCorrected[j]; + } + } + + /* The first Hamming code check has maybe corrected bit + * witch can be use to correct other bit, so make the + * same operation twice */ + + /* Reset the number of irrecoverable errors */ + HammingIrrecoverableErrorNb = 0; + + + /* Process the the Hamming (15,11,3) code + * check on each line (second time). + * Do not check the 4th last lines */ + for(i = 0; i < 9; i++) + { + /* Get a full line */ + for(j = 0; j < 15; j++) + { + LineUncorrected[j] = DataMatrix[i][j]; + } + + /* Apply Hamming (15,11,3) code correction */ + if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false) + { + /* Row/Line contains irrecoverable errors */ + HammingIrrecoverableErrorNb++; + } + else + { + /* Row/Line contains correct data */ + } + + /* Re-inject the line in the matrix (only the util data [11 bit], + * not the Hamming part [4 bit]) */ + for(j = 0; j < 11; j++) + { + DataMatrix[i][j] = LineCorrected[j]; + } + } + + + /* Process the the Hamming (15,11,3) code + * check on each column/row (second time) */ + for(i = 0; i < 15; i++) + { + /* Get a full column */ + for(j = 0; j < 13; j++) + { + ColumnUncorrected[j] = DataMatrix[j][i]; + } + + /* Apply Hamming (15,11,3) code correction */ + if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false) + { + /* Row/Line contains irrecoverable errors */ + HammingIrrecoverableErrorNb++; + } + else + { + /* Row/Line contains correct data */ + } + + /* Re-inject the column in the matrix (only the util data [11 bit], + * not the Hamming part [4 bit]) */ + for(j = 0; j < 9; j++) + { + DataMatrix[j][i] = ColumnCorrected[j]; + } + } + + /* Extract the DMR data (96 bit) from the matrix */ + /* First line : Do not take R(2), R(1) and R(0) */ + k = 0; + + for(i = 3; i < 11; i++) + { + DMRDataExtracted[k] = DataMatrix[0][i]; + k++; + } + + /* Next lines */ + for(i = 1; i < 9; i++) + { + for(j = 0; j < 11; j++) + { + DMRDataExtracted[k] = DataMatrix[i][j]; + k++; + } + } + + /* Return the number of irrecoverable Hamming errors */ + return HammingIrrecoverableErrorNb; +} /* End BPTC_196x96_Extract_Data() */ + + +/* + * @brief : This function extract the 77 bits of a deinteleaved 128 bits + * buffer using BPTC (128,77). + * + * @note : See DMR standard ETSI TS 102 361-1 chapter B.2.1 for details + * + * @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (128 bytes + act as a 16 x 8 byte data matrix) + * + * @param DMRDataExtracted : Pointer where the DMR data will be written (77 bytes) + * DMRDataExtracted[0..71] = Util data (hamming corrected) + * DMRDataExtracted[72..76] = 5 bit of CRC extracted (to be checked) + * + * @return 0 = All is OK + * Other = Error - Irrecoverable Hamming parity check error number + */ +uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]) +{ + uint32_t i, j, k; + uint8_t DataMatrix[8][16]; + uint8_t LineUncorrected[16]; + uint8_t LineCorrected[11]; + uint32_t HammingIrrecoverableErrorNb = 0; + uint32_t ParityCheckErrorNb = 0; + uint32_t NbOfOne; + + /* First step : Reconstitute the BPTC 16x8 matrix */ + for(i = 0; i < 8; i++) + { + for(j = 0; j < 16; j++) + { + /* Only the LSBit of the byte is stored */ + DataMatrix[i][j] = (InputDataMatrix[i][j] & 1); + } + } + + /* Init the Hamming (16,11,4) library + * Not needed because it has already been done + * in the "InitAllFecFunction()" function */ + //Hamming_16_11_4_init(); + + /* Process the the Hamming (16,11,4) code + * check on each line. + * Do not check the last line (line nb 8) */ + for(i = 0; i < 7; i++) + { + /* Get a full line */ + for(j = 0; j < 16; j++) + { + LineUncorrected[j] = DataMatrix[i][j]; + } + + /* Apply Hamming (16,11,4) code correction */ + if(Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false) + { + HammingIrrecoverableErrorNb++; + } + + /* Re-inject the line in the matrix (only the util data [11 bit], + * not the Hamming part [4 bit]) */ + for(j = 0; j < 11; j++) + { + DataMatrix[i][j] = LineCorrected[j]; + } + } + + /* Extract the DMR data (77 bit) from the matrix */ + k = 0; + + /* 2 first lines */ + for(i = 0; i < 2; i++) + { + for(j = 0; j < 11; j++) + { + DMRDataExtracted[k] = DataMatrix[i][j]; + k++; + } + } + + /* 5 Next lines */ + for(i = 2; i < 7; i++) + { + for(j = 0; j < 10; j++) + { + DMRDataExtracted[k] = DataMatrix[i][j]; + k++; + } + } + + /* 5 bit of CRC */ + for(i = 2; i < 7; i++) + { + DMRDataExtracted[k] = DataMatrix[i][10]; + k++; + } + + /* Verify the data integrity by checking + * all column parity bit */ + ParityCheckErrorNb = 0; + for(i = 0; i < 16; i++) + { + NbOfOne = 0; + + /* Get a full column */ + for(j = 0; j < 7; j++) + { + NbOfOne += DataMatrix[j][i]; + } + + /* Check the parity bit (number of "1" + * must be even) */ + if((NbOfOne % 2) != DataMatrix[7][i]) + { + ParityCheckErrorNb++; + } + } + + /* Return the number of irrecoverable Hamming errors + + * the number of parity check error */ + return (HammingIrrecoverableErrorNb + ParityCheckErrorNb); +} /* End BPTC_128x77_Extract_Data() */ + + +/* + * @brief : This function extract the 32 bits of a deinteleaved 32 bits + * buffer using Reverse Channel Single Burst BPTC. + * + * @note : See DMR standard ETSI TS 102 361-1 chapter B.2.2.2 for details + * + * @param InputInterleavedData : Pointer of DMR input data interleaved (32 bytes + act as a 16 x 2 bits data matrix) + * + * @param DMRDataExtracted : Pointer where the DMR data will be written (77 bytes) + * DMRDataExtracted[0..10] = Util data (Hamming corrected) + * DMRDataExtracted[11..15] = Hamming data + * DMRDataExtracted[16..31] = 16 bits of odd parity (to be checked) + * + * @return 0 = All is OK + * Other = Error - Irrecoverable Hamming parity check error number + */ +uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32]) +{ + uint32_t i; + uint8_t DataMatrix[32]; + uint8_t LineUncorrected[16]; + uint8_t LineCorrected[11]; + uint32_t HammingIrrecoverableErrorNb = 0; + uint32_t ParityCheckErrorNb = 0; + + for(i = 0; i < 32; i++) + { + DataMatrix[DeInterleaveReverseChannelBptc[i]] = InputInterleavedData[i] & 1; + } + + /* Copy the entire input buffer to the output buffer */ + for(i = 0; i < 32; i++) + { + DMRDataExtracted[i] = DataMatrix[i]; + } + + /* Prepare the line to be corrected */ + for(i = 0; i < 16; i++) + { + LineUncorrected[i] = DataMatrix[i]; + } + + /* Apply Hamming (16,11,4) code correction */ + if(Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false) + { + HammingIrrecoverableErrorNb++; + } + + /* Copy the corrected content to the output buffer */ + for(i = 0; i < 11; i++) + { + DMRDataExtracted[i] = LineCorrected[i]; + } + + /* Check Parity bits */ + for(i = 0; i < 16; i++) + { + /* Odd parity ==> If data = 1 then parity = 0 + * If data = 0 then parity = 1 */ + if(DMRDataExtracted[i] == DMRDataExtracted[i + 16]) ParityCheckErrorNb++; + } + + //fprintf(stderr, "Hamming ERR=%u ; Parity ERR=%u", HammingIrrecoverableErrorNb, ParityCheckErrorNb); + + /* Return the number of irrecoverable Hamming errors + + * the number of parity check error */ + return (HammingIrrecoverableErrorNb + ParityCheckErrorNb); + +} /* End BPTC_16x2_Extract_Data() */ + + + +/* End of file */ diff --git a/src/dmr_data.c b/src/dmr_data.c index 8d61c0f..1eddd5f 100644 --- a/src/dmr_data.c +++ b/src/dmr_data.c @@ -24,240 +24,407 @@ processDMRdata (dsd_opts * opts, dsd_state * state) int i, dibit; int *dibit_p; char sync[25]; - char syncdata[25]; - char cachdata[13]; - char cc[5]; + char syncdata[48]; + char cachdata[13] = {0}; + char cc[5] = {0}; + char ccAscii[5] = {0}; char bursttype[5]; - int dcc_buf; + unsigned int burst; + char info[196]; + char SlotType[20]; + unsigned int SlotTypeOk; + + /* Remove warning compiler */ + //UNUSED_VARIABLE(cachdata[0]); + //UNUSED_VARIABLE(cc[0]); + //UNUSED_VARIABLE(ccAscii[0]); #ifdef DMR_DUMP - int k; char syncbits[49]; char cachbits[25]; + int k; #endif - cc[0]=0; - cc[1]=0; - cc[2]=0; - cc[3]=0; //initialize with zeroes to prevent segfault down below - state->dmr_color_code = 0; //reset - cc[4] = 0; + + ccAscii[4] = 0; bursttype[4] = 0; dibit_p = state->dibit_buf_p - 90; // CACH for (i = 0; i < 12; i++) + { + dibit = *dibit_p; + dibit_p++; + if (opts->inverted_dmr == 1) { - dibit = *dibit_p; - dibit_p++; - if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - cachdata[i] = dibit; - if (i == 2) - { - state->currentslot = (1 & (dibit >> 1)); // bit 1 - if (state->currentslot == 0) - { - state->slot0light[0] = '['; - state->slot0light[6] = ']'; - state->slot1light[0] = ' '; - state->slot1light[6] = ' '; - } - else - { - state->slot1light[0] = '['; - state->slot1light[6] = ']'; - state->slot0light[0] = ' '; - state->slot0light[6] = ' '; - } - } + dibit = (dibit ^ 2); } + cachdata[i] = dibit; + if (i == 2) + { + /* Change the current slot only if we are in relayed mode (not in direct mode) */ + if(state->directmode == 0) state->currentslot = (1 & (dibit >> 1)); // bit 1 + + if (state->currentslot == 0) + { + state->slot1light[0] = '['; + state->slot1light[6] = ']'; + state->slot2light[0] = ' '; + state->slot2light[6] = ' '; + } + else + { + state->slot2light[0] = '['; + state->slot2light[6] = ']'; + state->slot1light[0] = ' '; + state->slot1light[6] = ' '; + } + } + } cachdata[12] = 0; #ifdef DMR_DUMP k = 0; for (i = 0; i < 12; i++) - { - dibit = cachdata[i]; - cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 - k++; - cachbits[k] = (1 & dibit) + 48; // bit 0 - k++; - } + { + dibit = cachdata[i]; + cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 + k++; + cachbits[k] = (1 & dibit) + 48; // bit 0 + k++; + } cachbits[24] = 0; - fprintf (stderr, "%s ", cachbits); + fprintf(stderr, "%s ", cachbits); #endif - // current slot - dibit_p += 49; + // Current slot - First half - Data Payload - 1st part + for (i = 0; i < 49; i++) + { + dibit = *dibit_p; + dibit_p++; + if (opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + info[2*i] = (1 & (dibit >> 1)); // bit 1 + info[(2*i)+1] = (1 & dibit); // bit 0 + } // slot type dibit = *dibit_p; dibit_p++; if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - //c[0] = (1 & (dibit >> 1)) + 48; // bit 1 - //cc[1] = (1 & dibit) + 48; // bit 0 - cc[0] = (1 & (dibit >> 1)); // bit 1 - cc[1] = (1 & dibit); // bit 0 - dcc_buf = 0; - //dcc_buf = (1 & (dibit >> 1)) | (1 & dibit); - dcc_buf = ((1 & (dibit >> 1)) + 48) | ((1 & dibit) + 48); - + { + dibit = (dibit ^ 2); + } + cc[0] = (1 & (dibit >> 1)); // bit 1 + cc[1] = (1 & dibit); // bit 0 + SlotType[0] = (1 & (dibit >> 1)); // bit 1 + SlotType[1] = (1 & dibit); // bit 0 dibit = *dibit_p; dibit_p++; if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - //cc[2] = (1 & (dibit >> 1)) + 48; // bit 1 - //cc[3] = (1 & dibit) + 48; // bit 0 - cc[2] = (1 & (dibit >> 1)); // bit 1 - cc[3] = (1 & dibit); // bit 0 - - dcc_buf = dcc_buf << 2; - //dcc_buf = (1 & (dibit >> 1)) | (1 & dibit); - dcc_buf = ((1 & (dibit >> 1)) + 48) | ((1 & dibit) + 48); - - //state->dmr_color_code = dcc_buf; - state->dmr_color_code = (unsigned int)((cc[0] << 3) + (cc[1] << 2) + (cc[2] << 1) + cc[3]); - //cc[2] = (1 & (dibit >> 1)); // bit 1 - //cc[3] = (1 & dibit); // bit 0 + { + dibit = (dibit ^ 2); + } + cc[2] = (1 & (dibit >> 1)); // bit 1 + cc[3] = (1 & dibit); // bit 0 + SlotType[2] = (1 & (dibit >> 1)); // bit 1 + SlotType[3] = (1 & dibit); // bit 0 + ccAscii[0] = cc[0] + '0'; + ccAscii[1] = cc[1] + '0'; + ccAscii[2] = cc[2] + '0'; + ccAscii[3] = cc[3] + '0'; + ccAscii[4] = cc[4] + '\0'; dibit = *dibit_p; dibit_p++; if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - bursttype[0] = (1 & (dibit >> 1)) + 48; // bit 1 - bursttype[1] = (1 & dibit) + 48; // bit 0 + { + dibit = (dibit ^ 2); + } +// burst = (unsigned int)((1 & (dibit >> 1)) << 3); +// burst |= (unsigned int)((1 & (dibit >> 0)) << 2); +// bursttype[0] = (1 & (dibit >> 1)) + 48; // bit 1 +// bursttype[1] = (1 & dibit) + 48; // bit 0 + SlotType[4] = (1 & (dibit >> 1)); // bit 1 + SlotType[5] = (1 & dibit); // bit 0 dibit = *dibit_p; dibit_p++; if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - bursttype[2] = (1 & (dibit >> 1)) + 48; // bit 1 - bursttype[3] = (1 & dibit) + 48; // bit 0 + { + dibit = (dibit ^ 2); + } +// burst |= (unsigned int)((1 & (dibit >> 1)) << 1); +// burst |= (unsigned int)((1 & (dibit >> 0)) << 0); +// bursttype[2] = (1 & (dibit >> 1)) + 48; // bit 1 +// bursttype[3] = (1 & dibit) + 48; // bit 0 + SlotType[6] = (1 & (dibit >> 1)); // bit 1 + SlotType[7] = (1 & dibit); // bit 0 - // parity bit + // Parity bit + dibit = *dibit_p; dibit_p++; - - if (strcmp (bursttype, "0000") == 0) - { - sprintf (state->fsubtype, " PI Header "); - } - else if (strcmp (bursttype, "0001") == 0) - { - sprintf (state->fsubtype, " VOICE Header "); - } - else if (strcmp (bursttype, "0010") == 0) - { - sprintf (state->fsubtype, " TLC "); - } - else if (strcmp (bursttype, "0011") == 0) - { - sprintf (state->fsubtype, " CSBK "); - } - else if (strcmp (bursttype, "0100") == 0) - { - sprintf (state->fsubtype, " MBC Header "); - } - else if (strcmp (bursttype, "0101") == 0) - { - sprintf (state->fsubtype, " MBC "); - } - else if (strcmp (bursttype, "0110") == 0) - { - sprintf (state->fsubtype, " DATA Header "); - } - else if (strcmp (bursttype, "0111") == 0) - { - sprintf (state->fsubtype, " RATE 1/2 DATA"); - } - else if (strcmp (bursttype, "1000") == 0) - { - sprintf (state->fsubtype, " RATE 3/4 DATA"); - } - else if (strcmp (bursttype, "1001") == 0) - { - sprintf (state->fsubtype, " Slot idle "); - } - else if (strcmp (bursttype, "1010") == 0) - { - sprintf (state->fsubtype, " Rate 1 DATA "); - } - else - { - sprintf (state->fsubtype, " "); - } + if (opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + SlotType[8] = (1 & (dibit >> 1)); // bit 1 + SlotType[9] = (1 & dibit); // bit 0 // signaling data or sync for (i = 0; i < 24; i++) + { + dibit = *dibit_p; + dibit_p++; + if (opts->inverted_dmr == 1) { - dibit = *dibit_p; - dibit_p++; - if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - syncdata[i] = dibit; - sync[i] = (dibit | 1) + 48; + dibit = (dibit ^ 2); } + syncdata[2*i] = (1 & (dibit >> 1)); // bit 1 + syncdata[(2*i)+1] = (1 & dibit); // bit 0 + sync[i] = (dibit | 1) + 48; + } sync[24] = 0; - syncdata[24] = 0; #ifdef DMR_DUMP k = 0; for (i = 0; i < 24; i++) - { - dibit = syncdata[i]; - syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 - k++; - syncbits[k] = (1 & dibit) + 48; // bit 0 - k++; - } + { + syncbits[k] = syncdata[(k*2)] + 48; // bit 1 + k++; + syncbits[k] = syncdata[(k*2)+1] + 48; // bit 0 + k++; + } syncbits[48] = 0; - fprintf (stderr, "%s ", syncbits); + fprintf(stderr, "%s ", syncbits); #endif - if ((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0)) + if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0)) + { + if (state->currentslot == 0) { - if (state->currentslot == 0) - { - sprintf (state->slot0light, "[slot0]"); - } - else - { - sprintf (state->slot1light, "[slot1]"); - } + sprintf(state->slot1light, "[slot1]"); } + else + { + sprintf(state->slot2light, "[slot2]"); + } + } + else if(strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0) + { + state->currentslot = 0; + sprintf(state->slot1light, "[sLoT1]"); + } + + else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0) + { + state->currentslot = 1; + sprintf(state->slot2light, "[sLoT2]"); + } if (opts->errorbars == 1) - { - fprintf (stderr, "%s %s ", state->slot0light, state->slot1light); - } + { + fprintf(stderr, "%s %s ", state->slot1light, state->slot2light); + } - // current slot second half, cach, next slot 1st half - skipDibit (opts, state, 120); + // Slot type - Second part - Parity bit + for (i = 0; i < 5; i++) + { + dibit = getDibit(opts, state); + if (opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + SlotType[(i*2) + 10] = (1 & (dibit >> 1)); // bit 1 + SlotType[(i*2) + 11] = (1 & dibit); // bit 0 + } + + /* Check and correct the SlotType (apply Golay(20,8) FEC check) */ + if(Golay_20_8_decode((unsigned char *)SlotType)) SlotTypeOk = 1; + else SlotTypeOk = 0; + + /* Slot Type parity checked => Fill the color code */ + state->color_code = (unsigned int)((cc[0] << 3) + (cc[1] << 2) + (cc[2] << 1) + cc[3]); + state->color_code_ok = SlotTypeOk; + + /* Reconstitute the burst type */ + burst = (unsigned int)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]); + + /* Reconstitute the burst type */ + bursttype[0] = SlotType[4] + '0'; + bursttype[1] = SlotType[5] + '0'; + bursttype[2] = SlotType[6] + '0'; + bursttype[3] = SlotType[7] + '0'; + bursttype[4] = '\0'; + + if (strcmp (bursttype, "0000") == 0) + { + sprintf(state->fsubtype, " PI Header "); + } + else if (strcmp (bursttype, "0001") == 0) + { + sprintf(state->fsubtype, " VOICE Header "); + } + else if (strcmp (bursttype, "0010") == 0) + { + sprintf(state->fsubtype, " TLC "); + } + else if (strcmp (bursttype, "0011") == 0) + { + sprintf(state->fsubtype, " CSBK "); + } + else if (strcmp (bursttype, "0100") == 0) + { + sprintf(state->fsubtype, " MBC Header "); + } + else if (strcmp (bursttype, "0101") == 0) + { + sprintf(state->fsubtype, " MBC "); + } + else if (strcmp (bursttype, "0110") == 0) + { + sprintf(state->fsubtype, " DATA Header "); + } + else if (strcmp (bursttype, "0111") == 0) + { + sprintf(state->fsubtype, " RATE 1/2 DATA"); + } + else if (strcmp (bursttype, "1000") == 0) + { + sprintf(state->fsubtype, " RATE 3/4 DATA"); + } + else if (strcmp (bursttype, "1001") == 0) + { + sprintf(state->fsubtype, " Slot idle "); + } + else if (strcmp (bursttype, "1010") == 0) + { + sprintf(state->fsubtype, " Rate 1 DATA "); + } + else + { + sprintf(state->fsubtype, " "); + } + + // Current slot - First half - Data Payload - 1st part + for (i = 0; i < 49; i++) + { + dibit = getDibit(opts, state); + if (opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1 + info[(2*i) + 99] = (1 & dibit); // bit 0 + } + + // Skip cach (24 bit = 12 dibit) and next slot 1st half (98 + 10 bit = 49 + 5 dibit) + skipDibit (opts, state, 12 + 49 + 5); if (opts->errorbars == 1) + { + /* Print the color code */ + fprintf(stderr, "| Color Code=%02d ", (int)state->color_code); + + if(state->color_code_ok) fprintf(stderr, "(OK) |"); + else fprintf(stderr, "(CRC ERR) |"); + + if (strcmp (state->fsubtype, " ") == 0) { - if (strcmp (state->fsubtype, " ") == 0) - { - fprintf (stderr, " Unknown burst type: %s\n", bursttype); - } - else - { - fprintf (stderr, "%s\n", state->fsubtype); - } + fprintf(stderr, " Unknown burst type: %s", bursttype); } -} + else + { + fprintf(stderr, "%s", state->fsubtype); + } + } + + switch(burst) + { + /* Burst = PI header */ + case 0b0000: + { + break; + } + + /* Burst = Voice LC header */ + case 0b0001: + { + /* Extract data from Voice LC Header */ + ProcessDmrVoiceLcHeader(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType); + break; + } + + /* Burst = TLC */ + case 0b0010: + { + ProcessDmrTerminaisonLC(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType); + break; + } + + /* Burst = CSBK */ + case 0b0011: + { + break; + } + + /* Burst = MBC Header */ + case 0b0100: + { + break; + } + + /* Burst = MBC */ + case 0b0101: + { + break; + } + + /* Burst = DATA Header */ + case 0b0110: + { + break; + } + + /* Burst = RATE 1/2 DATA */ + case 0b0111: + { + break; + } + + /* Burst = RATE 3/4 DATA */ + case 0b1000: + { + break; + } + + /* Burst = Slot idle */ + case 0b1001: + { + break; + } + + /* Burst = Rate 1 DATA */ + case 0b1010: + { + break; + } + + /* Default */ + default: + { + /* Nothing to do */ + break; + } + } /* End switch(burst) */ + + if (opts->errorbars == 1) + { + fprintf(stderr, "\n"); + } + +} /* End processDMRdata() */ diff --git a/src/dmr_encryption.c b/src/dmr_encryption.c new file mode 100644 index 0000000..afb8e5a --- /dev/null +++ b/src/dmr_encryption.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 DSD Author + * GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0) + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "dsd.h" +#include "dmr_const.h" +#include "p25p1_check_hdu.h" + + +void ProcessDMREncryption (dsd_opts * opts, dsd_state * state) +{ + uint32_t i, j; + uint32_t Frame; + TimeSlotVoiceSuperFrame_t * TSVoiceSupFrame = NULL; + int *errs; + int *errs2; + + /* + * Currently encryption is not supported in this public version... + */ + + /* Check the current time slot */ + if(state->currentslot == 0) + { + TSVoiceSupFrame = &state->TS1SuperFrame; + } + else + { + TSVoiceSupFrame = &state->TS2SuperFrame; + } + + /* Apply encryption here + * + * A DMR superframe = 6 frames x 3 AMBE voice sample of 49 bits each + * uint8_t KeyStream[6][3][49]; + * + * 1�) Initialize the "KeyStream" buffer (total 882 bits) with correct bits (depending of + * encryption mode used, MotoTRBO BP, Hytera BP, MotoTRBO EP, MotoTRBO AES... + * + * 2�) Apply a XOR between "KeyStream" and "TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j]" + * like code : + * for(Frame = 0; Frame < 6; Frame++) + * { + * for(i = 0; i < 3; i++) + * { + * for(j = 0; j < 49; j++) + * { + * TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i] ^= KeyStream[Frame][i][j]; + * } + * } + * } + * + * 3�) Play all AMBE decoded sample (see the "for" loop below) + * + * */ + + /* + * Play all AMBE voice samples + * 1 DMR voice superframe = 6 DMR frames */ + for(Frame = 0; Frame < 6; Frame++) + { + /* 1 DMR frame contains 3 AMBE voice samples */ + for(i = 0; i < 3; i++) + { + errs = (int*)&(TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].errs1[i]); + errs2 = (int*)&(TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].errs2[i]); + state->errs = TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement + state->errs2 = TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement + + mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errs, errs2, state->err_str, + TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i], + state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality); + + if (opts->mbe_out_f != NULL) + { + saveAmbe2450Data (opts, state, TSVoiceSupFrame->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]); + } + if (opts->errorbars == 1) + { + fprintf(stderr, "%s", state->err_str); + } + + state->debug_audio_errors += *errs2; + + processAudio(opts, state); + + if (opts->wav_out_f != NULL) + { + writeSynthesizedVoice (opts, state); + } + + if (opts->audio_out == 1 && opts->p25enc != 1) //play only if not equal to enc value, going to set p25enc to 1 if enc detected?? + { + playSynthesizedVoice (opts, state); + } + } /* End for(i = 0; i < 3; i++) */ + } /* End for(Frame = 0; Frame < 6; Frame++) */ +} /* End ProcessDMREncryption() */ + + + +/* End of file */ diff --git a/src/dmr_sync.c b/src/dmr_sync.c new file mode 100644 index 0000000..857276e --- /dev/null +++ b/src/dmr_sync.c @@ -0,0 +1,839 @@ +/* + * Copyright (C) 2010 DSD Author + * GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0) + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "dsd.h" + +//#define PRINT_PI_HEADER_BYTES +//#define PRINT_VOICE_LC_HEADER_BYTES +//#define PRINT_TERMINAISON_LC_BYTES +//#define PRINT_VOICE_BURST_BYTES + + +void ProcessDmrVoiceLcHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]) +{ + uint32_t i, j, k; + uint32_t CRCExtracted = 0; + uint32_t CRCComputed = 0; + uint32_t CRCCorrect = 0; + uint32_t IrrecoverableErrors = 0; + uint8_t DeInteleavedData[196]; + uint8_t DmrDataBit[96]; + uint8_t DmrDataByte[12]; + TimeSlotVoiceSuperFrame_t * TSVoiceSupFrame = NULL; + uint8_t R[3]; + uint8_t BPTCReservedBits = 0; + + /* Remove warning compiler */ + //UNUSED_VARIABLE(syncdata[0]); + //UNUSED_VARIABLE(SlotType[0]); + //UNUSED_VARIABLE(BPTCReservedBits); + + /* Check the current time slot */ + if(state->currentslot == 0) + { + TSVoiceSupFrame = &state->TS1SuperFrame; + } + else + { + TSVoiceSupFrame = &state->TS2SuperFrame; + } + + CRCExtracted = 0; + CRCComputed = 0; + IrrecoverableErrors = 0; + + /* Deinterleave DMR data */ + BPTCDeInterleaveDMRData(info, DeInteleavedData); + + /* Extract the BPTC 196,96 DMR data */ + IrrecoverableErrors = BPTC_196x96_Extract_Data(DeInteleavedData, DmrDataBit, R); + + /* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */ + BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x08); + + /* Convert the 96 bit of voice LC Header data into 12 bytes */ + k = 0; + for(i = 0; i < 12; i++) + { + DmrDataByte[i] = 0; + for(j = 0; j < 8; j++) + { + DmrDataByte[i] = DmrDataByte[i] << 1; + DmrDataByte[i] = DmrDataByte[i] | (DmrDataBit[k] & 0x01); + k++; + } + } + + /* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */ + CRCExtracted = 0; + for(i = 0; i < 24; i++) + { + CRCExtracted = CRCExtracted << 1; + CRCExtracted = CRCExtracted | (uint32_t)(DmrDataBit[i + 72] & 1); + } + + /* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */ + CRCExtracted = CRCExtracted ^ 0x969696; + + /* Check/correct the full link control data and compute the Reed-Solomon (12,9) CRC */ + CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0x969696); + + /* Convert corrected 12 bytes into 96 bits */ + for(i = 0, j = 0; i < 12; i++, j+=8) + { + DmrDataBit[j + 0] = (DmrDataByte[i] >> 7) & 0x01; + DmrDataBit[j + 1] = (DmrDataByte[i] >> 6) & 0x01; + DmrDataBit[j + 2] = (DmrDataByte[i] >> 5) & 0x01; + DmrDataBit[j + 3] = (DmrDataByte[i] >> 4) & 0x01; + DmrDataBit[j + 4] = (DmrDataByte[i] >> 3) & 0x01; + DmrDataBit[j + 5] = (DmrDataByte[i] >> 2) & 0x01; + DmrDataBit[j + 6] = (DmrDataByte[i] >> 1) & 0x01; + DmrDataBit[j + 7] = (DmrDataByte[i] >> 0) & 0x01; + } + + /* Store the Protect Flag (PF) bit */ + TSVoiceSupFrame->FullLC.ProtectFlag = (unsigned int)(DmrDataBit[0]); + + /* Store the Reserved bit */ + TSVoiceSupFrame->FullLC.Reserved = (unsigned int)(DmrDataBit[1]); + + /* Store the Full Link Control Opcode (FLCO) */ + TSVoiceSupFrame->FullLC.FullLinkControlOpcode = (unsigned int)ConvertBitIntoBytes(&DmrDataBit[2], 6); + + /* Store the Feature set ID (FID) */ + TSVoiceSupFrame->FullLC.FeatureSetID = (unsigned int)ConvertBitIntoBytes(&DmrDataBit[8], 8); + //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + + /* Store the Service Options */ + TSVoiceSupFrame->FullLC.ServiceOptions = (unsigned int)ConvertBitIntoBytes(&DmrDataBit[16], 8); + + /* Store the Group address (Talk Group) */ + TSVoiceSupFrame->FullLC.GroupAddress = (unsigned int)ConvertBitIntoBytes(&DmrDataBit[24], 24); + //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + /* Store the Source address */ + TSVoiceSupFrame->FullLC.SourceAddress = (unsigned int)ConvertBitIntoBytes(&DmrDataBit[48], 24); + //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + + if((IrrecoverableErrors == 0) && CRCCorrect) + { + /* CRC is correct so consider the Full LC data as correct/valid */ + TSVoiceSupFrame->FullLC.DataValidity = 1; + } + else + { + /* CRC checking error, so consider the Full LC data as invalid */ + TSVoiceSupFrame->FullLC.DataValidity = 0; + } + + /* Print the destination ID (TG) and the source ID */ + fprintf(stderr, "| TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); + fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); + //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + + //HERE HERE do some work to get this Emergency and BP modes to display in ncurses + //state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + + if(TSVoiceSupFrame->FullLC.ServiceOptions & 0x80) fprintf(stderr, "Emergency "); + if(TSVoiceSupFrame->FullLC.ServiceOptions & 0x40) + { + /* By default select the basic privacy (BP), if the encryption mode is EP ARC4 or AES256 + * a PI Header will be sent with the encryption mode and DSD will upgrade automatically + * the new encryption mode */ + opts->EncryptionMode = MODE_BASIC_PRIVACY; + + fprintf(stderr, "Encrypted "); + } + else + { + opts->EncryptionMode = MODE_UNENCRYPTED; + fprintf(stderr, "Clear/Unencrypted "); + } + + /* Check the "Reserved" bits */ + if(TSVoiceSupFrame->FullLC.ServiceOptions & 0x30) + { + /* Experimentally determined with DSD+, when the "Reserved" bit field + * is equal to 0x2, this is a TXI call */ + if((TSVoiceSupFrame->FullLC.ServiceOptions & 0x30) == 0x20) fprintf(stderr, "TXI "); + else fprintf(stderr, "Reserved=%d ", (TSVoiceSupFrame->FullLC.ServiceOptions & 0x30) >> 4); + } + if(TSVoiceSupFrame->FullLC.ServiceOptions & 0x08) fprintf(stderr, "Broadcast "); + if(TSVoiceSupFrame->FullLC.ServiceOptions & 0x04) fprintf(stderr, "OVCM "); + if(TSVoiceSupFrame->FullLC.ServiceOptions & 0x03) + { + if((TSVoiceSupFrame->FullLC.ServiceOptions & 0x03) == 0x01) fprintf(stderr, "Priority 1 "); + else if((TSVoiceSupFrame->FullLC.ServiceOptions & 0x03) == 0x02) fprintf(stderr, "Priority 2 "); + else if((TSVoiceSupFrame->FullLC.ServiceOptions & 0x03) == 0x03) fprintf(stderr, "Priority 3 "); + else fprintf(stderr, "No Priority "); /* We should never go here */ + } + fprintf(stderr, "Call "); + + if(TSVoiceSupFrame->FullLC.DataValidity) fprintf(stderr, "(OK) "); + else if(IrrecoverableErrors == 0) fprintf(stderr, "RAS (FEC OK/CRC ERR)"); + else fprintf(stderr, "(FEC FAIL/CRC ERR)"); + +#ifdef PRINT_VOICE_LC_HEADER_BYTES + fprintf(stderr, "\n"); + fprintf(stderr, "VOICE LC HEADER : "); + for(i = 0; i < 12; i++) + { + fprintf(stderr, "0x%02X", DmrDataByte[i]); + if(i != 11) fprintf(stderr, " - "); + } + + fprintf(stderr, "\n"); + + fprintf(stderr, "BPTC(196,96) Reserved bit R(0)-R(2) = 0x%02X\n", BPTCReservedBits); + + fprintf(stderr, "CRC extracted = 0x%04X - CRC computed = 0x%04X - ", CRCExtracted, CRCComputed); + + if((IrrecoverableErrors == 0) && CRCCorrect) + { + fprintf(stderr, "CRCs are equal + FEC OK !\n"); + } + else if(IrrecoverableErrors == 0) + { + else fprintf(stderr, "FEC correctly corrected but CRCs are incorrect\n"); + } + else + { + fprintf(stderr, "ERROR !!! CRCs are different and FEC Failed !\n"); + } + + fprintf(stderr, "Hamming Irrecoverable Errors = %u\n", IrrecoverableErrors); +#endif /* PRINT_VOICE_LC_HEADER_BYTES */ +} /* End ProcessDmrVoiceLcHeader() */ + + +void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]) +{ + uint32_t i, j, k; + uint32_t CRCExtracted = 0; + uint32_t CRCComputed = 0; + uint32_t CRCCorrect = 0; + uint32_t IrrecoverableErrors = 0; + uint8_t DeInteleavedData[196]; + uint8_t DmrDataBit[96]; + uint8_t DmrDataByte[12]; + TimeSlotVoiceSuperFrame_t * TSVoiceSupFrame = NULL; + uint8_t R[3]; + uint8_t BPTCReservedBits = 0; + + /* Remove warning compiler */ + //UNUSED_VARIABLE(opts); + //UNUSED_VARIABLE(syncdata[0]); + //UNUSED_VARIABLE(SlotType[0]); + //UNUSED_VARIABLE(BPTCReservedBits); + + /* Check the current time slot */ + if(state->currentslot == 0) + { + TSVoiceSupFrame = &state->TS1SuperFrame; + } + else + { + TSVoiceSupFrame = &state->TS2SuperFrame; + } + + CRCExtracted = 0; + CRCComputed = 0; + IrrecoverableErrors = 0; + + /* Deinterleave DMR data */ + BPTCDeInterleaveDMRData(info, DeInteleavedData); + + /* Extract the BPTC 196,96 DMR data */ + IrrecoverableErrors = BPTC_196x96_Extract_Data(DeInteleavedData, DmrDataBit, R); + + /* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */ + BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x08); + + /* Convert the 96 bit of voice LC Header data into 12 bytes */ + k = 0; + for(i = 0; i < 12; i++) + { + DmrDataByte[i] = 0; + for(j = 0; j < 8; j++) + { + DmrDataByte[i] = DmrDataByte[i] << 1; + DmrDataByte[i] = DmrDataByte[i] | (DmrDataBit[k] & 0x01); + k++; + } + } + + /* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */ + CRCExtracted = 0; + for(i = 0; i < 24; i++) + { + CRCExtracted = CRCExtracted << 1; + CRCExtracted = CRCExtracted | (uint32_t)(DmrDataBit[i + 72] & 1); + } + + /* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */ + CRCExtracted = CRCExtracted ^ 0x999999; + + /* Check/correct the full link control data and compute the Reed-Solomon (12,9) CRC */ + CRCCorrect = ComputeAndCorrectFullLinkControlCrc(DmrDataByte, &CRCComputed, 0x999999); + + /* Convert corrected 12 bytes into 96 bits */ + for(i = 0, j = 0; i < 12; i++, j+=8) + { + DmrDataBit[j + 0] = (DmrDataByte[i] >> 7) & 0x01; + DmrDataBit[j + 1] = (DmrDataByte[i] >> 6) & 0x01; + DmrDataBit[j + 2] = (DmrDataByte[i] >> 5) & 0x01; + DmrDataBit[j + 3] = (DmrDataByte[i] >> 4) & 0x01; + DmrDataBit[j + 4] = (DmrDataByte[i] >> 3) & 0x01; + DmrDataBit[j + 5] = (DmrDataByte[i] >> 2) & 0x01; + DmrDataBit[j + 6] = (DmrDataByte[i] >> 1) & 0x01; + DmrDataBit[j + 7] = (DmrDataByte[i] >> 0) & 0x01; + } + + /* Store the Protect Flag (PF) bit */ + TSVoiceSupFrame->FullLC.ProtectFlag = (unsigned int)(DmrDataBit[0]); + + /* Store the Reserved bit */ + TSVoiceSupFrame->FullLC.Reserved = (unsigned int)(DmrDataBit[1]); + + /* Store the Full Link Control Opcode (FLCO) */ + TSVoiceSupFrame->FullLC.FullLinkControlOpcode = (unsigned int)ConvertBitIntoBytes(&DmrDataBit[2], 6); + + /* Store the Feature set ID (FID) */ + TSVoiceSupFrame->FullLC.FeatureSetID = (unsigned int)ConvertBitIntoBytes(&DmrDataBit[8], 8); + //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + + /* Store the Service Options */ + TSVoiceSupFrame->FullLC.ServiceOptions = (unsigned int)ConvertBitIntoBytes(&DmrDataBit[16], 8); + + /* Store the Group address (Talk Group) */ + TSVoiceSupFrame->FullLC.GroupAddress = (unsigned int)ConvertBitIntoBytes(&DmrDataBit[24], 24); + //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + /* Store the Source address */ + TSVoiceSupFrame->FullLC.SourceAddress = (unsigned int)ConvertBitIntoBytes(&DmrDataBit[48], 24); + //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + if((IrrecoverableErrors == 0))// && CRCCorrect) + { + /* CRC is correct so consider the Full LC data as correct/valid */ + TSVoiceSupFrame->FullLC.DataValidity = 1; + } + else + { + /* CRC checking error, so consider the Full LC data as invalid */ + TSVoiceSupFrame->FullLC.DataValidity = 0; + } + + /* Print the destination ID (TG) and the source ID */ + fprintf(stderr, "| TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); + fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); + //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + + + if((IrrecoverableErrors == 0) && CRCCorrect) + { + fprintf(stderr, "(OK) "); + //only set on good CRC value or corrected values + state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + state->dmr_color_code = state->color_code; + } + else if(IrrecoverableErrors == 0) + { + fprintf(stderr, "RAS (FEC OK/CRC ERR)"); + //only set on good CRC value or corrected values + state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + state->dmr_color_code = state->color_code; + } + else fprintf(stderr, "(FEC FAIL/CRC ERR)"); + +#ifdef PRINT_TERMINAISON_LC_BYTES + fprintf(stderr, "\n"); + fprintf(stderr, "TERMINAISON LINK CONTROL (TLC) : "); + for(i = 0; i < 12; i++) + { + fprintf(stderr, "0x%02X", DmrDataByte[i]); + if(i != 11) fprintf(stderr, " - "); + } + + fprintf(stderr, "\n"); + + fprintf(stderr, "BPTC(196,96) Reserved bit R(0)-R(2) = 0x%02X\n", BPTCReservedBits); + + fprintf(stderr, "CRC extracted = 0x%04X - CRC computed = 0x%04X - ", CRCExtracted, CRCComputed); + + if((IrrecoverableErrors == 0) && CRCCorrect) + { + fprintf(stderr, "CRCs are equal + FEC OK !\n"); + } + else if(IrrecoverableErrors == 0) + { + else fprintf(stderr, "FEC correctly corrected but CRCs are incorrect\n"); + } + else + { + fprintf(stderr, "ERROR !!! CRCs are different and FEC Failed !\n"); + } + + fprintf(stderr, "Hamming Irrecoverable Errors = %u\n", IrrecoverableErrors); +#endif /* PRINT_TERMINAISON_LC_BYTES */ +} /* End ProcessDmrTerminaisonLC() */ + + +/* Extract the Link Control (LC) embedded in the SYNC + * of a DMR voice superframe */ +void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state) +{ + uint32_t i, j, k; + uint32_t Burst; + uint8_t BptcDataMatrix[8][16]; + uint8_t LC_DataBit[77]; + uint8_t LC_DataBytes[10]; + TimeSlotVoiceSuperFrame_t * TSVoiceSupFrame = NULL; + uint32_t IrrecoverableErrors; + uint8_t CRCExtracted; + uint8_t CRCComputed; + uint32_t CRCCorrect = 0; + + /* Remove warning compiler */ + //UNUSED_VARIABLE(opts); + + /* Check the current time slot */ + if(state->currentslot == 0) + { + TSVoiceSupFrame = &state->TS1SuperFrame; + } + else + { + TSVoiceSupFrame = &state->TS2SuperFrame; + } + + /* First step : Reconstitute the BPTC 16x8 matrix */ + Burst = 1; /* Burst B to E contains embedded signaling data */ + k = 0; + for(i = 0; i < 16; i++) + { + for(j = 0; j < 8; j++) + { + /* Only the LSBit of the byte is stored */ + BptcDataMatrix[j][i] = TSVoiceSupFrame->TimeSlotRawVoiceFrame[Burst].Sync[k + 8]; + k++; + + /* Go on to the next burst once 32 bit + * of the SNYC have been stored */ + if(k >= 32) + { + k = 0; + Burst++; + } + } /* End for(j = 0; j < 8; j++) */ + } /* End for(i = 0; i < 16; i++) */ + + /* Extract the 72 LC bit (+ 5 CRC bit) of the matrix */ + IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit); + + /* Convert the 77 bit of voice LC Header data into 9 bytes */ + k = 0; + for(i = 0; i < 10; i++) + { + LC_DataBytes[i] = 0; + for(j = 0; j < 8; j++) + { + LC_DataBytes[i] = LC_DataBytes[i] << 1; + LC_DataBytes[i] = LC_DataBytes[i] | (LC_DataBit[k] & 0x01); + k++; + if(k >= 76) break; + } + } + + /* Reconstitute the 5 bit CRC */ + CRCExtracted = (LC_DataBit[72] & 1) << 4; + CRCExtracted |= (LC_DataBit[73] & 1) << 3; + CRCExtracted |= (LC_DataBit[74] & 1) << 2; + CRCExtracted |= (LC_DataBit[75] & 1) << 1; + CRCExtracted |= (LC_DataBit[76] & 1) << 0; + + /* Compute the 5 bit CRC */ + CRCComputed = ComputeCrc5Bit(LC_DataBit); + + if(CRCExtracted == CRCComputed) CRCCorrect = 1; + else CRCCorrect = 0; + + /* Store the Protect Flag (PF) bit */ + TSVoiceSupFrame->FullLC.ProtectFlag = (unsigned int)(LC_DataBit[0]); + + /* Store the Reserved bit */ + TSVoiceSupFrame->FullLC.Reserved = (unsigned int)(LC_DataBit[1]); + + /* Store the Full Link Control Opcode (FLCO) */ + TSVoiceSupFrame->FullLC.FullLinkControlOpcode = (unsigned int)ConvertBitIntoBytes(&LC_DataBit[2], 6); + + /* Store the Feature set ID (FID) */ + TSVoiceSupFrame->FullLC.FeatureSetID = (unsigned int)ConvertBitIntoBytes(&LC_DataBit[8], 8); + //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + + /* Store the Service Options */ + TSVoiceSupFrame->FullLC.ServiceOptions = (unsigned int)ConvertBitIntoBytes(&LC_DataBit[16], 8); + + /* Store the Group address (Talk Group) */ + TSVoiceSupFrame->FullLC.GroupAddress = (unsigned int)ConvertBitIntoBytes(&LC_DataBit[24], 24); + //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + /* Store the Source address */ + TSVoiceSupFrame->FullLC.SourceAddress = (unsigned int)ConvertBitIntoBytes(&LC_DataBit[48], 24); + //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + + /* Check the CRC values */ + if((IrrecoverableErrors == 0))// && CRCCorrect) + { + /* Data ara correct */ + //fprintf(stderr, "\nLink Control (LC) Data CRCs are correct !!! Number of error = %u\n", NbOfError); + + /* CRC is correct so consider the Full LC data as correct/valid */ + TSVoiceSupFrame->FullLC.DataValidity = 1; + } + else + { + /* CRC checking error, so consider the Full LC data as invalid */ + TSVoiceSupFrame->FullLC.DataValidity = 0; + } + + /* Print the destination ID (TG) and the source ID */ + fprintf(stderr, "| TG=%u Src=%u ", TSVoiceSupFrame->FullLC.GroupAddress, TSVoiceSupFrame->FullLC.SourceAddress); + fprintf(stderr, "FID=0x%02X ", TSVoiceSupFrame->FullLC.FeatureSetID); + //state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + //state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + //state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + + if((IrrecoverableErrors == 0) && CRCCorrect) + { + fprintf(stderr, "(OK)"); + //only set on good CRC? + state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + state->dmr_color_code = state->color_code; + } + else if(IrrecoverableErrors == 0) + { + fprintf(stderr, "RAS (FEC OK/CRC ERR)"); + //or set if was corrected + state->lasttg = TSVoiceSupFrame->FullLC.GroupAddress; + state->lastsrc = TSVoiceSupFrame->FullLC.SourceAddress; + state->dmr_fid = TSVoiceSupFrame->FullLC.FeatureSetID; + state->dmr_so = TSVoiceSupFrame->FullLC.ServiceOptions; + state->dmr_color_code = state->color_code; + } + else fprintf(stderr, "(FEC FAIL/CRC ERR)"); + +#ifdef PRINT_VOICE_BURST_BYTES + fprintf(stderr, "\n"); + fprintf(stderr, "VOICE BURST BYTES : "); + for(i = 0; i < 10; i++) + { + fprintf(stderr, "0x%02X", LC_DataBytes[i]); + if(i != 9) fprintf(stderr, " - "); + } + + fprintf(stderr, "\n"); + + fprintf(stderr, "CRC extracted = 0x%04X - CRC computed = 0x%04X - ", CRCExtracted, CRCComputed); + + if((IrrecoverableErrors == 0) && CRCCorrect) + { + fprintf(stderr, "CRCs are equal + FEC OK !\n"); + } + else if(IrrecoverableErrors == 0) + { + else fprintf(stderr, "FEC correctly corrected but CRCs are incorrect\n"); + } + else + { + fprintf(stderr, "ERROR !!! CRCs are different and FEC Failed !\n"); + } + + fprintf(stderr, "Hamming Irrecoverable Errors = %u\n", IrrecoverableErrors); +#endif /* PRINT_VOICE_BURST_BYTES */ + +} /* End ProcessVoiceBurstSync() */ + + +/* + * @brief : This function compute the CRC-CCITT of the DMR data + * by using the polynomial x^16 + x^12 + x^5 + 1 + * + * @param Input : A buffer pointer of the DMR data (80 bytes) + * + * @return The 16 bit CRC + */ +uint16_t ComputeCrcCCITT(uint8_t * DMRData) +{ + 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 < 80; i++) + { + if(((CRC >> 15) & 1) ^ (DMRData[i] & 1)) + { + CRC = (CRC << 1) ^ Polynome; + } + else + { + CRC <<= 1; + } + } + + /* Invert the CRC */ + CRC ^= 0xFFFF; + + /* Return the CRC */ + return CRC; +} /* End ComputeCrcCCITT() */ + + +/* + * @brief : This function compute the CRC-24 bit of the full + * link control by using the Reed-Solomon(12,9) FEC + * + * @param FullLinkControlDataBytes : A buffer pointer of the DMR data bytes (12 bytes) + * + * @param CRCComputed : A 32 bit pointer where the computed CRC 24-bit will be stored + * + * @param CRCMask : The 24 bit CRC mask to apply + * + * @return 0 = CRC error + * 1 = CRC is correct + */ +uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask) +{ + uint32_t i; + rs_12_9_codeword_t VoiceLCHeaderStr; + rs_12_9_poly_t syndrome; + uint8_t errors_found = 0; + rs_12_9_correct_errors_result_t result = RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND; + uint32_t CrcIsCorrect = 0; + + for(i = 0; i < 12; i++) + { + VoiceLCHeaderStr.data[i] = FullLinkControlDataBytes[i]; + + /* Apply CRC mask on each 3 last bytes + * of the full link control */ + if(i == 9) + { + VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 16); + } + else if(i == 10) + { + VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 8); + } + else if(i == 11) + { + VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask); + } + else + { + /* Nothing to do */ + } + } + + /* Check and correct the full link LC control with Reed Solomon (12,9) FEC */ + rs_12_9_calc_syndrome(&VoiceLCHeaderStr, &syndrome); + if(rs_12_9_check_syndrome(&syndrome) != 0) result = rs_12_9_correct_errors(&VoiceLCHeaderStr, &syndrome, &errors_found); + + /* Reconstitue the CRC */ + *CRCComputed = (uint32_t)((VoiceLCHeaderStr.data[9] << 16) & 0xFF0000); + *CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[10] << 8) & 0x00FF00); + *CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[11] << 0) & 0x0000FF); + + if((result == RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND) || + (result == RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CORRECTED)) + { + //fprintf(stderr, "CRC OK : 0x%06X\n", *CRCComputed); + CrcIsCorrect = 1; + + /* Reconstitue full link control data after FEC correction */ + for(i = 0; i < 12; i++) + { + FullLinkControlDataBytes[i] = VoiceLCHeaderStr.data[i]; + + /* Apply CRC mask on each 3 last bytes + * of the full link control */ + if(i == 9) + { + FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 16); + } + else if(i == 10) + { + FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 8); + } + else if(i == 11) + { + FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask); + } + else + { + /* Nothing to do */ + } + } + } + else + { + //fprintf(stderr, "CRC ERROR : 0x%06X\n", *CRCComputed); + CrcIsCorrect = 0; + } + + /* Return the CRC status */ + return CrcIsCorrect; +} /* End ComputeAndCorrectFullLinkControlCrc() */ + + +/* + * @brief : This function compute the 5 bit CRC of the DMR voice burst data + * See ETSI TS 102 361-1 chapter B.3.11 + * + * @param Input : A buffer pointer of the DMR data (72 bytes) + * + * @return The 5 bit CRC + */ +uint8_t ComputeCrc5Bit(uint8_t * DMRData) +{ + uint32_t i, j, k; + uint8_t Buffer[9]; + uint32_t Sum; + uint8_t CRC = 0; + + /* Convert the 72 bit into 9 bytes */ + k = 0; + for(i = 0; i < 9; i++) + { + Buffer[i] = 0; + for(j = 0; j < 8; j++) + { + Buffer[i] = Buffer[i] << 1; + Buffer[i] = Buffer[i] | DMRData[k++]; + } + } + + /* Add all 9 bytes */ + Sum = 0; + for(i = 0; i < 9; i++) + { + Sum += (uint32_t)Buffer[i]; + } + + /* Sum MOD 31 = CRC */ + CRC = (uint8_t)(Sum % 31); + + /* Return the CRC */ + return CRC; +} /* End ComputeCrc5Bit() */ + + +/* + * @brief : This function returns the Algorithm ID into an explicit string + * + * @param AlgID : The algorithm ID + * @arg : 0x21 for ARC4 + * @arg : 0x22 for DES + * @arg : 0x25 for AES256 + * + * @return A constant string pointer that explain the Alg ID used + */ +uint8_t * DmrAlgIdToStr(uint8_t AlgID) +{ + if(AlgID == 0x21) return (uint8_t *)"ARC4"; + else if(AlgID == 0x25) return (uint8_t *)"AES256"; + else return (uint8_t *)"UNKNOWN"; + //state->payload_algid = AlgID; +} /* End DmrAlgIdToStr */ + +/* + * @brief : This function returns the encryption mode into an explicit string + * + * @param PrivacyMode : The algorithm ID + * @arg : MODE_UNENCRYPTED + * @arg : MODE_BASIC_PRIVACY + * @arg : MODE_ENHANCED_PRIVACY_ARC4 + * @arg : MODE_ENHANCED_PRIVACY_DES + * @arg : MODE_ENHANCED_PRIVACY_AES256 + * @arg : MODE_HYTERA_BASIC_40_BIT + * @arg : MODE_HYTERA_BASIC_128_BIT + * @arg : MODE_HYTERA_BASIC_256_BIT + * + * @return A constant string pointer that explain the encryption mode used + */ +uint8_t * DmrAlgPrivacyModeToStr(uint32_t PrivacyMode) +{ + switch(PrivacyMode) + { + case MODE_UNENCRYPTED: + { + return (uint8_t *)"NOT ENC"; + break; + } + case MODE_BASIC_PRIVACY: + { + return (uint8_t *)"BP"; + break; + } + case MODE_ENHANCED_PRIVACY_ARC4: + { + return (uint8_t *)"EP ARC4"; + break; + } + case MODE_ENHANCED_PRIVACY_AES256: + { + return (uint8_t *)"EP AES256"; + break; + } + case MODE_HYTERA_BASIC_40_BIT: + { + return (uint8_t *)"HYTERA BASIC 40 BIT"; + break; + } + case MODE_HYTERA_BASIC_128_BIT: + { + return (uint8_t *)"HYTERA BASIC 128 BIT"; + break; + } + case MODE_HYTERA_BASIC_256_BIT: + { + return (uint8_t *)"HYTERA BASIC 256 BIT"; + break; + } + default: + { + return (uint8_t *)"UNKNOWN"; + break; + } + } /* End switch(PrivacyMode) */ +} /* End DmrAlgPrivacyModeToStr() */ + + +/* End of file */ diff --git a/src/dmr_voice.c b/src/dmr_voice.c index b7a76f7..5522a76 100644 --- a/src/dmr_voice.c +++ b/src/dmr_voice.c @@ -18,365 +18,633 @@ #include "dsd.h" #include "dmr_const.h" -void -processDMRvoice (dsd_opts * opts, dsd_state * state) +void processDMRvoice (dsd_opts * opts, dsd_state * state) { // extracts AMBE frames from DMR frame - int i, j, dibit; + int i, j, k, l, dibit; int *dibit_p; - char ambe_fr[4][24]; - char ambe_fr2[4][24]; - char ambe_fr3[4][24]; + char ambe_fr[4][24] = {0}; + char ambe_fr2[4][24] = {0}; + char ambe_fr3[4][24] = {0}; const int *w, *x, *y, *z; char sync[25]; - char syncdata[25]; - char cachdata[13]; + char syncdata[48]; + char cachdata[13] = {0}; int mutecurrentslot; int msMode; + char cc[4]; + unsigned char EmbeddedSignalling[16]; + unsigned int EmbeddedSignallingOk; + + /* Remove warning compiler */ + //UNUSED_VARIABLE(ambe_fr[0][0]); + //UNUSED_VARIABLE(ambe_fr2[0][0]); + //UNUSED_VARIABLE(ambe_fr3[0][0]); + //UNUSED_VARIABLE(cachdata[0]); #ifdef DMR_DUMP int k; char syncbits[49]; - char cachbits[25]; + char cachbits[25];12 #endif + /* Init the superframe buffers */ + memset(&state->TS1SuperFrame, 0, sizeof(TimeSlotVoiceSuperFrame_t)); + memset(&state->TS2SuperFrame, 0, sizeof(TimeSlotVoiceSuperFrame_t)); + + /* Init the color code status */ + state->color_code_ok = 0; + mutecurrentslot = 0; msMode = 0; dibit_p = state->dibit_buf_p - 144; - for (j = 0; j < 6; j++) + for(j = 0; j < 6; j++) + { + // 2nd half of previous slot + for(i = 0; i < 54; i++) { - // 2nd half of previous slot - for (i = 0; i < 54; i++) + if(j > 0) + { + dibit = getDibit(opts, state); + } + else + { + dibit = *dibit_p; + dibit_p++; + if(opts->inverted_dmr == 1) { - if (j > 0) - { - dibit = getDibit (opts, state); - } - else - { - dibit = *dibit_p; - dibit_p++; - if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - } + dibit = (dibit ^ 2); } + } + } - // CACH - for (i = 0; i < 12; i++) + // CACH + for(i = 0; i < 12; i++) + { + if(j > 0) + { + dibit = getDibit(opts, state); + } + else + { + dibit = *dibit_p; + dibit_p++; + if(opts->inverted_dmr == 1) { - if (j > 0) - { - dibit = getDibit (opts, state); - } - else - { - dibit = *dibit_p; - dibit_p++; - if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - } - cachdata[i] = dibit; - if (i == 2) - { - state->currentslot = (1 & (dibit >> 1)); // bit 1 - if (state->currentslot == 0) - { - state->slot0light[0] = '['; - state->slot0light[6] = ']'; - state->slot1light[0] = ' '; - state->slot1light[6] = ' '; - } - else - { - state->slot1light[0] = '['; - state->slot1light[6] = ']'; - state->slot0light[0] = ' '; - state->slot0light[6] = ' '; - } - } + dibit = (dibit ^ 2); } - cachdata[12] = 0; + } + cachdata[i] = dibit; + if(i == 2) + { + /* Change the current slot only if we are in relayed mode (not in direct mode) */ + if(state->directmode == 0) state->currentslot = (1 & (dibit >> 1)); // bit 1 + + if(state->currentslot == 0) + { + state->slot1light[0] = '['; + state->slot1light[6] = ']'; + state->slot2light[0] = ' '; + state->slot2light[6] = ' '; + } + else + { + state->slot2light[0] = '['; + state->slot2light[6] = ']'; + state->slot1light[0] = ' '; + state->slot1light[6] = ' '; + } + } + } + cachdata[12] = 0; #ifdef DMR_DUMP - k = 0; - for (i = 0; i < 12; i++) - { - dibit = cachdata[i]; - cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 - k++; - cachbits[k] = (1 & dibit) + 48; // bit 0 - k++; - } - cachbits[24] = 0; - fprintf (stderr, "%s ", cachbits); + k = 0; + for(i = 0; i < 12; i++) + { + dibit = cachdata[i]; + cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 + k++; + cachbits[k] = (1 & dibit) + 48; // bit 0 + k++; + } + cachbits[24] = 0; + fprintf(stderr, "%s ", cachbits); #endif - // current slot frame 1 - w = rW; - x = rX; - y = rY; - z = rZ; - for (i = 0; i < 36; i++) + // current slot frame 1 + w = rW; + x = rX; + y = rY; + z = rZ; + k = 0; + for(i = 0; i < 36; i++) + { + if(j > 0) + { + dibit = getDibit(opts, state); + } + else + { + dibit = *dibit_p; + dibit_p++; + if(opts->inverted_dmr == 1) { - if (j > 0) - { - dibit = getDibit (opts, state); - } - else - { - dibit = *dibit_p; - dibit_p++; - if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - } - ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1 - ambe_fr[*y][*z] = (1 & dibit); // bit 0 - w++; - x++; - y++; - z++; + dibit = (dibit ^ 2); } + } + ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr[*y][*z] = (1 & dibit); // bit 0 - // current slot frame 2 first half - w = rW; - x = rX; - y = rY; - z = rZ; - for (i = 0; i < 18; i++) - { - if (j > 0) - { - dibit = getDibit (opts, state); - } - else - { - dibit = *dibit_p; - dibit_p++; - if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - } - ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1 - ambe_fr2[*y][*z] = (1 & dibit); // bit 0 - w++; - x++; - y++; - z++; - } + /* Save the current frame in the appropriate superframe buffer */ + if(state->currentslot == 0) + { + /* Time slot 1 superframe buffer filling => RAW voice bit */ + state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & (dibit >> 1)); // bit 1 + state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & dibit); // bit 0 - // signaling data or sync - for (i = 0; i < 24; i++) - { - if (j > 0) - { - dibit = getDibit (opts, state); - } - else - { - dibit = *dibit_p; - dibit_p++; - if (opts->inverted_dmr == 1) - { - dibit = (dibit ^ 2); - } - } - syncdata[i] = dibit; - sync[i] = (dibit | 1) + 48; - } - sync[24] = 0; - syncdata[24] = 0; + /* Time slot 1 superframe buffer filling => Deinterleaved voice bit */ + state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*w][*x] = (1 & (dibit >> 1)); // bit 1 + state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*y][*z] = (1 & dibit); // bit 0 + } + else + { + /* Time slot 2 superframe buffer filling => RAW voice bit */ + state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & (dibit >> 1)); // bit 1 + state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & dibit); // bit 0 - if ((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0)) - { - mutecurrentslot = 1; - if (state->currentslot == 0) - { - sprintf (state->slot0light, "[slot0]"); - } - else - { - sprintf (state->slot1light, "[slot1]"); - } - } - else if ((strcmp (sync, DMR_BS_VOICE_SYNC) == 0) || (strcmp (sync, DMR_MS_VOICE_SYNC) == 0)) - { - mutecurrentslot = 0; - if (state->currentslot == 0) - { - sprintf (state->slot0light, "[SLOT0]"); - } - else - { - sprintf (state->slot1light, "[SLOT1]"); - } - } - if ((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0)) - { - msMode = 1; - } + /* Time slot 2 superframe buffer filling => Deinterleaved voice bit */ + state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*w][*x] = (1 & (dibit >> 1)); // bit 1 + state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*y][*z] = (1 & dibit); // bit 0 + } - if ((j == 0) && (opts->errorbars == 1)) + w++; + x++; + y++; + z++; + } + + // current slot frame 2 first half + w = rW; + x = rX; + y = rY; + z = rZ; + k = 0; + for(i = 0; i < 18; i++) + { + if(j > 0) + { + dibit = getDibit(opts, state); + } + else + { + dibit = *dibit_p; + dibit_p++; + if(opts->inverted_dmr == 1) { - fprintf (stderr, "%s %s VOICE e:", state->slot0light, state->slot1light); + dibit = (dibit ^ 2); } + } + ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr2[*y][*z] = (1 & dibit); // bit 0 + + /* Save the current frame in the appropriate superframe buffer */ + if(state->currentslot == 0) + { + /* Time slot 1 superframe buffer filling => RAW voice bit */ + state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1 + state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0 + + /* Time slot 1 superframe buffer filling => Deinterleaved voice bit */ + state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1 + state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0 + } + else + { + /* Time slot 2 superframe buffer filling => RAW voice bit */ + state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1 + state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0 + + /* Time slot 2 superframe buffer filling => Deinterleaved voice bit */ + state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1 + state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0 + } + + w++; + x++; + y++; + z++; + } + + l = 0; + // signaling data or sync + for(i = 0; i < 24; i++) + { + if(j > 0) + { + dibit = getDibit(opts, state); + } + else + { + dibit = *dibit_p; + dibit_p++; + if(opts->inverted_dmr == 1) + { + dibit = (dibit ^ 2); + } + } + syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1 + syncdata[(2*i)+1] = (1 & dibit); // bit 0 + sync[i] = (dibit | 1) + 48; + + /* Save the current frame in the appropriate superframe buffer */ + if(state->currentslot == 0) + { + /* Time slot 1 superframe buffer filling => SYNC data */ + state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & (dibit >> 1)); // bit 1 + state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & dibit); // bit 0 + } + else + { + /* Time slot 2 superframe buffer filling => SYNC data */ + state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & (dibit >> 1)); // bit 1 + state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & dibit); // bit 0 + } + } + sync[24] = 0; + + /* Copy the Embedded signaling (1st part) */ + for(i = 0; i < 8; i++) + { + EmbeddedSignalling[i] = syncdata[i]; + } + /* Copy the Embedded signaling (2nd part) */ + for(i = 0; i < 8; i++) + { + EmbeddedSignalling[i + 8] = syncdata[i + 40]; + } + + /* Check and correct the SlotType (apply Golay(20,8) FEC check) */ + if(QR_16_7_6_decode(EmbeddedSignalling)) EmbeddedSignallingOk = 1; + else EmbeddedSignallingOk = 0; + + /* Get the color code */ + cc[0] = EmbeddedSignalling[0]; + cc[1] = EmbeddedSignalling[1]; + cc[2] = EmbeddedSignalling[2]; + cc[3] = EmbeddedSignalling[3]; + + /* The color code is normally the same for all + * 6 received voice frame, so in case of error + * do not store it except if the last SYNC frame + * has been received. */ + if(EmbeddedSignallingOk || ((state->color_code_ok == 0) && (j == 5))) + { + /* Save the color code */ + state->color_code = (unsigned int)((cc[0] << 3) + (cc[1] << 2) + (cc[2] << 1) + cc[3]); + state->color_code_ok = EmbeddedSignallingOk; + } + + /* Save the Power Control (PI) indicator */ + state->PI = (unsigned int)EmbeddedSignalling[4]; + state->PI_ok = EmbeddedSignallingOk; + + /* Save the Link Control Start Stop (LCSS) indicator */ + state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]); + state->LCSS_ok = EmbeddedSignallingOk; + + if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0)) + { + mutecurrentslot = 1; + state->directmode = 0; + if(state->currentslot == 0) + { + sprintf(state->slot1light, "[slot1]"); + } + else + { + sprintf(state->slot2light, "[slot2]"); + } + } + else if(strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0) + { + mutecurrentslot = 1; + state->currentslot = 0; + state->directmode = 1; /* Direct mode */ + sprintf(state->slot1light, "[sLoT1]"); + } + else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0) + { + mutecurrentslot = 1; + state->currentslot = 1; + state->directmode = 1; /* Direct mode */ + sprintf(state->slot1light, "[sLoT2]"); + } + else if((strcmp (sync, DMR_BS_VOICE_SYNC) == 0) || (strcmp (sync, DMR_MS_VOICE_SYNC) == 0)) + { + mutecurrentslot = 0; + state->directmode = 0; + if(state->currentslot == 0) + { + sprintf(state->slot1light, "[SLOT1]"); + } + else + { + sprintf(state->slot2light, "[SLOT2]"); + } + } + else if(strcmp (sync, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0) + { + mutecurrentslot = 0; + state->currentslot = 0; + state->directmode = 1; /* Direct mode */ + sprintf(state->slot1light, "[sLoT1]"); + } + else if(strcmp (sync, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0) + { + mutecurrentslot = 0; + state->currentslot = 1; + state->directmode = 1; /* Direct mode */ + sprintf(state->slot2light, "[sLoT2]"); + } + + if((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0)) + { + msMode = 1; + } + +// TODO : To be removed +// if((j == 0) && (opts->errorbars == 1)) +// { +// fprintf(stderr, "%s %s VOICE e:", state->slot1light, state->slot2light); +// } #ifdef DMR_DUMP - k = 0; - for (i = 0; i < 24; i++) - { - dibit = syncdata[i]; - syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 - k++; - syncbits[k] = (1 & dibit) + 48; // bit 0 - k++; - } - syncbits[48] = 0; - fprintf (stderr, "%s ", syncbits); + k = 0; + for(i = 0; i < 24; i++) + { + dibit = syncdata[i]; + syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 + k++; + syncbits[k] = (1 & dibit) + 48; // bit 0 + k++; + } + syncbits[48] = 0; + fprintf(stderr, "%s ", syncbits); #endif - // current slot frame 2 second half - for (i = 0; i < 18; i++) - { - dibit = getDibit (opts, state); - ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1 - ambe_fr2[*y][*z] = (1 & dibit); // bit 0 - w++; - x++; - y++; - z++; - } + // current slot frame 2 second half + for(i = 0; i < 18; i++) + { + dibit = getDibit(opts, state); + ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr2[*y][*z] = (1 & dibit); // bit 0 - if (mutecurrentslot == 0) - { - if (state->firstframe == 1) - { // we don't know if anything received before the first sync after no carrier is valid - state->firstframe = 0; - } - else - { - processMbeFrame (opts, state, NULL, ambe_fr, NULL); - processMbeFrame (opts, state, NULL, ambe_fr2, NULL); - } - } + /* Save the current frame in the appropriate superframe buffer */ + if(state->currentslot == 0) + { + /* Time slot 1 superframe buffer filling => RAW voice bit */ + state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1 + state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0 - // current slot frame 3 - w = rW; - x = rX; - y = rY; - z = rZ; - for (i = 0; i < 36; i++) - { - dibit = getDibit (opts, state); - ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1 - ambe_fr3[*y][*z] = (1 & dibit); // bit 0 - w++; - x++; - y++; - z++; - } - if (mutecurrentslot == 0) - { - processMbeFrame (opts, state, NULL, ambe_fr3, NULL); - } + /* Time slot 1 superframe buffer filling => Deinterleaved voice bit */ + state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1 + state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0 + } + else + { + /* Time slot 2 superframe buffer filling */ + state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1 + state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0 - // CACH - for (i = 0; i < 12; i++) - { - dibit = getDibit (opts, state); - cachdata[i] = dibit; - } - cachdata[12] = 0; + /* Time slot 2 superframe buffer filling => Deinterleaved voice bit */ + state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1 + state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0 + } + + w++; + x++; + y++; + z++; + } + + if(mutecurrentslot == 0) + { + if(state->firstframe == 1) + { // we don't know if anything received before the first sync after no carrier is valid + state->firstframe = 0; + } + else + { + //processMbeFrame (opts, state, NULL, ambe_fr, NULL); + //processMbeFrame (opts, state, NULL, ambe_fr2, NULL); + } + } + + // current slot frame 3 + w = rW; + x = rX; + y = rY; + z = rZ; + k = 0; + for(i = 0; i < 36; i++) + { + dibit = getDibit(opts, state); + ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr3[*y][*z] = (1 & dibit); // bit 0 + + /* Save the current frame in the appropriate superframe buffer */ + if(state->currentslot == 0) + { + /* Time slot 1 superframe buffer filling => RAW voice bit */ + state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & (dibit >> 1)); // bit 1 + state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & dibit); // bit 0 + + /* Time slot 1 superframe buffer filling => Deinterleaved voice bit */ + state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*w][*x] = (1 & (dibit >> 1)); // bit 1 + state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*y][*z] = (1 & dibit); // bit 0 + } + else + { + /* Time slot 2 superframe buffer filling => RAW voice bit */ + state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & (dibit >> 1)); // bit 1 + state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & dibit); // bit 0 + + /* Time slot 2 superframe buffer filling => Deinterleaved voice bit */ + state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*w][*x] = (1 & (dibit >> 1)); // bit 1 + state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*y][*z] = (1 & dibit); // bit 0 + } + + w++; + x++; + y++; + z++; + } + if(mutecurrentslot == 0) + { + //processMbeFrame (opts, state, NULL, ambe_fr3, NULL); + } + + // CACH + for(i = 0; i < 12; i++) + { + dibit = getDibit(opts, state); + cachdata[i] = dibit; + } + cachdata[12] = 0; #ifdef DMR_DUMP - k = 0; - for (i = 0; i < 12; i++) - { - dibit = cachdata[i]; - cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 - k++; - cachbits[k] = (1 & dibit) + 48; // bit 0 - k++; - } - cachbits[24] = 0; - fprintf (stderr, "%s ", cachbits); + k = 0; + for(i = 0; i < 12; i++) + { + dibit = cachdata[i]; + cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 + k++; + cachbits[k] = (1 & dibit) + 48; // bit 0 + k++; + } + cachbits[24] = 0; + fprintf(stderr, "%s ", cachbits); #endif - // next slot + // next slot + skipDibit (opts, state, 54); + + // signaling data or sync + for(i = 0; i < 24; i++) + { + dibit = getDibit(opts, state); + syncdata[i] = dibit; + sync[i] = (dibit | 1) + 48; + } + sync[24] = 0; + syncdata[24] = 0; + + if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (msMode == 1)) + { + state->directmode = 0; + if(state->currentslot == 0) + { + sprintf(state->slot2light, " slot2 "); + } + else + { + sprintf(state->slot1light, " slot1 "); + } + } + else if(strcmp (sync, DMR_BS_VOICE_SYNC) == 0) + { + state->directmode = 0; + if(state->currentslot == 0) + { + sprintf(state->slot2light, " SLOT2 "); + } + else + { + sprintf(state->slot1light, " SLOT1 "); + } + } + else if((strcmp (sync, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0) || (strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)) + { + state->currentslot = 0; + state->directmode = 1; /* Direct mode */ + sprintf(state->slot1light, " sLoT1 "); + } + else if((strcmp (sync, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0) || (strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)) + { + state->currentslot = 1; + state->directmode = 1; /* Direct mode */ + sprintf(state->slot1light, " sLoT2 "); + } + +#ifdef DMR_DUMP + k = 0; + for(i = 0; i < 24; i++) + { + dibit = syncdata[i]; + syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 + k++; + syncbits[k] = (1 & dibit) + 48; // bit 0 + k++; + } + syncbits[48] = 0; + fprintf(stderr, "%s ", syncbits); +#endif + + if(j == 5) + { + // 2nd half next slot skipDibit (opts, state, 54); - // signaling data or sync - for (i = 0; i < 24; i++) - { - dibit = getDibit (opts, state); - syncdata[i] = dibit; - sync[i] = (dibit | 1) + 48; - } - sync[24] = 0; - syncdata[24] = 0; + // CACH + skipDibit (opts, state, 12); - if ((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (msMode == 1)) - { - if (state->currentslot == 0) - { - sprintf (state->slot1light, " slot1 "); - } - else - { - sprintf (state->slot0light, " slot0 "); - } - } - else if (strcmp (sync, DMR_BS_VOICE_SYNC) == 0) - { - if (state->currentslot == 0) - { - sprintf (state->slot1light, " SLOT1 "); - } - else - { - sprintf (state->slot0light, " SLOT0 "); - } - } - -#ifdef DMR_DUMP - k = 0; - for (i = 0; i < 24; i++) - { - dibit = syncdata[i]; - syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1 - k++; - syncbits[k] = (1 & dibit) + 48; // bit 0 - k++; - } - syncbits[48] = 0; - fprintf (stderr, "%s ", syncbits); -#endif - - if (j == 5) - { - // 2nd half next slot - skipDibit (opts, state, 54); - - // CACH - skipDibit (opts, state, 12); - - // first half current slot - skipDibit (opts, state, 54); - } + // first half current slot + skipDibit (opts, state, 54); } - if (opts->errorbars == 1) + /* Extract the 49 bits AMBE of the voice frame */ + if(state->currentslot == 0) { - fprintf (stderr, "\n"); + /* 3 x 49 bit of AMBE sample per voice frame => Slot 1buffer here */ + for(i = 0; i < 3; i++) + { + state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i] = mbe_eccAmbe3600x2450C0(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]); + //state->errs = state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i]; //correct placement + state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] = state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i]; + //state->errs2 = state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i]; //correct placement + mbe_demodulateAmbe3600x2450Data(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]); + state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] += mbe_eccAmbe3600x2450Data(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i], + &(state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].AmbeBit[i][0])); + } + //state->errs = state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i]; //correct placement + //state->errs2 = (int)state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i]; //correct placement } + else + { + /* 3 x 49 bit of AMBE sample per voice frame => Slot 2 buffer here */ + for(i = 0; i < 3; i++) + { + state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i] = mbe_eccAmbe3600x2450C0(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]); + //state->errs = state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i]; //correct placement + state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] = state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i]; + //state->errs2 = state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i]; //correct placement + mbe_demodulateAmbe3600x2450Data(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]); + state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] += mbe_eccAmbe3600x2450Data(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i], + &(state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].AmbeBit[i][0])); + } + //state->errs = state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i]; //correct placement + //state->errs2 = (int)state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i]; //correct placement + } + } /* End for(j = 0; j < 6; j++) */ -} + if(opts->errorbars == 1) + { + fprintf(stderr, "%s %s ", state->slot1light, state->slot2light); + + /* Print the color code */ + fprintf(stderr, "| Color Code=%02d ", (int)state->color_code); + + if(state->color_code_ok) fprintf(stderr, "(OK) |"); + else fprintf(stderr, "(CRC ERR) |"); + + fprintf(stderr, " VOICE e:"); + } + + /* Perform the SYNC DMR data embedded decoding */ + ProcessVoiceBurstSync(opts, state); + + /* Perform the DMR encryption decoding */ + ProcessDMREncryption(opts, state); + + /* Print DMR frame (if needed) */ + //DMRVoiceFrameProcess(opts, state); //HERE HERE, was enabled, disabled so I could build + + if(opts->errorbars == 1) + { + fprintf(stderr, "\n"); + } +} /* End processDMRvoice() */ diff --git a/src/dpmr_data.c b/src/dpmr_data.c new file mode 100644 index 0000000..a356ea9 --- /dev/null +++ b/src/dpmr_data.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2010 DSD Author + * GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0) + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "dsd.h" + + +/* Convert the Channel code (24 bit) into a valid + * dPMR color code [0..63] + * Return -1 on error */ +int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]) +{ + uint32_t ChannelCodeByte; + int32_t ColorCode; + uint32_t i; + + /* Reconstitute the 24 bit channel code into a 3 bytes integer */ + ChannelCodeByte = 0; + for(i = 0; i < 24; i++) + { + ChannelCodeByte <<= 1; + ChannelCodeByte |= (ChannelCodeBit[i] & 1); + } + + /* By analyzing all channel code on the + * dPMR standard, I observe that if we decompose the + * 24 bit into 12 dibit, all dibit's LSB are always + * equal to "1", so apply a mask to correct + * flipped LSB dibit. + * + * For more details see ETSI TS 102 658 chapter + * 6.1.5.2.2 "Channel Code Determined by Frequency + * and System Identity Code". + * + * Apply the mask 0x555555 + * 0x555555 = 0b010101010101010101010101 => All dibit's LSB + * are equal to "1". + */ + ChannelCodeByte |= 0x555555; + + + /* Switch to the right color code */ + switch(ChannelCodeByte & 0xFF0000) + { + case 0x570000: + { + switch(ChannelCodeByte) + { + case 0x575F77: {ColorCode = 0; break;} + case 0x577577: {ColorCode = 1; break;} + case 0x57DD75: {ColorCode = 2; break;} + case 0x57F775: {ColorCode = 3; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0x570000 */ + + case 0x550000: + { + switch(ChannelCodeByte) + { + case 0x55577D: {ColorCode = 4; break;} + case 0x557D7D: {ColorCode = 5; break;} + case 0x55D57F: {ColorCode = 6; break;} + case 0x55FF7F: {ColorCode = 7; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0x550000 */ + + case 0x5F0000: + { + switch(ChannelCodeByte) + { + case 0x5F555F: {ColorCode = 8; break;} + case 0x5F7F5F: {ColorCode = 9; break;} + case 0x5FD75D: {ColorCode = 10; break;} + case 0x5FFD5D: {ColorCode = 11; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0x5F0000 */ + + case 0x5D0000: + { + switch(ChannelCodeByte) + { + case 0x5D5D55: {ColorCode = 12; break;} + case 0x5D7755: {ColorCode = 13; break;} + case 0x5DDF57: {ColorCode = 14; break;} + case 0x5DF557: {ColorCode = 15; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0x5D0000 */ + + case 0x770000: + { + switch(ChannelCodeByte) + { + case 0x775DD7: {ColorCode = 16; break;} + case 0x7777D7: {ColorCode = 17; break;} + case 0x77DFD5: {ColorCode = 18; break;} + case 0x77F5D5: {ColorCode = 19; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0x770000 */ + + case 0x750000: + { + switch(ChannelCodeByte) + { + case 0x7555DD: {ColorCode = 20; break;} + case 0x757FDD: {ColorCode = 21; break;} + case 0x75D7DF: {ColorCode = 22; break;} + case 0x75FDDF: {ColorCode = 23; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0x750000 */ + + case 0x7F0000: + { + switch(ChannelCodeByte) + { + case 0x7F57FF: {ColorCode = 24; break;} + case 0x7F7DFF: {ColorCode = 25; break;} + case 0x7FD5FD: {ColorCode = 26; break;} + case 0x7FFFFD: {ColorCode = 27; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0x7F0000 */ + + case 0x7D0000: + { + switch(ChannelCodeByte) + { + case 0x7D5FF5: {ColorCode = 28; break;} + case 0x7D75F5: {ColorCode = 29; break;} + case 0x7DDDF7: {ColorCode = 30; break;} + case 0x7DF7F7: {ColorCode = 31; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0x7D0000 */ + + case 0xD70000: + { + switch(ChannelCodeByte) + { + case 0xD755F7: {ColorCode = 32; break;} + case 0xD77FF7: {ColorCode = 33; break;} + case 0xD7D7F5: {ColorCode = 34; break;} + case 0xD7FDF5: {ColorCode = 35; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0xD70000 */ + + case 0xD50000: + { + switch(ChannelCodeByte) + { + case 0xD55DFD: {ColorCode = 36; break;} + case 0xD577FD: {ColorCode = 37; break;} + case 0xD5DFFF: {ColorCode = 38; break;} + case 0xD5F5FF: {ColorCode = 39; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0xD50000 */ + + case 0xDF0000: + { + switch(ChannelCodeByte) + { + case 0xDF5FDF: {ColorCode = 40; break;} + case 0xDF75DF: {ColorCode = 41; break;} + case 0xDFDDDD: {ColorCode = 42; break;} + case 0xDFF7DD: {ColorCode = 43; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0xDF0000 */ + + case 0xDD0000: + { + switch(ChannelCodeByte) + { + case 0xDD57D5: {ColorCode = 44; break;} + case 0xDD7DD5: {ColorCode = 45; break;} + case 0xDDD5D7: {ColorCode = 46; break;} + case 0xDDFFD7: {ColorCode = 47; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0xDD0000 */ + + case 0xF70000: + { + switch(ChannelCodeByte) + { + case 0xF75757: {ColorCode = 48; break;} + case 0xF77D57: {ColorCode = 49; break;} + case 0xF7D555: {ColorCode = 50; break;} + case 0xF7FF55: {ColorCode = 51; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0xF70000 */ + + case 0xF50000: + { + switch(ChannelCodeByte) + { + case 0xF55F5D: {ColorCode = 52; break;} + case 0xF5755D: {ColorCode = 53; break;} + case 0xF5DD5F: {ColorCode = 54; break;} + case 0xF5F75F: {ColorCode = 55; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0xF50000 */ + + case 0xFF0000: + { + switch(ChannelCodeByte) + { + case 0xFF5D7F: {ColorCode = 56; break;} + case 0xFF777F: {ColorCode = 57; break;} + case 0xFFDF7D: {ColorCode = 58; break;} + case 0xFFF57D: {ColorCode = 59; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0xFF0000 */ + + case 0xFD0000: + { + switch(ChannelCodeByte) + { + case 0xFD5575: {ColorCode = 60; break;} + case 0xFD7F75: {ColorCode = 61; break;} + case 0xFDD777: {ColorCode = 62; break;} + case 0xFDFD77: {ColorCode = 63; break;} + default: {ColorCode = -1; break;} + } /* End switch(ChannelCodeByte) */ + break; + } /* End case 0xFD0000 */ + + default: + { + ColorCode = -1; + break; + } + } /* End switch(ChannelCodeByte & 0xFF0000) */ + + /* Return the colour code */ + return ColorCode; +} + + +/* End of file */ diff --git a/src/dpmr_voice.c b/src/dpmr_voice.c new file mode 100644 index 0000000..c7e4ae8 --- /dev/null +++ b/src/dpmr_voice.c @@ -0,0 +1,919 @@ +/* + * Copyright (C) 2010 DSD Author + * GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0) + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "dsd.h" +#include "dpmr_const.h" + +/* Uncomment this define to display the dPMR debug info */ +//#define dPMR_PRINT_DEBUG_INFO + + +void processdPMRvoice (dsd_opts * opts, dsd_state * state) +{ + // extracts AMBE frames from DMR frame + uint32_t i, j, k, dibit; + uint8_t CCH[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0}; + uint8_t CCHDescrambled[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0}; + uint8_t CCHDeInterleaved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0}; + uint8_t CCHDataHammingCorrected[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][48] = {0}; + uint8_t CCHDataCRC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0}; + uint8_t CCHDataCRCComputed[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0}; + //uint8_t TCH[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][72] = {0}; + uint8_t CC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE / 2][24] = {0}; + char ambe_fr[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][4][24] = {0}; + char ambe_fr2[4][24]; + const int *w, *x, *y, *z; + uint32_t ScramblerLFSR = 0; + bool correctable = true; + bool HammingCorrectable[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][6] = {0}; + uint32_t CrcOk[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0}; + uint32_t HammingOk[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0}; + uint32_t Temp = 0; + /* CCH (Control CHannel) data */ + uint32_t CCH_FrameNumber[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0}; + uint32_t CCH_CalledID = 0; + uint32_t CCH_CallingID = 0; + uint32_t CCH_CommunicationMode[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0}; + uint32_t CCH_Version[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + uint32_t CCH_CommsFormat[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + uint32_t CCH_EmergencyPriority[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + uint32_t CCH_Reserved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + uint32_t CCH_SlowData[NB_OF_DPMR_VOICE_FRAME_TO_DECODE]; + uint32_t *errs; + uint32_t *errs2; + uint8_t AmbeBitDescrambled[49] = {0}; + uint32_t ScramblerKey = 0; + uint32_t PartOfSuperFrame = 0; + uint32_t VoiceFrameFlag = 1; /* We consider the current frame as a voice frame */ + uint32_t AttachedDataFlag = 0; + uint8_t CalledID[8] = {0}; + uint8_t CallingID[8] = {0}; + + /* Remove compiler warning */ + //UNUSED_VARIABLE(AmbeBitDescrambled[0]); + //UNUSED_VARIABLE(ScramblerKey); + //UNUSED_VARIABLE(PartOfSuperFrame); + + if (opts->errorbars == 1) + { + fprintf(stderr, "VOICE e:"); + } + + /* First CCH (Control CHannel) - 72 bit */ + k = 0; + for (i = 0; i < 36; i++) + { + dibit = getDibit (opts, state); + + if (opts->inverted_dpmr == 1) + { + dibit = (dibit ^ 2); + } + + CCH[0][(i * 2)] = (1 & (dibit >> 1)); // bit 1 + CCH[0][(i * 2) + 1] = (1 & dibit); // bit 0 + +#ifdef DPMR_DUMP + fprintf(stderr, "%c", dibit + 48); +#endif + } +#ifdef DPMR_DUMP + fprintf(stderr, " "); +#endif + + /* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */ + k = 0; + for (j = 0; j < 4; j++) + { + w = dPmrW; + x = dPmrX; + y = dPmrY; + z = dPmrZ; + k = 0; + for (i = 0; i < 36; i++) + { + dibit = getDibit (opts, state); + + if (opts->inverted_dpmr == 1) + { + dibit = (dibit ^ 2); + } + +#ifdef DPMR_DUMP + fprintf(stderr, "%c", dibit + 48); +#endif + ambe_fr[j][*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr[j][*y][*z] = (1 & dibit); // bit 0 + ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr2[*y][*z] = (1 & dibit); // bit 0 + //TCH[j][k] = (1 & (dibit >> 1)); // bit 1 + //TCH[j][k + 1] = (1 & dibit); // bit 0 + state->dPMRVoiceFS2Frame.RawVoiceBit[j][k] = (1 & (dibit >> 1)); // bit 1 + state->dPMRVoiceFS2Frame.RawVoiceBit[j][k + 1] = (1 & dibit); // bit 0 + k = k + 2; + w++; + x++; + y++; + z++; + } + //processMbeFrame (opts, state, NULL, ambe_fr[j], NULL); //HERE HERE +#ifdef DPMR_DUMP + fprintf(stderr, " "); +#endif + } + + /* First CC (Channel Code) - 24 bit */ + k = 0; + for (i = 0; i < 12; i++) + { + dibit = getDibit (opts, state); + + if (opts->inverted_dpmr == 1) + { + dibit = (dibit ^ 2); + } + + CC[0][k++] = (1 & (dibit >> 1)); // bit 1 + CC[0][k++] = (1 & dibit); // bit 0 + +#ifdef DPMR_DUMP + fprintf(stderr, "%c", dibit + 48); +#endif + } +#ifdef DPMR_DUMP + fprintf(stderr, " "); +#endif + + /* Get the color code */ + state->dPMRVoiceFS2Frame.ColorCode[0] = (unsigned int)GetdPmrColorCode(CC[0]); + + /* Second CCH (Control CHannel) - 72 bit */ + k = 0; + for (i = 0; i < 36; i++) + { + dibit = getDibit (opts, state); + + if (opts->inverted_dpmr == 1) + { + dibit = (dibit ^ 2); + } + + CCH[1][k++] = (1 & (dibit >> 1)); // bit 1 + CCH[1][k++] = (1 & dibit); // bit 0 + +#ifdef DPMR_DUMP + fprintf(stderr, "%c", dibit + 48); +#endif + } + +#ifdef DPMR_DUMP + fprintf(stderr, " "); +#endif + + /* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */ + k = 0; + for (j = 0; j < 4; j++) + { + w = dPmrW; + x = dPmrX; + y = dPmrY; + z = dPmrZ; + k = 0; + for (i = 0; i < 36; i++) + { + dibit = getDibit (opts, state); + + if (opts->inverted_dpmr == 1) + { + dibit = (dibit ^ 2); + } + +#ifdef DPMR_DUMP + fprintf(stderr, "%c", dibit + 48); +#endif + ambe_fr[j + 4][*w][*x] = (1 & (dibit >> 1)); // bit 1 + ambe_fr[j + 4][*y][*z] = (1 & dibit); // bit 0 + //TCH[j + 4][k] = (1 & (dibit >> 1)); // bit 1 + //TCH[j + 4][k + 1] = (1 & dibit); // bit 0 + state->dPMRVoiceFS2Frame.RawVoiceBit[j + 4][k] = (1 & (dibit >> 1)); // bit 1 + state->dPMRVoiceFS2Frame.RawVoiceBit[j + 4][k + 1] = (1 & dibit); // bit 0 + k = k + 2; + w++; + x++; + y++; + z++; + } + +#ifdef DPMR_DUMP + fprintf(stderr, " "); +#endif + } + + +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "\n"); +#endif /* dPMR_PRINT_DEBUG_INFO */ + + /* Initialize the Hamming (12,8) error code detection + * Not needed because it has already been done + * in the "InitAllFecFunction()" function */ + //Hamming_12_8_init(); + + /* Decoding all CCH (Control CHannel) */ + for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++) + { + /* Init LFSR value - All bit to '1' */ + ScramblerLFSR = 0x1FF; + + /* Descramble data - Input 72 bit => Output 72 bit */ + ScrambledPMRBit(&ScramblerLFSR, CCH[i], CCHDescrambled[i], 72); + + /* Deinterleave data - Input 72 bit => Output 72 bit */ + DeInterleave6x12DPmrBit(CCHDescrambled[i], CCHDeInterleaved[i]); + + correctable = true; + for(j = 0; j < 6; j++) + { + /* Apply the Hamming(12,8) correction - Input 72 bit => Output 48 bit */ + HammingCorrectable[i][j] = Hamming_12_8_decode(&CCHDeInterleaved[i][j*12], &CCHDataHammingCorrected[i][j*8], 1); + if(HammingCorrectable[i][j] == false) correctable = false; + } + + if(correctable) + { +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "CCH data corrected successfully\n"); +#endif /* dPMR_PRINT_DEBUG_INFO */ + + HammingOk[i] = 1; + } + else + { +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "ERROR !!! CCH data contains uncorrectable bit\n"); +#endif /* dPMR_PRINT_DEBUG_INFO */ + + HammingOk[i] = 0; + } + + /* Reconstitute the 7 bit CRC */ + CCHDataCRC[i] = 0; + CCHDataCRC[i] |= (CCHDataHammingCorrected[i][41]) << 6; + CCHDataCRC[i] |= (CCHDataHammingCorrected[i][42]) << 5; + CCHDataCRC[i] |= (CCHDataHammingCorrected[i][43]) << 4; + CCHDataCRC[i] |= (CCHDataHammingCorrected[i][44]) << 3; + CCHDataCRC[i] |= (CCHDataHammingCorrected[i][45]) << 2; + CCHDataCRC[i] |= (CCHDataHammingCorrected[i][46]) << 1; + CCHDataCRC[i] |= (CCHDataHammingCorrected[i][47]) << 0; + + /* Compute the 7 bit CRC */ + CCHDataCRCComputed[i] = CRC7BitdPMR(CCHDataHammingCorrected[i], 41); + + if(CCHDataCRC[i] == CCHDataCRCComputed[i]) + { +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "Good CCH CRCs ! CRC received = 0x%02X, CRC computed = 0x%02X\n", + CCHDataCRC[i], CCHDataCRCComputed[i]); +#endif /* dPMR_PRINT_DEBUG_INFO */ + + CrcOk[i] = 1; + } + else + { +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "ERROR !!! CCH CRCs are different ! CRC received = 0x%02X, CRC computed = 0x%02X\n", + CCHDataCRC[i], CCHDataCRCComputed[i]); +#endif /* dPMR_PRINT_DEBUG_INFO */ + + CrcOk[i] = 0; + } + + /* Fill the frame number */ + CCH_FrameNumber[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][0], 2); + + /* Fill the communication mode */ + CCH_CommunicationMode[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][14], 3); + + /* Fill the version */ + CCH_Version[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][17], 2); + + /* Fill the comm format */ + CCH_CommsFormat[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][19], 2); + + /* Fill the emergency priority bit */ + CCH_EmergencyPriority[i] = (uint32_t)CCHDataHammingCorrected[i][21]; + + /* Fill the reserved bit */ + CCH_Reserved[i] = (uint32_t)CCHDataHammingCorrected[i][22]; + + /* Fill the slow data */ + CCH_SlowData[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][23], 18); + + /* Copy the CCH data into the structure */ + memcpy(state->dPMRVoiceFS2Frame.CCHData[i], CCHDataHammingCorrected[i], 48); + state->dPMRVoiceFS2Frame.CCHDataHammingOk[i] = HammingOk[i]; + state->dPMRVoiceFS2Frame.CCHDataCRC[i] = CCHDataCRC[i]; + state->dPMRVoiceFS2Frame.CCHDataCrcOk[i] = CrcOk[i]; + state->dPMRVoiceFS2Frame.FrameNumbering[i] = CCH_FrameNumber[i]; + state->dPMRVoiceFS2Frame.CommunicationMode[i] = CCH_CommunicationMode[i]; + state->dPMRVoiceFS2Frame.Version[i] = CCH_Version[i]; + state->dPMRVoiceFS2Frame.CommsFormat[i] = CCH_CommsFormat[i]; + state->dPMRVoiceFS2Frame.EmergencyPriority[i] = CCH_EmergencyPriority[i]; + state->dPMRVoiceFS2Frame.Reserved[i] = CCH_Reserved[i]; + state->dPMRVoiceFS2Frame.SlowData[i] = CCH_SlowData[i]; + } /* End for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++) */ + + /* Get the last TG */ + strcpy((char *)CalledID, (char *)state->dPMRVoiceFS2Frame.CalledID); + CalledID[7] = '\0'; + + /* Get the last source ID */ + strcpy((char *)CallingID, (char *)state->dPMRVoiceFS2Frame.CallingID); + CallingID[7] = '\0'; + +#ifdef dPMR_PRINT_DEBUG_INFO + /* Print Hamming debug info */ + fprintf(stderr, " | "); + for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++) + { + fprintf(stderr, "Hamming[%u]=", i); + for(j = 0; j < 6; j++) + { + if(HammingCorrectable[i][j]) fprintf(stderr, "1"); + else fprintf(stderr, "0"); + } + fprintf(stderr, " HammingOk[%u]=%u", i, HammingOk[i]); + fprintf(stderr, " CrcOk[%u]=%u", i, CrcOk[i]); + //fprintf(stderr, " Bit[%u]=", i); + //for(j = 0; j < 7; j++) + //{ + // if(CCHDataHammingCorrected[i][j + 41]) fprintf(stderr, "1"); + // else fprintf(stderr, "0"); + //} + fprintf(stderr, " CRCrec=0x%02X CRCcomp=0x%02X", CCHDataCRC[i], CCHDataCRCComputed[i]); + if(i < (NB_OF_DPMR_VOICE_FRAME_TO_DECODE - 1)) fprintf(stderr, " - "); + } +#endif /* dPMR_PRINT_DEBUG_INFO */ + + +#if (NB_OF_DPMR_VOICE_FRAME_TO_DECODE == 2) + /* To determine the part of the payload frame, we need to check the + * Hamming code result, the CRC and the frame number */ +// if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 0)) || +// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 1))) + if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 0)) || + ((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 1))) + { + /* First part of the super frame */ + PartOfSuperFrame = 1; + +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "First part of the Super Frame\n"); +#endif /* dPMR_PRINT_DEBUG_INFO */ + + /* The next part will normally be the second part */ + opts->dPMR_next_part_of_superframe = 2; + + /* Fill the called ID (talk group - TG) */ + Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[0][2], 12); + CCH_CalledID = ((Temp << 12) & 0x00FFF000); /* 12 MSBit */ + Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[1][2], 12); + CCH_CalledID |= (Temp & 0x00000FFF); /* 12 LSBit */ + ConvertAirInterfaceID(CCH_CalledID, CalledID); + CalledID[7] = '\0'; + + /* Determine if the called TG ID only is correct */ + //if(HammingOk[0] && CrcOk[0] && HammingOk[1] && CrcOk[1]) + if((CrcOk[0] || (HammingCorrectable[0][0] && HammingCorrectable[0][1])) && + (CrcOk[1] || (HammingCorrectable[1][0] && HammingCorrectable[1][1]))) + { + /* Save CCH data parameters */ + state->dPMRVoiceFS2Frame.CalledIDOk = 1; + } + else + { + state->dPMRVoiceFS2Frame.CalledIDOk = 0; + } + + /* BUGFIX : Copy in all case the TG ID */ + strcpy((char *)state->dPMRVoiceFS2Frame.CalledID, (char *)CalledID); + } + /* To determine the part of the payload frame, we need to check the + * Hamming code result, the CRC and the frame number */ +// else if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 2)) || +// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 3))) + else if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 2)) || + ((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 3))) + { + /* Second part of the super frame */ + PartOfSuperFrame = 2; + +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "Second part of the Super Frame\n"); +#endif /* dPMR_PRINT_DEBUG_INFO */ + + /* The next part will normally be the first part */ + opts->dPMR_next_part_of_superframe = 1; + + /* Fill the calling ID (source) */ + Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[0][2], 12); + CCH_CallingID = ((Temp << 12) & 0x00FFF000); /* 12 MSBit */ + Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[1][2], 12); + CCH_CallingID |= (Temp & 0x00000FFF); /* 12 LSBit */ + ConvertAirInterfaceID(CCH_CallingID, CallingID); + CallingID[7] = '\0'; + + /* Determine if the calling SRC ID is correct */ + //if(HammingOk[0] && CrcOk[0] && HammingOk[1] && CrcOk[1]) + if((CrcOk[0] || (HammingCorrectable[0][0] && HammingCorrectable[0][1])) && + (CrcOk[1] || (HammingCorrectable[1][0] && HammingCorrectable[1][1]))) + { + /* Save CCH data parameters */ + state->dPMRVoiceFS2Frame.CallingIDOk = 1; + } + else + { + state->dPMRVoiceFS2Frame.CallingIDOk = 0; + } + + /* BUGFIX : Copy in all case the SRC ID */ + strcpy((char *)state->dPMRVoiceFS2Frame.CallingID, (char *)CallingID); + } + else + { + /* The dPMR source and destination ID are now invalid */ + state->dPMRVoiceFS2Frame.CalledIDOk = 0; + state->dPMRVoiceFS2Frame.CallingIDOk = 0; + + /* Unknown part of superframe => We suppose + * it is the next part of the previous frame received */ + PartOfSuperFrame = opts->dPMR_next_part_of_superframe; + + /* Set the next part of the superframe to receive */ + if(opts->dPMR_next_part_of_superframe == 1) + { + +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "Supposed first part of the Super Frame\n"); +#endif /* dPMR_PRINT_DEBUG_INFO */ + + /* Set the next part as the second part */ + opts->dPMR_next_part_of_superframe = 2; + } + else if(opts->dPMR_next_part_of_superframe == 2) + { + +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "Supposed second part of the Super Frame\n"); +#endif /* dPMR_PRINT_DEBUG_INFO */ + + /* Set the next part as the first part */ + opts->dPMR_next_part_of_superframe = 1; + } + else + { + +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "Unknown part of the Super Frame\n"); +#endif /* dPMR_PRINT_DEBUG_INFO */ + + /* Unknown current part, set to 0 */ + opts->dPMR_next_part_of_superframe = 0; + } + } + + if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1)) + { + /* Display the color code */ + //fprintf(stderr, " | Color Code=%02d ", (int)state->dPMRVoiceFS2Frame.ColorCode[0]); + } + else + { + /* Unknown color code, display "??" */ + //fprintf(stderr, " | Color Code=?? "); + } + + /* Display the destination ID (talk group - TG) */ + //fprintf(stderr, "| TG=%s", CalledID); + if(state->dPMRVoiceFS2Frame.CalledIDOk) + { + fprintf(stderr, "| TG=%s", CalledID); + //strcpy (state->dpmr_target_id, CalledID); //HERE HERE + state->dpmr_target_id = CalledID; + //fprintf(stderr, " (CRC OK) "); + if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1)) + { + fprintf(stderr, " | Color Code=%02d ", (int)state->dPMRVoiceFS2Frame.ColorCode[0]); + state->dpmr_color_code = (int)state->dPMRVoiceFS2Frame.ColorCode[0]; + + } + + } + else + { + //fprintf(stderr, " (CRC ERR) "); + } + + /* Display the source ID */ + //fprintf(stderr, "| Src=%s", CallingID); + if(state->dPMRVoiceFS2Frame.CallingIDOk) + { + fprintf(stderr, "| Src=%s", CallingID); + //strcpy (state->dpmr_caller_id, CallingID); + state->dpmr_caller_id = CallingID; + + //fprintf(stderr, " (CRC OK) "); + } + else + { + fprintf(stderr, " (CRC ERR) "); + } + +#endif /* (NB_OF_DPMR_VOICE_FRAME_TO_DECODE == 2) */ + + /* Test if the current frame is encrypted or not */ + for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++) + { + //if(HammingOk[i] && CrcOk[i]) + if(HammingCorrectable[i][2] && CrcOk[i]) + { + /* Unencrypted voice frame => Version = 0 (0b00) */ + if(state->dPMRVoiceFS2Frame.Version[i] == 0x00) + { + opts->dPMR_curr_frame_is_encrypted = 0; + } + /* Encrypted voice frame => Version = 3 (0b11) */ + else if(state->dPMRVoiceFS2Frame.Version[i] == 0x03) + { + opts->dPMR_curr_frame_is_encrypted = 1; + } + else + { + /* Unknown clear or encrypted voice frame status */ + } + + /* Valid encryption parameter found, + * exit the "for" loop */ + break; + } /* End if(HammingCorrectable[i][2] && CrcOk[i]) */ + } + + if(opts->dPMR_curr_frame_is_encrypted) fprintf(stderr, "| Scrambler "); + + /* Determine if the received frame is a voice frame, a voice frame + data + * or a data frame. + * + * See ETSI TS 102 658 chapter 5.0.5 "Communication Mode [BT]" + * for details + */ + VoiceFrameFlag = 0; + AttachedDataFlag = 0; + for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++) + { + /* Search for voice frame + * Note : Due to error decoding, we parse all CCH + * Communication Mode parameters, if a voice parameter is + * found, the entire payload is considered as voice */ + if((state->dPMRVoiceFS2Frame.CommunicationMode[i] == 0) || + (state->dPMRVoiceFS2Frame.CommunicationMode[i] == 1) || + (state->dPMRVoiceFS2Frame.CommunicationMode[i] == 5)) + { + /* Voice frame detected */ + VoiceFrameFlag = 1; + + /* Check if the voice frame contains attached data */ + if((state->dPMRVoiceFS2Frame.CommunicationMode[i] == 5) && state->dPMRVoiceFS2Frame.CCHDataCrcOk[i]) + { + AttachedDataFlag = 1; + } + } + } + + if(VoiceFrameFlag) + { + if(AttachedDataFlag) fprintf(stderr, "| Voice+Data "); + else fprintf(stderr, "| Voice "); + } + else + { + fprintf(stderr, "| Data "); + } + + /* Demodulate and store AMBE sample only if the current superframe + * is a voice frame */ + if(VoiceFrameFlag) + { + /* There is 4 AMBE voice sample per voice frame (= 8 per superframe) */ + for(i = 0; i < (NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4); i++) + { + /* Apply ECC 1 to the AMBE frames */ + state->dPMRVoiceFS2Frame.errs1[i] = (unsigned int)mbe_eccAmbe3600x2450C0(ambe_fr[i]); + state->dPMRVoiceFS2Frame.errs2[i] = state->dPMRVoiceFS2Frame.errs1[i]; + + /* Demodulate the AMBE frames */ + mbe_demodulateAmbe3600x2450Data(ambe_fr[i]); + + /* Apply ECC 2 to the AMBE frames and get the 49 bit of the voice sample */ + state->dPMRVoiceFS2Frame.errs2[i] += (unsigned int)mbe_eccAmbe3600x2450Data(ambe_fr[i], (char *)state->dPMRVoiceFS2Frame.AmbeBit[i]); + } + } /* End if(VoiceFrameFlag && !AttachedDataFlag) */ + + /* Check if the voice frame to play is encrypted */ + if(opts->dPMR_curr_frame_is_encrypted) + { + /* Encryption is not supported in the public version */ + ScramblerKey = 0; + } + else + { + /* The frame is unencrypted, + * so set the scrambler key to 0 */ + ScramblerKey = 0; + } + + + /* Play only voice frame */ + if(VoiceFrameFlag) + { + +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "Voice frame to play\n"); +#endif /* dPMR_PRINT_DEBUG_INFO */ + /* There is 4 AMBE voice sample per voice frame (so 2 x 4 = 8 per superframe) */ + for(i = 0; i < (NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4); i++) + { + errs = (uint32_t*)&(state->dPMRVoiceFS2Frame.errs1[i]); + errs2 = (uint32_t*)&(state->dPMRVoiceFS2Frame.errs2[i]); + state->errs = state->dPMRVoiceFS2Frame.errs1[i]; + state->errs2 = state->dPMRVoiceFS2Frame.errs2[i]; + + //sounds like choppy shit, but .amb file playback is acceptable, so investigate why this doesn't work properly + mbe_processAmbe2450Dataf (state->audio_out_temp_buf, (int *)errs, (int *)errs2, state->err_str, + (char *)state->dPMRVoiceFS2Frame.AmbeBit[i], + state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality); + + //mbe_processAmbe2450Data ((short *) state->audio_out_temp_buf_p, (int *)errs, (int *)errs2, state->err_str, + // (char *)state->dPMRVoiceFS2Frame.AmbeBit[i], + // state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality); + if (opts->mbe_out_f != NULL) + { + saveAmbe2450Data (opts, state, (char *)state->dPMRVoiceFS2Frame.AmbeBit[i]); + } + if (opts->errorbars == 1) + { + fprintf(stderr, "%s", state->err_str); + } + + state->debug_audio_errors += *errs2; + + processAudio(opts, state); + + if (opts->wav_out_f != NULL) + { + writeSynthesizedVoice (opts, state); + } + + if (opts->audio_out == 1) + { + /* Play the AMBE Frames */ + playSynthesizedVoice (opts, state); + } + } /* End for(i = 0; i < (NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4); i++) */ + } /* End if(VoiceFrameFlag) */ + else + { +#ifdef dPMR_PRINT_DEBUG_INFO + fprintf(stderr, "Data frame, don't play it !\n"); +#endif /* dPMR_PRINT_DEBUG_INFO */ + } + + if (opts->errorbars == 1) + { + fprintf(stderr, "\n"); + } + + +#ifdef dPMR_PRINT_DEBUG_INFO + /* Display the CCH content */ + for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++) + { + fprintf(stderr, "i = %u - ", i); + fprintf(stderr, "Comm Mode = %01u - ", state->dPMRVoiceFS2Frame.CommunicationMode[i]); + fprintf(stderr, "Version = %01u - ", state->dPMRVoiceFS2Frame.Version[i]); + fprintf(stderr, "Comms Format = %01u - ", state->dPMRVoiceFS2Frame.CommsFormat[i]); + fprintf(stderr, "Emergency = %01u - ", state->dPMRVoiceFS2Frame.EmergencyPriority[i]); + fprintf(stderr, "Reserved = %01u - ", state->dPMRVoiceFS2Frame.Reserved[i]); + fprintf(stderr, "Slow Data = 0x%05X - ", state->dPMRVoiceFS2Frame.SlowData[i]); + if(HammingOk[i] && CrcOk[i]) fprintf(stderr, "Valid"); + else fprintf(stderr, "CRC ERROR"); + fprintf(stderr, "\n"); + } +#endif /* dPMR_PRINT_DEBUG_INFO */ + + /* Print sPMR frame (if needed) */ + //dPMRVoiceFrameProcess(opts, state); //HERE HERE disabled to build +} /* End processdPMRvoice() */ + + +/* Scrambler used for dPMR transmission (different of the + * voice encryption scrambler), see ETSI TS 102 658 chapter + * 7.3 for the polynomial description. + * It is a X^9 + X^5 + 1 polynomial. */ +void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferOut, uint32_t NbOfBitToScramble) +{ + uint8_t S[9] = {0}; + uint32_t i; + uint8_t Temp; + uint32_t LFSRValue; + + LFSRValue = *LfsrValue; + + /* Load the initial LFSR value */ + for(i = 0; i < 9; i++) + { + S[i] = LFSRValue & 1; + LFSRValue >>= 1; + } + + /* There are 72 bit to encrypt for voice and 288 bit for data */ + for(i = 0; i < NbOfBitToScramble; i++) + { + BufferOut[i] = (BufferIn[i] ^ S[0]) & 0x01; + + /* Shift registers */ + Temp = S[4] ^ S[0]; + S[0] = S[1]; + S[1] = S[2]; + S[2] = S[3]; + S[3] = S[4]; + S[4] = S[5]; + S[5] = S[6]; + S[6] = S[7]; + S[7] = S[8]; + S[8] = Temp; + } + + /* Save the final LFSR value */ + LFSRValue = 0; + for(i = 9; i > 0; i--) + { + LFSRValue <<= 1; + LFSRValue |= S[i - 1] & 1; + } + + *LfsrValue = LFSRValue; +} /* End ScrambleDPmrBit() */ + + +/* Scrambler used for dPMR encryption / decryption, + * see ETSI TS 102 658 chapter 7.3 for the + * polynomial description. + * It is a X^9 + X^5 + 1 polynomial. */ +void DeInterleave6x12DPmrBit(uint8_t * BufferIn, uint8_t * BufferOut) +{ + uint8_t Matrix[12][6] = {0}; + uint32_t i, j, k; + + /* Step 1 : Filling the 12 x 6 bit matrix */ + k = 0; + for(i = 0; i < 12; i++) + { + for(j = 0; j < 6; j++) + { + Matrix[i][j] = BufferIn[k++]; + } + } + + /* Step 2 : Filling the output buffer with deinterleaved data */ + k = 0; + for(j = 0; j < 6; j++) + { + for(i = 0; i < 12; i++) + { + BufferOut[k++] = Matrix[i][j]; + } + } +} /* End DeInterleave6x12DPmrBit() */ + + +/* CRC 7 bit computation with the following + * polynomial : X^7 + X^3 + 1 */ +uint8_t CRC7BitdPMR(uint8_t * BufferIn, uint32_t BitLength) +{ + uint8_t ShiftRegister = 0x00; /* All bit to '0' (7 LSBit only used) */ + uint8_t Polynome = 0x09; /* X^7 + X^3 + 1 */ + uint32_t i; + + for(i = 0; i < BitLength; i++) + { + if(((ShiftRegister >> 6) & 1) ^ BufferIn[i]) + { + ShiftRegister = ((ShiftRegister << 1) ^ Polynome) & 0x7F; + } + else + { + ShiftRegister = (ShiftRegister << 1) & 0x7F; + } + } + + return ShiftRegister; +} /* End CRC7BitdPMR() */ + + +/* CRC 8 bit computation with the following + * polynomial : X^8 + X^2 + X + 1 */ +uint8_t CRC8BitdPMR(uint8_t * BufferIn, uint32_t BitLength) +{ + uint8_t ShiftRegister = 0xFF; /* All bit to '1' */ + uint8_t Polynome = 0x07; /* X^7 + X^3 + 1 */ + uint32_t i; + + for(i = 0; i < BitLength; i++) + { + if(((ShiftRegister >> 7) & 1) ^ BufferIn[i]) + { + ShiftRegister = ((ShiftRegister << 1) ^ Polynome) & 0xFF; + } + else + { + ShiftRegister = (ShiftRegister << 1) & 0xFF; + } + } + + return ShiftRegister; +} /* End CRC8BitdPMR() */ + + +/* Convert an air interface identifier (AI ID) into + * a 7 ASCII digit string. + * + * See dPMR standard chapter A.1.2.1.1.6 + * "Mapping of dialled strings to the AI address space" */ +void ConvertAirInterfaceID(uint32_t AI_ID, uint8_t ID[8]) +{ + uint32_t AI_ID_Temp = AI_ID; + uint32_t Digit; + + /* 1st digit */ + Digit = AI_ID_Temp / 1464100; + AI_ID_Temp = AI_ID_Temp % 1464100; + if(Digit == 10) ID[0] = '*'; + else ID[0] = Digit + '0'; + + /* 2nd digit */ + Digit = AI_ID_Temp / 146410; + AI_ID_Temp = AI_ID_Temp % 146410; + if(Digit == 10) ID[1] = '*'; + else ID[1] = Digit + '0'; + + /* 3rd digit */ + Digit = AI_ID_Temp / 14641; + AI_ID_Temp = AI_ID_Temp % 14641; + if(Digit == 10) ID[2] = '*'; + else ID[2] = Digit + '0'; + + /* 4th digit */ + Digit = AI_ID_Temp / 1331; + AI_ID_Temp = AI_ID_Temp % 1331; + if(Digit == 10) ID[3] = '*'; + else ID[3] = Digit + '0'; + + /* 5th digit */ + Digit = AI_ID_Temp / 121; + AI_ID_Temp = AI_ID_Temp % 121; + if(Digit == 10) ID[4] = '*'; + else ID[4] = Digit + '0'; + + /* 6th digit */ + Digit = AI_ID_Temp / 11; + AI_ID_Temp = AI_ID_Temp % 11; + if(Digit == 10) ID[5] = '*'; + else ID[5] = Digit + '0'; + + /* 7th digit */ + Digit = AI_ID_Temp; + if(Digit == 10) ID[6] = '*'; + else ID[6] = Digit + '0'; + + /* Add the "end of string" */ + ID[7] = '\0'; + +} /* End convertAirInterfaceID() */ + + + +/* End of file */ diff --git a/src/dsd_file.c b/src/dsd_file.c index a4ec9ca..6eb4dfc 100644 --- a/src/dsd_file.c +++ b/src/dsd_file.c @@ -72,7 +72,8 @@ saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d) { fprintf(stderr, "\n"); } - for (i = 0; i < 6; i++) + //for (i = 0; i < 6; i++) + for (i = 0; i < 7; i++) { b = 0; @@ -103,12 +104,17 @@ readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d) { int i, j, k; - unsigned char b; + unsigned char b, x; state->errs2 = fgetc (opts->mbe_in_f); state->errs = state->errs2; + k = 0; + if (opts->payload == 1) //make opt variable later on to toggle this + { + fprintf(stderr, "\n"); + } for (i = 0; i < 11; i++) { b = fgetc (opts->mbe_in_f); @@ -119,10 +125,24 @@ readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d) for (j = 0; j < 8; j++) { imbe_d[k] = (b & 128) >> 7; + + x = x << 1; + x |= ((b & 0x80) >> 7); + b = b << 1; b = b & 255; k++; } + //new printout on reading MBE files back in + if (opts->payload == 1) //make opt variable later on to toggle this + { + fprintf (stderr, "[%02X] ", x); + } + // + } + if (opts->payload == 1) + { + fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2); //not sure that errs here are legit values } return (0); } @@ -132,13 +152,18 @@ readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d) { int i, j, k; - unsigned char b; + unsigned char b, x; state->errs2 = fgetc (opts->mbe_in_f); state->errs = state->errs2; k = 0; - for (i = 0; i < 6; i++) + if (opts->payload == 1) //make opt variable later on to toggle this + { + fprintf(stderr, "\n"); + } + //for (i = 0; i < 6; i++) + for (i = 0; i < 7; i++) { b = fgetc (opts->mbe_in_f); if (feof (opts->mbe_in_f)) @@ -148,10 +173,22 @@ readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d) for (j = 0; j < 8; j++) { ambe_d[k] = (b & 128) >> 7; + + x = x << 1; + x |= ((b & 0x80) >> 7); + b = b << 1; b = b & 255; k++; } + if (opts->payload == 1) //make opt variable later on to toggle this + { + fprintf (stderr, "[%02X] ", x); + } + } + if (opts->payload == 1) + { + fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2); } b = fgetc (opts->mbe_in_f); ambe_d[48] = (b & 1); @@ -247,7 +284,7 @@ closeMbeOutFile (dsd_opts * opts, dsd_state * state) { 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); diff --git a/src/dsd_frame.c b/src/dsd_frame.c index 6044f5b..5b1c47a 100644 --- a/src/dsd_frame.c +++ b/src/dsd_frame.c @@ -244,6 +244,50 @@ processFrame (dsd_opts * opts, dsd_state * state) processProVoice (opts, state); return; } + //LEH dPMR + else if ((state->synctype == 20) || (state->synctype == 24)) + { + /* dPMR Frame Sync 1 */ + fprintf(stderr, "dPMR Frame sync 1 "); + } + else if ((state->synctype == 21) || (state->synctype == 25)) + { + /* dPMR Frame Sync 2 */ + fprintf(stderr, "dPMR Frame sync 2 "); + { + //state->rf_mod = GFSK_MODE; + state->nac = 0; + state->lastsrc = 0; + state->lasttg = 0; + if (opts->errorbars == 1) + { + if (opts->verbose > 0) + { + level = (int) state->max / 164; + fprintf(stderr, "inlvl: %2i%% ", level); + } + } + state->nac = 0; + if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) + { + openMbeOutFile (opts, state); + } + sprintf(state->fsubtype, " VOICE "); + processdPMRvoice (opts, state); + return; + } + } + else if ((state->synctype == 22) || (state->synctype == 26)) + { + /* dPMR Frame Sync 3 */ + fprintf(stderr, "dPMR Frame sync 3 "); + } + else if ((state->synctype == 23) || (state->synctype == 27)) + { + /* dPMR Frame Sync 4 */ + fprintf(stderr, "dPMR Frame sync 4 "); + } + //LEH dPMR else { // Read the NAC, 12 bits diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c index e2a7417..44c6fd1 100644 --- a/src/dsd_frame_sync.c +++ b/src/dsd_frame_sync.c @@ -143,11 +143,22 @@ getFrameSync (dsd_opts * opts, dsd_state * state) * 17 = -NXDN (inverted data frame) * 18 = +D-STAR_HD * 19 = -D-STAR_HD + * 20 = +dPMR Frame Sync 1 + * 21 = +dPMR Frame Sync 2 + * 22 = +dPMR Frame Sync 3 + * 23 = +dPMR Frame Sync 4 + * 24 = -dPMR Frame Sync 1 + * 25 = -dPMR Frame Sync 2 + * 26 = -dPMR Frame Sync 3 + * 27 = -dPMR Frame Sync 4 + * 28 = +NXDN (sync only) + * 29 = -NXDN (sync only */ int i, j, t, o, dibit, sync, symbol, synctest_pos, lastt; char synctest[25]; + char synctest12[13]; //LEH DPMR char synctest18[19]; char synctest32[33]; char modulation[8]; @@ -195,7 +206,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state) playRawAudio(opts, state); //this is on line 21 in dsd_audio.c } */ - + /* if (opts->reset_state == 1 && state->carrier == 0) { @@ -512,6 +523,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state) } } } + //old style for DMR + /* if (opts->frame_dmr == 1) { if ((strcmp (synctest, DMR_MS_DATA_SYNC) == 0) || (strcmp (synctest, DMR_BS_DATA_SYNC) == 0) )//try this @@ -586,6 +599,734 @@ getFrameSync (dsd_opts * opts, dsd_state * state) } } } + */ + //LEH dPMR + if(opts->frame_dpmr == 1) + { + strncpy(synctest, (synctest_p - 23), 24); + strncpy(synctest12, (synctest_p - 11), 12); + + if(strcmp(synctest, DPMR_FRAME_SYNC_1) == 0) + { + if (opts->inverted_dpmr == 0) + { + //fprintf(stderr, "DPMR_FRAME_SYNC_1\n"); // TODO : To be removed + + if ((state->lastsynctype == 20) || (state->lastsynctype == 21) || + (state->lastsynctype == 22) || (state->lastsynctype == 23)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +dPMR ", synctest_pos + 1, modulation); + } + + /* The next part of the superframe will normally be the first part */ + //opts->dPMR_next_part_of_superframe = 1; + + state->lastsynctype = 20; + return (20); + } + else + { + state->lastsynctype = 20; + } + } /* End if (opts->inverted_dpmr == 0) */ + } + else if(strcmp(synctest12, DPMR_FRAME_SYNC_2) == 0) + { + if (opts->inverted_dpmr == 0) + { + //fprintf(stderr, "DPMR_FRAME_SYNC_2\n"); // TODO : To be removed + + // TODO : Modif 2019-01-04 + //if ((state->lastsynctype == 20) || (state->lastsynctype == 21) || + // (state->lastsynctype == 22) || (state->lastsynctype == 23)) + //{ + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +dPMR ", synctest_pos + 1, modulation); + } + + state->lastsynctype = 21; + return (21); + // TODO : Modif 2019-01-04 + //} + //else + //{ + // state->lastsynctype = 21; + //} + } + } + else if(strcmp(synctest12, DPMR_FRAME_SYNC_3) == 0) + { + if (opts->inverted_dpmr == 0) + { + //fprintf(stderr, "DPMR_FRAME_SYNC_3\n"); // TODO : To be removed + + if ((state->lastsynctype == 20) || (state->lastsynctype == 21) || + (state->lastsynctype == 22) || (state->lastsynctype == 23)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +dPMR ", synctest_pos + 1, modulation); + } + + /* The next part of the superframe will normally be the first part */ + //opts->dPMR_next_part_of_superframe = 1; + + state->lastsynctype = 22; + return (22); + } + else + { + state->lastsynctype = 22; + } + } + } + if(strcmp(synctest, DPMR_FRAME_SYNC_4) == 0) + { + if (opts->inverted_dpmr == 0) + { + //fprintf(stderr, "DPMR_FRAME_SYNC_4\n"); // TODO : To be removed + + if ((state->lastsynctype == 20) || (state->lastsynctype == 21) || + (state->lastsynctype == 22) || (state->lastsynctype == 23)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +dPMR ", synctest_pos + 1, modulation); + } + + /* The next part of the superframe will normally be the first part */ + //opts->dPMR_next_part_of_superframe = 1; + + state->lastsynctype = 23; + return (23); + } + else + { + state->lastsynctype = 23; + } + } + } + else if(strcmp(synctest, INV_DPMR_FRAME_SYNC_1) == 0) + { + if (opts->inverted_dpmr) + { + //fprintf(stderr, "INV_DPMR_FRAME_SYNC_1\n"); // TODO : To be removed + if ((state->lastsynctype == 24) || (state->lastsynctype == 25) || + (state->lastsynctype == 26) || (state->lastsynctype == 27)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -dPMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 24; + return (24); + } + else + { + state->lastsynctype = 24; + } + } + } + else if(strcmp(synctest12, INV_DPMR_FRAME_SYNC_2) == 0) + { + if (opts->inverted_dpmr) + { + //fprintf(stderr, "DPMR_FRAME_SYNC_2\n"); // TODO : To be removed + + if ((state->lastsynctype == 24) || (state->lastsynctype == 25) || + (state->lastsynctype == 26) || (state->lastsynctype == 27)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -dPMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 25; + return (25); + } + else + { + state->lastsynctype = 25; + } + } + } + else if(strcmp(synctest12, INV_DPMR_FRAME_SYNC_3) == 0) + { + if (opts->inverted_dpmr) + { + //fprintf(stderr, "INV_DPMR_FRAME_SYNC_3\n"); // TODO : To be removed + + if ((state->lastsynctype == 24) || (state->lastsynctype == 25) || + (state->lastsynctype == 26) || (state->lastsynctype == 27)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -dPMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 26; + return (26); + } + else + { + state->lastsynctype = 26; + } + } + } + if(strcmp(synctest, INV_DPMR_FRAME_SYNC_4) == 0) + { + if (opts->inverted_dpmr) + { + //fprintf(stderr, "INV_DPMR_FRAME_SYNC_4\n"); // TODO : To be removed + + if ((state->lastsynctype == 24) || (state->lastsynctype == 25) || + (state->lastsynctype == 26) || (state->lastsynctype == 27)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -dPMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 27; + return (27); + } + else + { + state->lastsynctype = 27; + } + } + } + else + { + /* No dPMR frame sync detected */ + } + } /* End if(opts->frame_dpmr == 1) */ + + // + //LEH dPMR + /* //testing frame syncs here for better performance + if(opts->frame_dpmr == 1) + { + strncpy(synctest, (synctest_p - 23), 24); + strncpy(synctest12, (synctest_p - 11), 12); + + + + //if(strcmp(synctest, DPMR_FRAME_SYNC_1) == 0) + if (strncmperr (synctest, DPMR_FRAME_SYNC_1, 24, 1) == 0 ) //24 bits on frame 1 and 4, 12 on 2 and 3? + { + if (opts->inverted_dpmr == 0) + { + //fprintf(stderr, "DPMR_FRAME_SYNC_1\n"); // TODO : To be removed + + if ((state->lastsynctype == 20) || (state->lastsynctype == 21) || + (state->lastsynctype == 22) || (state->lastsynctype == 23)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +dPMR ", synctest_pos + 1, modulation); + } + + //The next part of the superframe will normally be the first part + //opts->dPMR_next_part_of_superframe = 1; + + state->lastsynctype = 20; + return (20); + } + else + { + state->lastsynctype = 20; + } + } //End if (opts->inverted_dpmr == 0) + } + + + + //else if(strcmp(synctest12, DPMR_FRAME_SYNC_2) == 0) + if (strncmperr (synctest12, DPMR_FRAME_SYNC_2, 12, 1) == 0 ) //char synctest12[13] ?? + { + if (opts->inverted_dpmr == 0) + { + //fprintf(stderr, "DPMR_FRAME_SYNC_2\n"); // TODO : To be removed + + // TODO : Modif 2019-01-04 + if ((state->lastsynctype == 20) || (state->lastsynctype == 21) || + (state->lastsynctype == 22) || (state->lastsynctype == 23)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +dPMR ", synctest_pos + 1, modulation); + } + + state->lastsynctype = 21; + return (21); + // TODO : Modif 2019-01-04 + } + else + { + state->lastsynctype = 21; + } + } + } + + + + //else if(strcmp(synctest12, DPMR_FRAME_SYNC_3) == 0) + if (strncmperr (synctest12, DPMR_FRAME_SYNC_3, 12, 1) == 0 ) //char synctest12[13] ?? + { + if (opts->inverted_dpmr == 0) + { + //fprintf(stderr, "DPMR_FRAME_SYNC_3\n"); // TODO : To be removed + + if ((state->lastsynctype == 20) || (state->lastsynctype == 21) || + (state->lastsynctype == 22) || (state->lastsynctype == 23)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +dPMR ", synctest_pos + 1, modulation); + } + + //The next part of the superframe will normally be the first part// + //opts->dPMR_next_part_of_superframe = 1; + + state->lastsynctype = 22; + return (22); + } + else + { + state->lastsynctype = 22; + } + } + } + //if(strcmp(synctest, DPMR_FRAME_SYNC_4) == 0) + if (strncmperr (synctest, DPMR_FRAME_SYNC_4, 24, 1) == 0 ) //if, or else if? original is else if + { + if (opts->inverted_dpmr == 0) + { + //fprintf(stderr, "DPMR_FRAME_SYNC_4\n"); // TODO : To be removed + + if ((state->lastsynctype == 20) || (state->lastsynctype == 21) || + (state->lastsynctype == 22) || (state->lastsynctype == 23)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +dPMR ", synctest_pos + 1, modulation); + } + + //The next part of the superframe will normally be the first part + //opts->dPMR_next_part_of_superframe = 1; + + state->lastsynctype = 23; + return (23); + } + else + { + state->lastsynctype = 23; + } + } + } + //else if(strcmp(synctest, INV_DPMR_FRAME_SYNC_1) == 0) + //else if (strncmperr (synctest12, INV_DPMR_FRAME_SYNC_1, 12, 1) == 0 ) + if (strncmperr (synctest, INV_DPMR_FRAME_SYNC_1, 24, 1) == 0 ) //if, or else if + { + if (opts->inverted_dpmr) + { + //fprintf(stderr, "INV_DPMR_FRAME_SYNC_1\n"); // TODO : To be removed + if ((state->lastsynctype == 24) || (state->lastsynctype == 25) || + (state->lastsynctype == 26) || (state->lastsynctype == 27)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -dPMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 24; + return (24); + } + else + { + state->lastsynctype = 24; + } + } + } + + + + //else if(strcmp(synctest12, INV_DPMR_FRAME_SYNC_2) == 0) + if (strncmperr (synctest12, INV_DPMR_FRAME_SYNC_2, 12, 1) == 0 ) + { + if (opts->inverted_dpmr) + { + //fprintf(stderr, "DPMR_FRAME_SYNC_2\n"); // TODO : To be removed + + if ((state->lastsynctype == 24) || (state->lastsynctype == 25) || + (state->lastsynctype == 26) || (state->lastsynctype == 27)) + { + + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -dPMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 25; + return (25); + } + else + { + state->lastsynctype = 25; + } + } + } + //else if(strcmp(synctest12, INV_DPMR_FRAME_SYNC_3) == 0) + + + + if (strncmperr (synctest12, INV_DPMR_FRAME_SYNC_3, 12, 1) == 0 ) + { + if (opts->inverted_dpmr) + { + //fprintf(stderr, "INV_DPMR_FRAME_SYNC_3\n"); // TODO : To be removed + + if ((state->lastsynctype == 24) || (state->lastsynctype == 25) || + (state->lastsynctype == 26) || (state->lastsynctype == 27)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -dPMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 26; + return (26); + } + else + { + state->lastsynctype = 26; + } + } + } + //if(strcmp(synctest, INV_DPMR_FRAME_SYNC_4) == 0) //if, or else if + if (strncmperr (synctest, INV_DPMR_FRAME_SYNC_4, 24, 1) == 0 ) + { + if (opts->inverted_dpmr) + { + //fprintf(stderr, "INV_DPMR_FRAME_SYNC_4\n"); // TODO : To be removed + + if ((state->lastsynctype == 24) || (state->lastsynctype == 25) || + (state->lastsynctype == 26) || (state->lastsynctype == 27)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + sprintf(state->ftype, " dPMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -dPMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 27; + return (27); + } + else + { + state->lastsynctype = 27; + } + } + } + else + { + //No dPMR frame sync detected + } + + } //End if(opts->frame_dpmr == 1) + */ + //LEH DMR Sync + if (opts->frame_dmr == 1) + { + if ((strcmp (synctest, DMR_MS_DATA_SYNC) == 0) || (strcmp (synctest, DMR_BS_DATA_SYNC) == 0)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + (lmax)) / 2; + state->min = ((state->min) + (lmin)) / 2; + state->directmode = 0; + if (opts->inverted_dmr == 0) + { + // data frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 10; + return (10); + } + else + { + // inverted voice frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation); + } + if (state->lastsynctype != 11) + { + state->firstframe = 1; + } + state->lastsynctype = 11; + return (11); + } + } + if(strcmp (synctest, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + (lmax)) / 2; + state->min = ((state->min) + (lmin)) / 2; + state->currentslot = 0; + state->directmode = 1; //Direct mode + if (opts->inverted_dmr == 0) + { + // data frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 10; + return (10); + } + else + { + // inverted voice frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation); + } + if (state->lastsynctype != 11) + { + state->firstframe = 1; + } + state->lastsynctype = 11; + return (11); + } + } /* End if(strcmp (synctest, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0) */ + if(strcmp (synctest, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + (lmax)) / 2; + state->min = ((state->min) + (lmin)) / 2; + state->currentslot = 1; + state->directmode = 1; //Direct mode + if (opts->inverted_dmr == 0) + { + // data frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 10; + return (10); + } + else + { + // inverted voice frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation); + } + if (state->lastsynctype != 11) + { + state->firstframe = 1; + } + state->lastsynctype = 11; + return (11); + } + } /* End if(strcmp (synctest, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0) */ + if((strcmp (synctest, DMR_MS_VOICE_SYNC) == 0) || (strcmp (synctest, DMR_BS_VOICE_SYNC) == 0)) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + state->directmode = 0; + if (opts->inverted_dmr == 0) + { + // voice frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation); + } + if (state->lastsynctype != 12) + { + state->firstframe = 1; + } + state->lastsynctype = 12; + return (12); + } + else + { + // inverted data frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 13; + return (13); + } + } + if(strcmp (synctest, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + state->currentslot = 0; + state->directmode = 1; //Direct mode + if (opts->inverted_dmr == 0) + { + // voice frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation); + } + if (state->lastsynctype != 12) + { + state->firstframe = 1; + } + state->lastsynctype = 12; + return (12); + } + else + { + // inverted data frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 13; + return (13); + } + } /* End if(strcmp (synctest, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0) */ + if(strcmp (synctest, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0) + { + state->carrier = 1; + state->offset = synctest_pos; + state->max = ((state->max) + lmax) / 2; + state->min = ((state->min) + lmin) / 2; + state->currentslot = 1; + state->directmode = 1; //Direct mode + if (opts->inverted_dmr == 0) + { + // voice frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation); + } + if (state->lastsynctype != 12) + { + state->firstframe = 1; + } + state->lastsynctype = 12; + return (12); + } + else + { + // inverted data frame + sprintf(state->ftype, " DMR "); + if (opts->errorbars == 1) + { + printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation); + } + state->lastsynctype = 13; + return (13); + } + } //End if(strcmp (synctest, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0) + } //End if (opts->frame_dmr == 1) + + //end LEH DMR Sync if (opts->frame_provoice == 1) { strncpy (synctest32, (synctest_p - 31), 32); diff --git a/src/dsd_main.c b/src/dsd_main.c index 2d3c38f..7dbe392 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -168,6 +168,7 @@ initOpts (dsd_opts * opts) opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dmr = 1; + opts->frame_dpmr = 0; //borrow from LEH opts->frame_provoice = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 1; @@ -190,16 +191,19 @@ initOpts (dsd_opts * opts) opts->rtl_bandwidth = 12; //changed recommended default to 12, 24 for ProVoice opts->pulse_raw_rate_in = 48000; opts->pulse_raw_rate_out = 48000; //doing tests with 2 channels at 24000 for 48000 audio default in pulse - opts->pulse_digi_rate_in = 24000; + opts->pulse_digi_rate_in = 48000; opts->pulse_digi_rate_out = 24000; //need to copy this to rtl type in and change rate out to 8000 opts->pulse_raw_in_channels = 1; opts->pulse_raw_out_channels = 1; - opts->pulse_digi_in_channels = 2; + opts->pulse_digi_in_channels = 1; //2 opts->pulse_digi_out_channels = 2; opts->pulse_flush = 1; //set 0 to flush, 1 for flushed opts->use_ncurses_terminal = 0; //ncurses terminal disabled by default, call with -N opts->payload = 0; //initialize with 0 so can pass == 0 arguments + + opts->EncryptionMode = MODE_UNENCRYPTED; + opts->inverted_dpmr = 0; } void @@ -297,6 +301,10 @@ initState (dsd_state * state) state->nxdn_last_ran = -1; state->nxdn_last_rid = 0; + state->dpmr_color_code = -1; + state->dpmr_caller_id = 0; + state->dpmr_target_id = 0; + #ifdef TRACE_DSD state->debug_sample_index = 0; state->debug_label_file = NULL; @@ -357,11 +365,12 @@ usage () fprintf (stderr," -fa Auto-detect frame type (default)\n"); fprintf (stderr," -f1 Decode only P25 Phase 1\n"); fprintf (stderr," -fd Decode only D-STAR\n"); + fprintf (stderr," -fr Decode only DMR/MOTOTRBO\n"); + fprintf (stderr," -fx Decode only X2-TDMA\n"); fprintf (stderr," -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n"); fprintf (stderr," -fn Decode only NXDN96* (12.5 kHz)\n"); fprintf (stderr," -fp Decode only ProVoice*\n"); - fprintf (stderr," -fr Decode only DMR/MOTOTRBO\n"); - fprintf (stderr," -fx Decode only X2-TDMA\n"); + fprintf (stderr," -fm Decode only dPMR*\n"); fprintf (stderr," -l Disable DMR/MOTOTRBO and NXDN input filtering\n"); fprintf (stderr," -ma Auto-select modulation optimizations (default)\n"); fprintf (stderr," -mc Use only C4FM modulation optimizations\n"); @@ -371,6 +380,7 @@ usage () fprintf (stderr," -u Unvoiced speech quality (default=3)\n"); fprintf (stderr," -xx Expect non-inverted X2-TDMA signal\n"); fprintf (stderr," -xr Expect inverted DMR/MOTOTRBO signal\n"); + fprintf (stderr," -xd Expect inverted ICOM dPMR signal\n"); //still need to do this fprintf (stderr,"\n"); fprintf (stderr," * denotes frame types that cannot be auto-detected.\n"); fprintf (stderr,"\n"); @@ -619,6 +629,8 @@ main (int argc, char **argv) initOpts (&opts); initState (&state); + InitAllFecFunction(); //HERE + exitflag = 0; signal (SIGINT, sigfun); @@ -807,6 +819,7 @@ main (int argc, char **argv) opts.frame_nxdn48 = 0; //turn it on, doesn't work due to symbol rate difference opts.frame_nxdn96 = 0; //causes false positives, leave on 0 opts.frame_dmr = 1; + opts.frame_dpmr = 0; //borrow from LEH opts.frame_provoice = 0; //turn it on, doesn't work due to symbol rate difference } else if (optarg[0] == 'd') @@ -817,6 +830,7 @@ main (int argc, char **argv) opts.frame_nxdn48 = 0; opts.frame_nxdn96 = 0; opts.frame_dmr = 0; + opts.frame_dpmr = 0; //borrow from LEH opts.frame_provoice = 0; fprintf (stderr,"Decoding only D-STAR frames.\n"); } @@ -828,6 +842,7 @@ main (int argc, char **argv) opts.frame_nxdn48 = 0; opts.frame_nxdn96 = 0; opts.frame_dmr = 0; + opts.frame_dpmr = 0; //borrow from LEH opts.frame_provoice = 0; fprintf (stderr,"Decoding only X2-TDMA frames.\n"); } @@ -839,6 +854,7 @@ main (int argc, char **argv) opts.frame_nxdn48 = 0; opts.frame_nxdn96 = 0; opts.frame_dmr = 0; + opts.frame_dpmr = 0; //borrow from LEH opts.frame_provoice = 1; state.samplesPerSymbol = 5; state.symbolCenter = 2; @@ -858,6 +874,7 @@ main (int argc, char **argv) opts.frame_nxdn48 = 0; opts.frame_nxdn96 = 0; opts.frame_dmr = 0; + opts.frame_dpmr = 0; //borrow from LEH opts.frame_provoice = 0; opts.mod_gfsk = 0; opts.mod_c4fm = 1; //Phase 1 only uses C4FM, right? @@ -873,6 +890,7 @@ main (int argc, char **argv) opts.frame_nxdn48 = 1; opts.frame_nxdn96 = 0; opts.frame_dmr = 0; + opts.frame_dpmr = 0; //borrow from LEH opts.frame_provoice = 0; //I think I've got it to decode on proper modulation now c4fm, not really sure what the difference is in DSD, seems to work either way for some reason state.samplesPerSymbol = 20; @@ -894,6 +912,7 @@ main (int argc, char **argv) opts.frame_nxdn48 = 0; opts.frame_nxdn96 = 1; opts.frame_dmr = 0; + opts.frame_dpmr = 0; //borrow from LEH opts.frame_provoice = 0; opts.mod_c4fm = 1; //was 0 opts.mod_qpsk = 0; @@ -910,6 +929,7 @@ main (int argc, char **argv) opts.frame_nxdn48 = 0; opts.frame_nxdn96 = 0; opts.frame_dmr = 1; + opts.frame_dpmr = 0; //borrow from LEH opts.frame_provoice = 0; opts.mod_c4fm = 1; //just a test opts.mod_qpsk = 0; @@ -917,6 +937,32 @@ main (int argc, char **argv) state.rf_mod = 0; // fprintf (stderr,"Decoding only DMR/MOTOTRBO frames.\n"); } + else if (optarg[0] == 'm') + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_provoice = 0; + opts.frame_dpmr = 1; + state.samplesPerSymbol = 20; //same as NXDN48 - 20 + state.symbolCenter = 10; //same as NXDN48 - 10 + opts.mod_c4fm = 1; + opts.mod_qpsk = 0; + opts.mod_gfsk = 0; + state.rf_mod = 0; + //opts.pulse_digi_rate_in = 48000; + //opts.pulse_digi_rate_out = 48000; //need to copy this to rtl type in and change rate out to 8000 + //opts.pulse_digi_in_channels = 1; + //opts.pulse_digi_out_channels = 1; + //opts.inverted_dpmr = 1; + fprintf(stderr, "Setting symbol rate to 2400 / second\n"); + //fprintf(stderr, "Enabling only GFSK modulation optimizations.\n"); + fprintf(stderr, "Enabling only C4FM modulation optimizations.\n"); + fprintf(stderr, "Decoding only dPMR (4800 baud frames).\n"); + } break; case 'm': if (optarg[0] == 'a') @@ -974,6 +1020,11 @@ main (int argc, char **argv) opts.inverted_dmr = 1; fprintf (stderr,"Expecting inverted DMR/MOTOTRBO signals.\n"); } + else if (optarg[0] == 'd') + { + opts.inverted_dpmr = 1; + fprintf (stderr, "Expecting inverted ICOM dPMR signals.\n"); + } break; case 'A': sscanf (optarg, "%i", &opts.mod_threshold); diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c index 92e8e39..173e5a2 100644 --- a/src/dsd_ncurses.c +++ b/src/dsd_ncurses.c @@ -36,7 +36,7 @@ int rn; //last ran int nc; //nac int src; //last rid? double? int lls = -1; //last last sync type -int dcc = 0; //initialize with 0 to prevent core dump +int dcc = -1; //initialize with 0 to prevent core dump int i = 0; char * s0last = " "; char * s1last = " "; @@ -53,7 +53,7 @@ char * FM_bannerN[9] = { "https://github.com/lwvmobile/dsd-fme/tree/pulseaudio " }; -char * SyncTypes[20] = { +char * SyncTypes[30] = { "+P25P1", "-P25P1", "+X2TDMA DATA", @@ -73,7 +73,15 @@ char * SyncTypes[20] = { "+NXDN DATA", //16 "-NXDN DATA", //17 "+DSTAR HD", - "-DSTAR HD" + "-DSTAR HD", + "+dPMR", + "+dPMR", + "+dPMR", + "+dPMR", + "-dPMR", + "-dPMR", + "-dPMR", + "-dPMR" }; @@ -187,7 +195,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //if (state->dmr_color_code != dcc && (lls == 11 || lls == 13 || lls == 10 || lls == 12) ) //DMR Voice + last two is data //if ( (call_matrix[9][4] != dcc || call_matrix[9][2] != rd) && (lls == 10 || lls == 11 || lls == 12 || lls == 13) ) //DMR - if ( (call_matrix[9][4] != dcc || call_matrix[9][2] != rd) && (lls == 12 || lls == 13) ) //DMR + if ( (call_matrix[9][4] != dcc || call_matrix[9][2] != rd) && (lls == 12 || lls == 13) ) //DMR, needs work { //dcc = state->dmr_color_code; for (short int k = 0; k < 9; k++) @@ -302,19 +310,25 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) rn = state->nxdn_last_ran; } - if (state->dmr_color_code > 0 && (lls == 10 || lls == 11 || lls == 12 || lls == 13) ) //DMR, DCC only carried on Data? + //if (state->dmr_color_code > 0 && (lls == 10 || lls == 11 || lls == 12 || lls == 13) ) //DMR, DCC only carried on Data? + //if (state->color_code > -1 && (lls == 10 || lls == 11 || lls == 12 || lls == 13) ) //DMR, DCC only carried on Data? + if (state->color_code_ok && (lls == 12 || lls == 13) ) //DMR, needs work + { dcc = state->dmr_color_code; + //dcc = state->color_code; } - if (state->lastsrc > 0 && (lls == 12 || lls == 13)) + if (state->lastsrc > 0 && (lls == 12 || lls == 13 || lls == 0 || lls == 1)) //DMR Voice and P25P1 { rd = state->lastsrc; + opts->p25enc = 0; } - if (state->lasttg > 0 && (lls == 12 || lls == 13)) + if (state->lasttg > 0 && (lls == 12 || lls == 13 || lls == 0 || lls == 1)) //DMR Voice and P25P1 { tg = state->lasttg; + opts->p25enc = 0; } //if (state->lastsynctype == 8 || state->lastsynctype == 9 || state->lastsynctype == 16 || state->lastsynctype == 17) //change this to NXDN syncs later on @@ -326,12 +340,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } //printw ("Error?: [%i] [%i] \n", state->errs, state->errs2); //what are these? - + /* //wasn't we already doing this twice if (state->lasttg > 0 && state->lastsrc > 0) { tg = state->lasttg; rd = state->lastsrc; } + */ if (state->nac > 0) { nc = state->nac; @@ -358,7 +373,35 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //if (state->lastsynctype == 12 || state->lastsynctype == 13) //DMR Voice Types if (lls == 12 || lls == 13) //DMR Voice Types { - printw ("| DCC: [%i]\n", dcc); + //printw ("| DCC: [%i] FID: [%02X]\n", dcc, state->dmr_fid); + printw ("| DCC: [%i] FID: [%02X] SOP: [%X] \n", dcc, state->dmr_fid, state->dmr_so); + printw ("| TID: [%i] RID: [%i]", tg, rd); + if(state->dmr_so & 0x80) //& apparently is same as == + { + attron(COLOR_PAIR(2)); + printw (" **Emergency** "); + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + } + + if(state->dmr_so & 0x40) + { + attron(COLOR_PAIR(2)); + printw (" **ENC** "); + //printw ("0x%X", state->payload_algid); + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + //opts->p25enc = 1; //just testing for now + } + + if(state->dmr_so & 0x30) + { + attron(COLOR_PAIR(2)); + printw (" **Private Call** "); + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + } + printw("\n"); //printw ("%s ", state->slot0light); printw ("| SLOT 0 "); if (state->currentslot == 0) //find out how to tell when slot0 has voice @@ -380,9 +423,39 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //if (state->lastsynctype == 10 || state->lastsynctype == 11) //DMR Data Types - if (lls == 10 || lls == 11) //DMR Data Types + if (lls == 10 || lls == 11 ) //DMR Data Types { - printw ("| DCC: [%i]\n", dcc); + //printw ("| DCC: [%i]\n", dcc); + printw ("| DCC: [%i] FID: [%02X] SOP: [%X] \n", dcc, state->dmr_fid, state->dmr_so); + printw ("| TID: [%i] RID: [%i]", tg, rd); + //does this need to be in DATA type? + /* + if(state->dmr_so & 0x80) + { + attron(COLOR_PAIR(2)); + printw (" **Emergency** "); + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + } + + if(state->dmr_so & 0x40) + { + attron(COLOR_PAIR(2)); + printw (" **ENC** "); + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + } + + if(state->dmr_so & 0x30) + { + attron(COLOR_PAIR(2)); + printw (" **Private Call** "); + attroff(COLOR_PAIR(2)); + attron(COLOR_PAIR(3)); + } + */ + printw("\n"); + //printw ("%s ", state->slot0light); printw ("| SLOT 0 "); if (state->currentslot == 0) //find out how to tell when slot0 has voice @@ -406,6 +479,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } printw ("%s \n", s1last); } + + //dPMR + if (lls == 20 || lls == 21 || lls == 22 || lls == 23 ||lls == 24 || lls == 25 || lls == 26 || lls == 27) + { + printw ("| DCC: [%i] ", state->dpmr_color_code); + printw ("TID: [%s] RID: [%s] \n", state->dpmr_target_id, state->dpmr_caller_id); + } + if (lls != -1) //is there a synctype 0? { printw ("| %s \n", SyncTypes[lls]); diff --git a/src/fec.c b/src/fec.c new file mode 100644 index 0000000..6180f2d --- /dev/null +++ b/src/fec.c @@ -0,0 +1,1448 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB. // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "dsd.h" + +unsigned char Hamming_7_4_m_corr[8]; //!< single bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Hamming_7_4_m_G[7*4] = { + 1, 0, 0, 0, 1, 0, 1, + 0, 1, 0, 0, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 1, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Hamming_7_4_m_H[7*3] = { + 1, 1, 1, 0, 1, 0, 0, + 0, 1, 1, 1, 0, 1, 0, + 1, 1, 0, 1, 0, 0, 1 +// 0 1 2 3 <- correctable bit positions +}; + + +// ======================================================================================== + + +unsigned char Hamming_12_8_m_corr[16]; //!< single bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Hamming_12_8_m_G[12*8] = { + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Hamming_12_8_m_H[12*4] = { + 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, + 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, + 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1 +// 0 1 2 3 4 5 6 7 <- correctable bit positions +}; + + +// ======================================================================================== + + +unsigned char Hamming_13_9_m_corr[16]; //!< single bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Hamming_13_9_m_G[13*9] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1 +}; + +//!< Parity check matrix of bits +const unsigned char Hamming_13_9_m_H[13*4] = { + 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, + 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, + 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, +// 0 1 2 3 4 5 6 7 8 <- correctable bit positions +}; + + +// ======================================================================================== + + +unsigned char Hamming_15_11_m_corr[16]; //!< single bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Hamming_15_11_m_G[15*11] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Hamming_15_11_m_H[15*4] = { + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, + 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, +// 0 1 2 3 4 5 6 7 8 9 10 <- correctable bit positions +}; + + +// ======================================================================================== + + +unsigned char Hamming_16_11_4_m_corr[32]; //!< single bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Hamming_16_11_4_m_G[16*11] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1 +}; + +//!< Parity check matrix of bits +const unsigned char Hamming_16_11_4_m_H[16*5] = { + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, + 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, + 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1 +}; + + +// ======================================================================================== + + +unsigned char Golay_20_8_m_corr[4096][3]; //!< up to 3 bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Golay_20_8_m_G[20*8] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Golay_20_8_m_H[20*12] = { + 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + + +// ======================================================================================== + + +unsigned char Golay_23_12_m_corr[2048][3]; //!< up to 3 bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Golay_23_12_m_G[23*12] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Golay_23_12_m_H[23*11] = { + 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + + +// ======================================================================================== + + +unsigned char Golay_24_12_m_corr[4096][3]; //!< up to 3 bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char Golay_24_12_m_G[24*12] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char Golay_24_12_m_H[24*12] = { + 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + + +// ======================================================================================== + + +unsigned char QR_16_7_6_m_corr[512][2]; //!< up to 2 bit error correction by syndrome index + +//!< Generator matrix of bits +const unsigned char QR_16_7_6_m_G[16*7] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, +}; + +//!< Parity check matrix of bits +const unsigned char QR_16_7_6_m_H[16*9] = { + 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + + +// ======================================================================================== + + +void Hamming_7_4_init() +{ + // correctable bit positions given syndrome bits as index (see above) + memset(Hamming_7_4_m_corr, 0xFF, 8); // initialize with all invalid positions + Hamming_7_4_m_corr[0b101] = 0; + Hamming_7_4_m_corr[0b111] = 1; + Hamming_7_4_m_corr[0b110] = 2; + Hamming_7_4_m_corr[0b011] = 3; + Hamming_7_4_m_corr[0b100] = 4; + Hamming_7_4_m_corr[0b010] = 5; + Hamming_7_4_m_corr[0b001] = 6; +} + +// Not very efficient but encode is used for unit testing only +void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 7); + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 7; j++) + { + encodedBits[j] += origBits[i] * Hamming_7_4_m_G[7*i + j]; + } + } + + for (i = 0; i < 7; i++) + { + encodedBits[i] %= 2; + } +} + +bool Hamming_7_4_decode(unsigned char *rxBits) // corrects in place +{ + unsigned int syndromeI = 0; // syndrome index + int is = 0; + + for (is = 0; is < 3; is++) + { + syndromeI += (((rxBits[0] * Hamming_7_4_m_H[7*is + 0]) + + (rxBits[1] * Hamming_7_4_m_H[7*is + 1]) + + (rxBits[2] * Hamming_7_4_m_H[7*is + 2]) + + (rxBits[3] * Hamming_7_4_m_H[7*is + 3]) + + (rxBits[4] * Hamming_7_4_m_H[7*is + 4]) + + (rxBits[5] * Hamming_7_4_m_H[7*is + 5]) + + (rxBits[6] * Hamming_7_4_m_H[7*is + 6])) % 2) << (2-is); + } + + if (syndromeI > 0) + { + if (Hamming_7_4_m_corr[syndromeI] == 0xFF) + { + return false; + } + else + { + rxBits[Hamming_7_4_m_corr[syndromeI]] ^= 1; // flip bit + } + } + + return true; +} + + +// ======================================================================================== + + +void Hamming_12_8_init() +{ + // correctable bit positions given syndrome bits as index (see above) + memset(Hamming_12_8_m_corr, 0xFF, 16); // initialize with all invalid positions + Hamming_12_8_m_corr[0b1110] = 0; + Hamming_12_8_m_corr[0b0111] = 1; + Hamming_12_8_m_corr[0b1010] = 2; + Hamming_12_8_m_corr[0b0101] = 3; + Hamming_12_8_m_corr[0b1011] = 4; + Hamming_12_8_m_corr[0b1100] = 5; + Hamming_12_8_m_corr[0b0110] = 6; + Hamming_12_8_m_corr[0b0011] = 7; + Hamming_12_8_m_corr[0b1000] = 8; + Hamming_12_8_m_corr[0b0100] = 9; + Hamming_12_8_m_corr[0b0010] = 10; + Hamming_12_8_m_corr[0b0001] = 11; +} + +// Not very efficient but encode is used for unit testing only +void Hamming_12_8_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 12); + + for (i = 0; i < 8; i++) + { + for (j = 0; j < 12; j++) + { + encodedBits[j] += origBits[i] * Hamming_12_8_m_G[12*i + j]; + } + } + + for (i = 0; i < 12; i++) + { + encodedBits[i] %= 2; + } +} + +bool Hamming_12_8_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords) +{ + bool correctable = true; + int ic = 0; + int is = 0; + int syndromeI = 0; // syndrome index + + for (ic = 0; ic < nbCodewords; ic++) + { + // calculate syndrome + + syndromeI = 0; // syndrome index + + for (is = 0; is < 4; is++) + { + syndromeI += (((rxBits[12*ic + 0] * Hamming_12_8_m_H[12*is + 0]) + + (rxBits[12*ic + 1] * Hamming_12_8_m_H[12*is + 1]) + + (rxBits[12*ic + 2] * Hamming_12_8_m_H[12*is + 2]) + + (rxBits[12*ic + 3] * Hamming_12_8_m_H[12*is + 3]) + + (rxBits[12*ic + 4] * Hamming_12_8_m_H[12*is + 4]) + + (rxBits[12*ic + 5] * Hamming_12_8_m_H[12*is + 5]) + + (rxBits[12*ic + 6] * Hamming_12_8_m_H[12*is + 6]) + + (rxBits[12*ic + 7] * Hamming_12_8_m_H[12*is + 7]) + + (rxBits[12*ic + 8] * Hamming_12_8_m_H[12*is + 8]) + + (rxBits[12*ic + 9] * Hamming_12_8_m_H[12*is + 9]) + + (rxBits[12*ic + 10] * Hamming_12_8_m_H[12*is + 10]) + + (rxBits[12*ic + 11] * Hamming_12_8_m_H[12*is + 11])) % 2) << (3-is); + } + + // correct bit + + if (syndromeI > 0) // single bit error correction + { + if (Hamming_12_8_m_corr[syndromeI] == 0xFF) // uncorrectable error + { + correctable = false; + } + else + { + rxBits[Hamming_12_8_m_corr[syndromeI]] ^= 1; // flip bit + } + } + + // move information bits + memcpy(&decodedBits[8*ic], &rxBits[12*ic], 8); + } + + return correctable; +} + + +// ======================================================================================== + + +void Hamming_13_9_init() +{ + // correctable bit positions given syndrome bits as index (see above) + memset(Hamming_13_9_m_corr, 0xFF, 16); // initialize with all invalid positions + Hamming_13_9_m_corr[0b1111] = 0; + Hamming_13_9_m_corr[0b1110] = 1; + Hamming_13_9_m_corr[0b0111] = 2; + Hamming_13_9_m_corr[0b1010] = 3; + Hamming_13_9_m_corr[0b0101] = 4; + Hamming_13_9_m_corr[0b1011] = 5; + Hamming_13_9_m_corr[0b1100] = 6; + Hamming_13_9_m_corr[0b0110] = 7; + Hamming_13_9_m_corr[0b0011] = 8; + Hamming_13_9_m_corr[0b1000] = 9; + Hamming_13_9_m_corr[0b0100] = 10; + Hamming_13_9_m_corr[0b0010] = 11; + Hamming_13_9_m_corr[0b0001] = 12; +} + +// Not very efficient but encode is used for unit testing only +void Hamming_13_9_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + memset(encodedBits, 0, 13); + + for (i = 0; i < 9; i++) + { + for (j = 0; j < 13; j++) + { + encodedBits[j] += origBits[i] * Hamming_13_9_m_G[13*i + j]; + } + } + + for (i = 0; i < 13; i++) + { + encodedBits[i] %= 2; + } +} + +bool Hamming_13_9_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords) +{ + bool correctable = true; + int ic = 0, is = 0; + int syndromeI = 0; // syndrome index + + for (ic = 0; ic < nbCodewords; ic++) + { + // calculate syndrome + + syndromeI = 0; // syndrome index + + for (is = 0; is < 4; is++) + { + syndromeI += (((rxBits[13*ic + 0] * Hamming_13_9_m_H[13*is + 0]) + + (rxBits[13*ic + 1] * Hamming_13_9_m_H[13*is + 1]) + + (rxBits[13*ic + 2] * Hamming_13_9_m_H[13*is + 2]) + + (rxBits[13*ic + 3] * Hamming_13_9_m_H[13*is + 3]) + + (rxBits[13*ic + 4] * Hamming_13_9_m_H[13*is + 4]) + + (rxBits[13*ic + 5] * Hamming_13_9_m_H[13*is + 5]) + + (rxBits[13*ic + 6] * Hamming_13_9_m_H[13*is + 6]) + + (rxBits[13*ic + 7] * Hamming_13_9_m_H[13*is + 7]) + + (rxBits[13*ic + 8] * Hamming_13_9_m_H[13*is + 8]) + + (rxBits[13*ic + 9] * Hamming_13_9_m_H[13*is + 9]) + + (rxBits[13*ic + 10] * Hamming_13_9_m_H[13*is + 10]) + + (rxBits[13*ic + 11] * Hamming_13_9_m_H[13*is + 11]) + + (rxBits[13*ic + 12] * Hamming_13_9_m_H[13*is + 12])) % 2) << (3-is); + } + + // correct bit + + if (syndromeI > 0) // single bit error correction + { + if (Hamming_13_9_m_corr[syndromeI] == 0xFF) // uncorrectable error + { + correctable = false; + break; + } + else + { + rxBits[Hamming_13_9_m_corr[syndromeI]] ^= 1; // flip bit + } + } + + // move information bits + if (decodedBits) + { + memcpy(&decodedBits[9*ic], &rxBits[13*ic], 9); + } + } + + return correctable; +} + + +// ======================================================================================== + + +void Hamming_15_11_init() +{ + // correctable bit positions given syndrome bits as index (see above) + memset(Hamming_15_11_m_corr, 0xFF, 16); // initialize with all invalid positions + Hamming_15_11_m_corr[0b1001] = 0; + Hamming_15_11_m_corr[0b1101] = 1; + Hamming_15_11_m_corr[0b1111] = 2; + Hamming_15_11_m_corr[0b1110] = 3; + Hamming_15_11_m_corr[0b0111] = 4; + Hamming_15_11_m_corr[0b1010] = 5; + Hamming_15_11_m_corr[0b0101] = 6; + Hamming_15_11_m_corr[0b1011] = 7; + Hamming_15_11_m_corr[0b1100] = 8; + Hamming_15_11_m_corr[0b0110] = 9; + Hamming_15_11_m_corr[0b0011] = 10; + Hamming_15_11_m_corr[0b1000] = 11; + Hamming_15_11_m_corr[0b0100] = 12; + Hamming_15_11_m_corr[0b0010] = 13; + Hamming_15_11_m_corr[0b0001] = 14; +} + +// Not very efficient but encode is used for unit testing only +void Hamming_15_11_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + memset(encodedBits, 0, 15); + + for (i = 0; i < 11; i++) + { + for (j = 0; j < 15; j++) + { + encodedBits[j] += origBits[i] * Hamming_15_11_m_G[15*i + j]; + } + } + + for (i = 0; i < 15; i++) + { + encodedBits[i] %= 2; + } +} + +bool Hamming_15_11_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords) +{ + bool correctable = true; + int ic = 0, is = 0; + int syndromeI = 0; // syndrome index + + for (ic = 0; ic < nbCodewords; ic++) + { + // calculate syndrome + + syndromeI = 0; // syndrome index + + for (is = 0; is < 4; is++) + { + syndromeI += (((rxBits[15*ic + 0] * Hamming_15_11_m_H[15*is + 0]) + + (rxBits[15*ic + 1] * Hamming_15_11_m_H[15*is + 1]) + + (rxBits[15*ic + 2] * Hamming_15_11_m_H[15*is + 2]) + + (rxBits[15*ic + 3] * Hamming_15_11_m_H[15*is + 3]) + + (rxBits[15*ic + 4] * Hamming_15_11_m_H[15*is + 4]) + + (rxBits[15*ic + 5] * Hamming_15_11_m_H[15*is + 5]) + + (rxBits[15*ic + 6] * Hamming_15_11_m_H[15*is + 6]) + + (rxBits[15*ic + 7] * Hamming_15_11_m_H[15*is + 7]) + + (rxBits[15*ic + 8] * Hamming_15_11_m_H[15*is + 8]) + + (rxBits[15*ic + 9] * Hamming_15_11_m_H[15*is + 9]) + + (rxBits[15*ic + 10] * Hamming_15_11_m_H[15*is + 10]) + + (rxBits[15*ic + 11] * Hamming_15_11_m_H[15*is + 11]) + + (rxBits[15*ic + 12] * Hamming_15_11_m_H[15*is + 12]) + + (rxBits[15*ic + 13] * Hamming_15_11_m_H[15*is + 13]) + + (rxBits[15*ic + 14] * Hamming_15_11_m_H[15*is + 14])) % 2) << (3-is); + } + + // correct bit + + if (syndromeI > 0) // single bit error correction + { + if (Hamming_15_11_m_corr[syndromeI] == 0xFF) // uncorrectable error + { + correctable = false; + break; + } + else + { + rxBits[Hamming_15_11_m_corr[syndromeI]] ^= 1; // flip bit + } + } + + // move information bits + if (decodedBits) + { + memcpy(&decodedBits[11*ic], &rxBits[15*ic], 11); + } + } + + return correctable; +} + + +// ======================================================================================== + + +void Hamming_16_11_4_init() +{ + // correctable bit positions given syndrome bits as index (see above) + memset(Hamming_16_11_4_m_corr, 0xFF, 32); // initialize with all invalid positions + Hamming_16_11_4_m_corr[0b10011] = 0; + Hamming_16_11_4_m_corr[0b11010] = 1; + Hamming_16_11_4_m_corr[0b11111] = 2; + Hamming_16_11_4_m_corr[0b11100] = 3; + Hamming_16_11_4_m_corr[0b01110] = 4; + Hamming_16_11_4_m_corr[0b10101] = 5; + Hamming_16_11_4_m_corr[0b01011] = 6; + Hamming_16_11_4_m_corr[0b10110] = 7; + Hamming_16_11_4_m_corr[0b11001] = 8; + Hamming_16_11_4_m_corr[0b01101] = 9; + Hamming_16_11_4_m_corr[0b00111] = 10; + Hamming_16_11_4_m_corr[0b10000] = 11; + Hamming_16_11_4_m_corr[0b01000] = 12; + Hamming_16_11_4_m_corr[0b00100] = 13; + Hamming_16_11_4_m_corr[0b00010] = 14; + Hamming_16_11_4_m_corr[0b00001] = 15; +} + +// Not very efficient but encode is used for unit testing only +void Hamming_16_11_4_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + memset(encodedBits, 0, 16); + + for (i = 0; i < 11; i++) + { + for (j = 0; j < 16; j++) + { + encodedBits[j] += origBits[i] * Hamming_16_11_4_m_G[16*i + j]; + } + } + + for (i = 0; i < 16; i++) + { + encodedBits[i] %= 2; + } +} + +bool Hamming_16_11_4_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords) +{ + bool correctable = true; + int ic = 0, is = 0; + int syndromeI = 0; // syndrome index + + for (ic = 0; ic < nbCodewords; ic++) + { + // calculate syndrome + syndromeI = 0; // syndrome index + for (is = 0; is < 5; is++) + { + syndromeI += (((rxBits[16*ic + 0] * Hamming_16_11_4_m_H[16*is + 0]) + + (rxBits[16*ic + 1] * Hamming_16_11_4_m_H[16*is + 1]) + + (rxBits[16*ic + 2] * Hamming_16_11_4_m_H[16*is + 2]) + + (rxBits[16*ic + 3] * Hamming_16_11_4_m_H[16*is + 3]) + + (rxBits[16*ic + 4] * Hamming_16_11_4_m_H[16*is + 4]) + + (rxBits[16*ic + 5] * Hamming_16_11_4_m_H[16*is + 5]) + + (rxBits[16*ic + 6] * Hamming_16_11_4_m_H[16*is + 6]) + + (rxBits[16*ic + 7] * Hamming_16_11_4_m_H[16*is + 7]) + + (rxBits[16*ic + 8] * Hamming_16_11_4_m_H[16*is + 8]) + + (rxBits[16*ic + 9] * Hamming_16_11_4_m_H[16*is + 9]) + + (rxBits[16*ic + 10] * Hamming_16_11_4_m_H[16*is + 10]) + + (rxBits[16*ic + 11] * Hamming_16_11_4_m_H[16*is + 11]) + + (rxBits[16*ic + 12] * Hamming_16_11_4_m_H[16*is + 12]) + + (rxBits[16*ic + 13] * Hamming_16_11_4_m_H[16*is + 13]) + + (rxBits[16*ic + 14] * Hamming_16_11_4_m_H[16*is + 14]) + + (rxBits[16*ic + 15] * Hamming_16_11_4_m_H[16*is + 15])) % 2) << (4-is); + } + + // correct bit + + if (syndromeI > 0) // single bit error correction + { + if (Hamming_16_11_4_m_corr[syndromeI] == 0xFF) // uncorrectable error + { + correctable = false; + break; + } + else + { + rxBits[Hamming_16_11_4_m_corr[syndromeI]] ^= 1; // flip bit + } + } + + // move information bits + if (decodedBits) + { + memcpy(&decodedBits[11*ic], &rxBits[16*ic], 11); + } + } + + return correctable; +} + + +// ======================================================================================== + + +void Golay_20_8_init() +{ + int i1 = 0, i2 = 0, i3 = 0, ir = 0, ip = 0; + int syndromeI = 0, syndromeIP = 0; + int ip1 = 0, ip2 = 0, ip3 = 0; + int syndromeIP1 = 0, syndromeIP2 = 0, syndromeIP3 = 0; + + memset(Golay_20_8_m_corr, 0xFF, 3*4096); + + for (i1 = 0; i1 < 8; i1++) + { + for (i2 = i1+1; i2 < 8; i2++) + { + for (i3 = i2+1; i3 < 8; i3++) + { + // 3 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += ((Golay_20_8_m_H[20*ir + i1] + Golay_20_8_m_H[20*ir + i2] + Golay_20_8_m_H[20*ir + i3]) % 2) << (11-ir); + } + + Golay_20_8_m_corr[syndromeI][0] = i1; + Golay_20_8_m_corr[syndromeI][1] = i2; + Golay_20_8_m_corr[syndromeI][2] = i3; + } + + // 2 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += ((Golay_20_8_m_H[20*ir + i1] + Golay_20_8_m_H[20*ir + i2]) % 2) << (11-ir); + } + + Golay_20_8_m_corr[syndromeI][0] = i1; + Golay_20_8_m_corr[syndromeI][1] = i2; + + // 1 possible bit flip left in the parity part + for (ip = 0; ip < 12; ip++) + { + syndromeIP = syndromeI ^ (1 << (11-ip)); + Golay_20_8_m_corr[syndromeIP][0] = i1; + Golay_20_8_m_corr[syndromeIP][1] = i2; + Golay_20_8_m_corr[syndromeIP][2] = 12 + ip; + } + } + + // single bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += Golay_20_8_m_H[20*ir + i1] << (11-ir); + } + + Golay_20_8_m_corr[syndromeI][0] = i1; + + for (ip1 = 0; ip1 < 12; ip1++) // 1 more bit flip in parity + { + syndromeIP1 = syndromeI ^ (1 << (11-ip1)); + Golay_20_8_m_corr[syndromeIP1][0] = i1; + Golay_20_8_m_corr[syndromeIP1][1] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2)); + Golay_20_8_m_corr[syndromeIP2][0] = i1; + Golay_20_8_m_corr[syndromeIP2][1] = 12 + ip1; + Golay_20_8_m_corr[syndromeIP2][2] = 12 + ip2; + } + } + } + + // no bit patterns (in message) -> all in parity + for (ip1 = 0; ip1 < 12; ip1++) // 1 bit flip in parity + { + syndromeIP1 = (1 << (11-ip1)); + Golay_20_8_m_corr[syndromeIP1][0] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2)); + Golay_20_8_m_corr[syndromeIP2][0] = 12 + ip1; + Golay_20_8_m_corr[syndromeIP2][1] = 12 + ip2; + + for (ip3 = ip2+1; ip3 < 12; ip3++) // 1 more bit flip in parity + { + syndromeIP3 = syndromeIP2 ^ (1 << (11-ip3)); + Golay_20_8_m_corr[syndromeIP3][0] = 12 + ip1; + Golay_20_8_m_corr[syndromeIP3][1] = 12 + ip2; + Golay_20_8_m_corr[syndromeIP3][2] = 12 + ip3; + } + } + } +} + +// Not very efficient but encode is used for unit testing only +void Golay_20_8_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 20); + + for (i = 0; i < 8; i++) + { + for (j = 0; j < 20; j++) + { + encodedBits[j] += origBits[i] * Golay_20_8_m_G[20*i + j]; + } + } + + for (i = 0; i < 20; i++) + { + encodedBits[i] %= 2; + } +} + +bool Golay_20_8_decode(unsigned char *rxBits) +{ + unsigned int syndromeI = 0; // syndrome index + int is = 0; + int i = 0; + + for (is = 0; is < 12; is++) + { + syndromeI += (((rxBits[0] * Golay_20_8_m_H[20*is + 0]) + + (rxBits[1] * Golay_20_8_m_H[20*is + 1]) + + (rxBits[2] * Golay_20_8_m_H[20*is + 2]) + + (rxBits[3] * Golay_20_8_m_H[20*is + 3]) + + (rxBits[4] * Golay_20_8_m_H[20*is + 4]) + + (rxBits[5] * Golay_20_8_m_H[20*is + 5]) + + (rxBits[6] * Golay_20_8_m_H[20*is + 6]) + + (rxBits[7] * Golay_20_8_m_H[20*is + 7]) + + (rxBits[8] * Golay_20_8_m_H[20*is + 8]) + + (rxBits[9] * Golay_20_8_m_H[20*is + 9]) + + (rxBits[10] * Golay_20_8_m_H[20*is + 10]) + + (rxBits[11] * Golay_20_8_m_H[20*is + 11]) + + (rxBits[12] * Golay_20_8_m_H[20*is + 12]) + + (rxBits[13] * Golay_20_8_m_H[20*is + 13]) + + (rxBits[14] * Golay_20_8_m_H[20*is + 14]) + + (rxBits[15] * Golay_20_8_m_H[20*is + 15]) + + (rxBits[16] * Golay_20_8_m_H[20*is + 16]) + + (rxBits[17] * Golay_20_8_m_H[20*is + 17]) + + (rxBits[18] * Golay_20_8_m_H[20*is + 18]) + + (rxBits[19] * Golay_20_8_m_H[20*is + 19])) % 2) << (11-is); + } + + if (syndromeI > 0) + { + i = 0; + + for (; i < 3; i++) + { + if (Golay_20_8_m_corr[syndromeI][i] == 0xFF) + { + break; + } + else + { + rxBits[Golay_20_8_m_corr[syndromeI][i]] ^= 1; // flip bit + } + } + + if (i == 0) + { + return false; + } + } + + return true; +} + + +// ======================================================================================== + + +void Golay_23_12_init() +{ + int i1 = 0, i2 = 0, i3 = 0, ir = 0, ip = 0; + int syndromeI = 0, syndromeIP = 0; + int ip1 = 0, ip2 = 0, ip3 = 0; + int syndromeIP1 = 0, syndromeIP2 = 0, syndromeIP3 = 0; + + memset(Golay_23_12_m_corr, 0xFF, 3*2048); + + for (i1 = 0; i1 < 12; i1++) + { + for (i2 = i1+1; i2 < 12; i2++) + { + for (i3 = i2+1; i3 < 12; i3++) + { + // 3 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 11; ir++) + { + syndromeI += ((Golay_23_12_m_H[23*ir + i1] + Golay_23_12_m_H[23*ir + i2] + Golay_23_12_m_H[23*ir + i3]) % 2) << (10-ir); + } + + Golay_23_12_m_corr[syndromeI][0] = i1; + Golay_23_12_m_corr[syndromeI][1] = i2; + Golay_23_12_m_corr[syndromeI][2] = i3; + } + + // 2 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 11; ir++) + { + syndromeI += ((Golay_23_12_m_H[23*ir + i1] + Golay_23_12_m_H[23*ir + i2]) % 2) << (10-ir); + } + + Golay_23_12_m_corr[syndromeI][0] = i1; + Golay_23_12_m_corr[syndromeI][1] = i2; + + // 1 possible bit flip left in the parity part + for (ip = 0; ip < 11; ip++) + { + syndromeIP = syndromeI ^ (1 << (10-ip)); + Golay_23_12_m_corr[syndromeIP][0] = i1; + Golay_23_12_m_corr[syndromeIP][1] = i2; + Golay_23_12_m_corr[syndromeIP][2] = 12 + ip; + } + } + + // single bit patterns + syndromeI = 0; + + for (ir = 0; ir < 11; ir++) + { + syndromeI += Golay_23_12_m_H[23*ir + i1] << (10-ir); + } + + Golay_23_12_m_corr[syndromeI][0] = i1; + + for (ip1 = 0; ip1 < 11; ip1++) // 1 more bit flip in parity + { + syndromeIP1 = syndromeI ^ (1 << (10-ip1)); + Golay_23_12_m_corr[syndromeIP1][0] = i1; + Golay_23_12_m_corr[syndromeIP1][1] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 11; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (10-ip2)); + Golay_23_12_m_corr[syndromeIP2][0] = i1; + Golay_23_12_m_corr[syndromeIP2][1] = 12 + ip1; + Golay_23_12_m_corr[syndromeIP2][2] = 12 + ip2; + } + } + } + + // no bit patterns (in message) -> all in parity + for (ip1 = 0; ip1 < 11; ip1++) // 1 bit flip in parity + { + syndromeIP1 = (1 << (10-ip1)); + Golay_23_12_m_corr[syndromeIP1][0] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 11; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (10-ip2)); + Golay_23_12_m_corr[syndromeIP2][0] = 12 + ip1; + Golay_23_12_m_corr[syndromeIP2][1] = 12 + ip2; + + for (ip3 = ip2+1; ip3 < 11; ip3++) // 1 more bit flip in parity + { + syndromeIP3 = syndromeIP2 ^ (1 << (10-ip3)); + Golay_23_12_m_corr[syndromeIP3][0] = 12 + ip1; + Golay_23_12_m_corr[syndromeIP3][1] = 12 + ip2; + Golay_23_12_m_corr[syndromeIP3][2] = 12 + ip3; + } + } + } +} + +// Not very efficient but encode is used for unit testing only +void Golay_23_12_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 23); + + for (i = 0; i < 12; i++) // orig bits + { + for (j = 0; j < 23; j++) // codeword bits + { + encodedBits[j] += origBits[i] * Golay_23_12_m_G[23*i + j]; + } + } + + for (i = 0; i < 23; i++) + { + encodedBits[i] %= 2; + } +} + +bool Golay_23_12_decode(unsigned char *rxBits) +{ + unsigned int syndromeI = 0; // syndrome index + int is = 0; + int i = 0; + + for (is = 0; is < 11; is++) + { + syndromeI += (((rxBits[0] * Golay_23_12_m_H[23*is + 0]) + + (rxBits[1] * Golay_23_12_m_H[23*is + 1]) + + (rxBits[2] * Golay_23_12_m_H[23*is + 2]) + + (rxBits[3] * Golay_23_12_m_H[23*is + 3]) + + (rxBits[4] * Golay_23_12_m_H[23*is + 4]) + + (rxBits[5] * Golay_23_12_m_H[23*is + 5]) + + (rxBits[6] * Golay_23_12_m_H[23*is + 6]) + + (rxBits[7] * Golay_23_12_m_H[23*is + 7]) + + (rxBits[8] * Golay_23_12_m_H[23*is + 8]) + + (rxBits[9] * Golay_23_12_m_H[23*is + 9]) + + (rxBits[10] * Golay_23_12_m_H[23*is + 10]) + + (rxBits[11] * Golay_23_12_m_H[23*is + 11]) + + (rxBits[12] * Golay_23_12_m_H[23*is + 12]) + + (rxBits[13] * Golay_23_12_m_H[23*is + 13]) + + (rxBits[14] * Golay_23_12_m_H[23*is + 14]) + + (rxBits[15] * Golay_23_12_m_H[23*is + 15]) + + (rxBits[16] * Golay_23_12_m_H[23*is + 16]) + + (rxBits[17] * Golay_23_12_m_H[23*is + 17]) + + (rxBits[18] * Golay_23_12_m_H[23*is + 18]) + + (rxBits[19] * Golay_23_12_m_H[23*is + 19]) + + (rxBits[20] * Golay_23_12_m_H[23*is + 20]) + + (rxBits[21] * Golay_23_12_m_H[23*is + 21]) + + (rxBits[22] * Golay_23_12_m_H[23*is + 22])) % 2) << (10-is); + } + + if (syndromeI > 0) + { + i = 0; + + for (; i < 3; i++) + { + if (Golay_23_12_m_corr[syndromeI][i] == 0xFF) + { + break; + } + else + { + rxBits[Golay_23_12_m_corr[syndromeI][i]] ^= 1; // flip bit + } + } + + if (i == 0) + { + return false; + } + } + + return true; +} + + +// ======================================================================================== + + +void Golay_24_12_init() +{ + int i1 = 0, i2 = 0, i3 = 0, ir = 0, ip = 0; + int syndromeI = 0, syndromeIP = 0; + int ip1 = 0, ip2 = 0, ip3 = 0; + int syndromeIP1 = 0, syndromeIP2 = 0, syndromeIP3 = 0; + + memset (Golay_24_12_m_corr, 0xFF, 3*4096); + + for (i1 = 0; i1 < 12; i1++) + { + for (i2 = i1+1; i2 < 12; i2++) + { + for (i3 = i2+1; i3 < 12; i3++) + { + // 3 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += ((Golay_24_12_m_H[24*ir + i1] + Golay_24_12_m_H[24*ir + i2] + Golay_24_12_m_H[24*ir + i3]) % 2) << (11-ir); + } + + Golay_24_12_m_corr[syndromeI][0] = i1; + Golay_24_12_m_corr[syndromeI][1] = i2; + Golay_24_12_m_corr[syndromeI][2] = i3; + } + + // 2 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += ((Golay_24_12_m_H[24*ir + i1] + Golay_24_12_m_H[24*ir + i2]) % 2) << (11-ir); + } + + Golay_24_12_m_corr[syndromeI][0] = i1; + Golay_24_12_m_corr[syndromeI][1] = i2; + + // 1 possible bit flip left in the parity part + for (ip = 0; ip < 12; ip++) + { + syndromeIP = syndromeI ^ (1 << (11-ip)); + Golay_24_12_m_corr[syndromeIP][0] = i1; + Golay_24_12_m_corr[syndromeIP][1] = i2; + Golay_24_12_m_corr[syndromeIP][2] = 12 + ip; + } + } + + // single bit patterns + syndromeI = 0; + + for (ir = 0; ir < 12; ir++) + { + syndromeI += Golay_24_12_m_H[24*ir + i1] << (11-ir); + } + + Golay_24_12_m_corr[syndromeI][0] = i1; + + for (ip1 = 0; ip1 < 12; ip1++) // 1 more bit flip in parity + { + syndromeIP1 = syndromeI ^ (1 << (11-ip1)); + Golay_24_12_m_corr[syndromeIP1][0] = i1; + Golay_24_12_m_corr[syndromeIP1][1] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2)); + Golay_24_12_m_corr[syndromeIP2][0] = i1; + Golay_24_12_m_corr[syndromeIP2][1] = 12 + ip1; + Golay_24_12_m_corr[syndromeIP2][2] = 12 + ip2; + } + } + } + + // no bit patterns (in message) -> all in parity + for (ip1 = 0; ip1 < 12; ip1++) // 1 bit flip in parity + { + syndromeIP1 = (1 << (11-ip1)); + Golay_24_12_m_corr[syndromeIP1][0] = 12 + ip1; + + for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2)); + Golay_24_12_m_corr[syndromeIP2][0] = 12 + ip1; + Golay_24_12_m_corr[syndromeIP2][1] = 12 + ip2; + + for (ip3 = ip2+1; ip3 < 12; ip3++) // 1 more bit flip in parity + { + syndromeIP3 = syndromeIP2 ^ (1 << (11-ip3)); + Golay_24_12_m_corr[syndromeIP3][0] = 12 + ip1; + Golay_24_12_m_corr[syndromeIP3][1] = 12 + ip2; + Golay_24_12_m_corr[syndromeIP3][2] = 12 + ip3; + } + } + } +} + +// Not very efficient but encode is used for unit testing only +void Golay_24_12_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 24); + + for (i = 0; i < 12; i++) + { + for (j = 0; j < 24; j++) + { + encodedBits[j] += origBits[i] * Golay_24_12_m_G[24*i + j]; + } + } + + for (i = 0; i < 24; i++) + { + encodedBits[i] %= 2; + } +} + +bool Golay_24_12_decode(unsigned char *rxBits) +{ + unsigned int syndromeI = 0; // syndrome index + int is = 0; + int i = 0; + + for (is = 0; is < 12; is++) + { + syndromeI += (((rxBits[0] * Golay_24_12_m_H[24*is + 0]) + + (rxBits[1] * Golay_24_12_m_H[24*is + 1]) + + (rxBits[2] * Golay_24_12_m_H[24*is + 2]) + + (rxBits[3] * Golay_24_12_m_H[24*is + 3]) + + (rxBits[4] * Golay_24_12_m_H[24*is + 4]) + + (rxBits[5] * Golay_24_12_m_H[24*is + 5]) + + (rxBits[6] * Golay_24_12_m_H[24*is + 6]) + + (rxBits[7] * Golay_24_12_m_H[24*is + 7]) + + (rxBits[8] * Golay_24_12_m_H[24*is + 8]) + + (rxBits[9] * Golay_24_12_m_H[24*is + 9]) + + (rxBits[10] * Golay_24_12_m_H[24*is + 10]) + + (rxBits[11] * Golay_24_12_m_H[24*is + 11]) + + (rxBits[12] * Golay_24_12_m_H[24*is + 12]) + + (rxBits[13] * Golay_24_12_m_H[24*is + 13]) + + (rxBits[14] * Golay_24_12_m_H[24*is + 14]) + + (rxBits[15] * Golay_24_12_m_H[24*is + 15]) + + (rxBits[16] * Golay_24_12_m_H[24*is + 16]) + + (rxBits[17] * Golay_24_12_m_H[24*is + 17]) + + (rxBits[18] * Golay_24_12_m_H[24*is + 18]) + + (rxBits[19] * Golay_24_12_m_H[24*is + 19]) + + (rxBits[20] * Golay_24_12_m_H[24*is + 20]) + + (rxBits[21] * Golay_24_12_m_H[24*is + 21]) + + (rxBits[22] * Golay_24_12_m_H[24*is + 22]) + + (rxBits[23] * Golay_24_12_m_H[24*is + 23])) % 2) << (11-is); + } + + if (syndromeI > 0) + { + i = 0; + + for (; i < 3; i++) + { + if (Golay_24_12_m_corr[syndromeI][i] == 0xFF) + { + break; + } + else + { + rxBits[Golay_24_12_m_corr[syndromeI][i]] ^= 1; // flip bit + } + } + + if (i == 0) + { + return false; + } + } + + return true; +} + + +// ======================================================================================== + + +void QR_16_7_6_init() +{ + int i1 = 0, i2 = 0, ir = 0, ip = 0; + int syndromeI = 0, syndromeIP = 0; + int ip1 = 0, ip2 = 0; + int syndromeIP1 = 0, syndromeIP2 = 0; + + memset(QR_16_7_6_m_corr, 0xFF, 2*512); + + for (i1 = 0; i1 < 7; i1++) + { + for (i2 = i1+1; i2 < 7; i2++) + { + // 2 bit patterns + syndromeI = 0; + + for (ir = 0; ir < 9; ir++) + { + syndromeI += ((QR_16_7_6_m_H[16*ir + i1] + QR_16_7_6_m_H[16*ir + i2]) % 2) << (8-ir); + } + + QR_16_7_6_m_corr[syndromeI][0] = i1; + QR_16_7_6_m_corr[syndromeI][1] = i2; + } + + // single bit patterns + syndromeI = 0; + + for (ir = 0; ir < 9; ir++) + { + syndromeI += QR_16_7_6_m_H[16*ir + i1] << (8-ir); + } + + QR_16_7_6_m_corr[syndromeI][0] = i1; + + // 1 possible bit flip left in the parity part + for (ip = 0; ip < 9; ip++) + { + syndromeIP = syndromeI ^ (1 << (8-ip)); + QR_16_7_6_m_corr[syndromeIP][0] = i1; + QR_16_7_6_m_corr[syndromeIP][1] = 7 + ip; + } + } + + // no bit patterns (in message) -> all in parity + for (ip1 = 0; ip1 < 9; ip1++) // 1 bit flip in parity + { + syndromeIP1 = (1 << (8-ip1)); + QR_16_7_6_m_corr[syndromeIP1][0] = 7 + ip1; + + for (ip2 = ip1+1; ip2 < 9; ip2++) // 1 more bit flip in parity + { + syndromeIP2 = syndromeIP1 ^ (1 << (8-ip2)); + QR_16_7_6_m_corr[syndromeIP2][0] = 7 + ip1; + QR_16_7_6_m_corr[syndromeIP2][1] = 7 + ip2; + } + } +} + +// Not very efficient but encode is used for unit testing only +void QR_16_7_6_encode(unsigned char *origBits, unsigned char *encodedBits) +{ + int i = 0, j = 0; + + memset(encodedBits, 0, 16); + + for (i = 0; i < 7; i++) + { + for (j = 0; j < 16; j++) + { + encodedBits[j] += origBits[i] * QR_16_7_6_m_G[16*i + j]; + } + } + + for (i = 0; i < 16; i++) + { + encodedBits[i] %= 2; + } +} + +bool QR_16_7_6_decode(unsigned char *rxBits) +{ + unsigned int syndromeI = 0; // syndrome index + int is = 0; + int i = 0; + + for (is = 0; is < 9; is++) + { + syndromeI += (((rxBits[0] * QR_16_7_6_m_H[16*is + 0]) + + (rxBits[1] * QR_16_7_6_m_H[16*is + 1]) + + (rxBits[2] * QR_16_7_6_m_H[16*is + 2]) + + (rxBits[3] * QR_16_7_6_m_H[16*is + 3]) + + (rxBits[4] * QR_16_7_6_m_H[16*is + 4]) + + (rxBits[5] * QR_16_7_6_m_H[16*is + 5]) + + (rxBits[6] * QR_16_7_6_m_H[16*is + 6]) + + (rxBits[7] * QR_16_7_6_m_H[16*is + 7]) + + (rxBits[8] * QR_16_7_6_m_H[16*is + 8]) + + (rxBits[9] * QR_16_7_6_m_H[16*is + 9]) + + (rxBits[10] * QR_16_7_6_m_H[16*is + 10]) + + (rxBits[11] * QR_16_7_6_m_H[16*is + 11]) + + (rxBits[12] * QR_16_7_6_m_H[16*is + 12]) + + (rxBits[13] * QR_16_7_6_m_H[16*is + 13]) + + (rxBits[14] * QR_16_7_6_m_H[16*is + 14]) + + (rxBits[15] * QR_16_7_6_m_H[16*is + 15])) % 2) << (8-is); + } + + if (syndromeI > 0) + { + i = 0; + + for (; i < 2; i++) + { + if (QR_16_7_6_m_corr[syndromeI][i] == 0xFF) + { + break; + } + else + { + rxBits[QR_16_7_6_m_corr[syndromeI][i]] ^= 1; // flip bit + } + } + + if (i == 0) + { + return false; + } + } + + return true; +} + + +// ======================================================================================== + + +/* This function init all FEC functions + * it must be called once at startup */ +void InitAllFecFunction(void) +{ + Hamming_7_4_init(); + Hamming_12_8_init(); + Hamming_13_9_init(); + Hamming_15_11_init(); + Hamming_16_11_4_init(); + Golay_20_8_init(); + Golay_23_12_init(); + Golay_24_12_init(); + QR_16_7_6_init(); +} /* End InitAllFEC() */ + + +/* End of file */ diff --git a/src/rs-12-9.c b/src/rs-12-9.c new file mode 100644 index 0000000..f64ac2d --- /dev/null +++ b/src/rs-12-9.c @@ -0,0 +1,316 @@ +/* + * This file is part of dmrshark. + * + * dmrshark 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. + * + * dmrshark 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. + * + * You should have received a copy of the GNU General Public License + * along with dmrshark. If not, see . +**/ + +// Reed-Solomon (12,9) error-correction coding. +// +// As there are 3 checksum bytes, we can only correct 1 erroneous byte. +// +// Based on the code by Henry Minsky (hqm@alum.mit.edu), http://rscode.sourceforge.net +// Berlekamp-Peterson and Berlekamp-Massey Algorithms for error-location are +// from Cain, Clark, "Error-Correction Coding For Digital Communications", pp. 205. + +//#include DEFAULTCONFIG + +#include "dsd.h" + +//#include + +#include +#include + +typedef struct { + uint8_t error_locations[256]; + uint8_t errors_num; +} rs_12_9_roots_t; + +// See DMR AI. spec. page 138. +static uint8_t rs_12_9_galois_exp_table[256] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1D, 0x3A, 0x74, 0xE8, 0xCD, 0x87, 0x13, 0x26, + 0x4C, 0x98, 0x2D, 0x5A, 0xB4, 0x75, 0xEA, 0xC9, 0x8F, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, + 0x9D, 0x27, 0x4E, 0x9C, 0x25, 0x4A, 0x94, 0x35, 0x6A, 0xD4, 0xB5, 0x77, 0xEE, 0xC1, 0x9F, 0x23, + 0x46, 0x8C, 0x05, 0x0A, 0x14, 0x28, 0x50, 0xA0, 0x5D, 0xBA, 0x69, 0xD2, 0xB9, 0x6F, 0xDE, 0xA1, + 0x5F, 0xBE, 0x61, 0xC2, 0x99, 0x2F, 0x5E, 0xBC, 0x65, 0xCA, 0x89, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, + 0xFD, 0xE7, 0xD3, 0xBB, 0x6B, 0xD6, 0xB1, 0x7F, 0xFE, 0xE1, 0xDF, 0xA3, 0x5B, 0xB6, 0x71, 0xE2, + 0xD9, 0xAF, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0D, 0x1A, 0x34, 0x68, 0xD0, 0xBD, 0x67, 0xCE, + 0x81, 0x1F, 0x3E, 0x7C, 0xF8, 0xED, 0xC7, 0x93, 0x3B, 0x76, 0xEC, 0xC5, 0x97, 0x33, 0x66, 0xCC, + 0x85, 0x17, 0x2E, 0x5C, 0xB8, 0x6D, 0xDA, 0xA9, 0x4F, 0x9E, 0x21, 0x42, 0x84, 0x15, 0x2A, 0x54, + 0xA8, 0x4D, 0x9A, 0x29, 0x52, 0xA4, 0x55, 0xAA, 0x49, 0x92, 0x39, 0x72, 0xE4, 0xD5, 0xB7, 0x73, + 0xE6, 0xD1, 0xBF, 0x63, 0xC6, 0x91, 0x3F, 0x7E, 0xFC, 0xE5, 0xD7, 0xB3, 0x7B, 0xF6, 0xF1, 0xFF, + 0xE3, 0xDB, 0xAB, 0x4B, 0x96, 0x31, 0x62, 0xC4, 0x95, 0x37, 0x6E, 0xDC, 0xA5, 0x57, 0xAE, 0x41, + 0x82, 0x19, 0x32, 0x64, 0xC8, 0x8D, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xDD, 0xA7, 0x53, 0xA6, + 0x51, 0xA2, 0x59, 0xB2, 0x79, 0xF2, 0xF9, 0xEF, 0xC3, 0x9B, 0x2B, 0x56, 0xAC, 0x45, 0x8A, 0x09, + 0x12, 0x24, 0x48, 0x90, 0x3D, 0x7A, 0xF4, 0xF5, 0xF7, 0xF3, 0xFB, 0xEB, 0xCB, 0x8B, 0x0B, 0x16, + 0x2C, 0x58, 0xB0, 0x7D, 0xFA, 0xE9, 0xCF, 0x83, 0x1B, 0x36, 0x6C, 0xD8, 0xAD, 0x47, 0x8E, 0x01, +}; + +// See DMR AI. spec. page 138. +static uint8_t rs_12_9_galois_log_table[256] = { + 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, + 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, + 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, + 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, + 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, + 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, + 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, + 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, + 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, + 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, + 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, + 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, + 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, + 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, + 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, + 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175 +}; + +static uint8_t rs_12_9_galois_exp_table_get(uint8_t pos) { + return rs_12_9_galois_exp_table[pos]; +} + +static uint8_t rs_12_9_galois_multiplication(uint8_t a, uint8_t b) { + if (a == 0 || b == 0) + return 0; + + return rs_12_9_galois_exp_table[(rs_12_9_galois_log_table[a] + rs_12_9_galois_log_table[b]) % 255]; +} + +static uint8_t rs_12_9_galois_inv(uint8_t elt) { + return rs_12_9_galois_exp_table[255-rs_12_9_galois_log_table[elt]]; +} + +// Multiply by z (shift right by 1). +static void rs_12_9_multiply_poly_z(rs_12_9_poly_t *poly) { + uint8_t i; + + for (i = RS_12_9_POLY_MAXDEG-1; i > 0; i--) + poly->data[i] = poly->data[i-1]; + poly->data[0] = 0; +} + +static void rs_12_9_multiplicate_polys(uint8_t dst[], rs_12_9_poly_t *p1, rs_12_9_poly_t *p2) { + uint8_t i; + int8_t j; + int8_t tmp1[RS_12_9_POLY_MAXDEG*2]; + + for (i = 0; i < RS_12_9_POLY_MAXDEG*2; i++) + dst[i] = 0; + + for (i = 0; i < RS_12_9_POLY_MAXDEG; i++) { + for(j = RS_12_9_POLY_MAXDEG; j < (RS_12_9_POLY_MAXDEG*2); j++) + tmp1[j] = 0; + + // Scale tmp1 by p1[i] + for(j = 0; j < RS_12_9_POLY_MAXDEG; j++) + tmp1[j] = rs_12_9_galois_multiplication(p2->data[j], p1->data[i]); + + // Shift (multiply) tmp1 right by i + for (j = (RS_12_9_POLY_MAXDEG*2)-1; j >= i; j--) + tmp1[j] = tmp1[j-i]; + for (j = 0; j < i; j++) + tmp1[j] = 0; + + // Add into partial product. + for (j = 0; j < (RS_12_9_POLY_MAXDEG*2); j++) + dst[j] ^= tmp1[j]; + } +} + +// Computes the combined erasure/error evaluator polynomial (error_locator_poly*syndrome mod z^4) +static void rs_12_9_calc_error_evaluator_poly(rs_12_9_poly_t *error_locator_poly, rs_12_9_poly_t *syndrome, rs_12_9_poly_t *error_evaluator_poly) { + uint8_t i; + uint8_t product[RS_12_9_POLY_MAXDEG*2]; + + rs_12_9_multiplicate_polys(product, error_locator_poly, syndrome); + for (i = 0; i < RS_12_9_CHECKSUMSIZE; i++) + error_evaluator_poly->data[i] = product[i]; + for (; i < RS_12_9_POLY_MAXDEG; i++) + error_evaluator_poly->data[i] = 0; +} + +static uint8_t rs_12_9_compute_discrepancy(rs_12_9_poly_t *error_locator_poly, rs_12_9_poly_t *syndrome, uint8_t L, uint8_t n) { + uint8_t i; + uint8_t sum = 0; + + for (i = 0; i <= L; i++) + sum ^= rs_12_9_galois_multiplication(error_locator_poly->data[i], syndrome->data[n-i]); + + return sum; +} + +// This finds the coefficients of the error locator polynomial, and then calculates +// the error evaluator polynomial using the Berlekamp-Massey algorithm. +// From Cain, Clark, "Error-Correction Coding For Digital Communications", pp. 216. +static void rs_12_9_calculate(rs_12_9_poly_t *syndrome, rs_12_9_poly_t *error_locator_poly, rs_12_9_poly_t *error_evaluator_poly) { + uint8_t n; + uint8_t L = 0; + uint8_t L2; + int8_t k; + uint8_t d; + uint8_t i; + uint8_t psi2[RS_12_9_POLY_MAXDEG]; + rs_12_9_poly_t D = { .data = { 0, 1, 0, } }; + + k = -1; + memset(error_locator_poly, 0, sizeof(rs_12_9_poly_t)); + error_locator_poly->data[0] = 1; + + for (n = 0; n < RS_12_9_CHECKSUMSIZE; n++) { + d = rs_12_9_compute_discrepancy(error_locator_poly, syndrome, L, n); + + if (d != 0) { + // psi2 = error_locator_poly - d*D + for (i = 0; i < RS_12_9_POLY_MAXDEG; i++) + psi2[i] = error_locator_poly->data[i] ^ rs_12_9_galois_multiplication(d, D.data[i]); + + if (L < (n-k)) { + L2 = n-k; + k = n-L; + + for (i = 0; i < RS_12_9_POLY_MAXDEG; i++) + D.data[i] = rs_12_9_galois_multiplication(error_locator_poly->data[i], rs_12_9_galois_inv(d)); + + L = L2; + } + + // error_locator_poly = psi2 + for (i = 0; i < RS_12_9_POLY_MAXDEG; i++) + error_locator_poly->data[i] = psi2[i]; + } + + rs_12_9_multiply_poly_z(&D); + } + + rs_12_9_calc_error_evaluator_poly(error_locator_poly, syndrome, error_evaluator_poly); +} + +// The error-locator polynomial's roots are found by looking for the values of a^n where +// evaluating the polynomial yields zero (evaluating rs_12_9_error_locator_poly at +// successive values of alpha (Chien's search)). +static rs_12_9_roots_t *rs_12_9_find_roots(rs_12_9_poly_t *error_locator_poly) { + static rs_12_9_roots_t roots; + uint8_t sum; + uint16_t r; + uint8_t k; + + memset(&roots, 0, sizeof(rs_12_9_roots_t)); + + for (r = 1; r < 256; r++) { + sum = 0; + // Evaluate rs_12_9_error_locator_poly at r + for (k = 0; k < RS_12_9_CHECKSUMSIZE+1; k++) + sum ^= rs_12_9_galois_multiplication(rs_12_9_galois_exp_table_get((k*r) % 255), error_locator_poly->data[k]); + + if (sum == 0) + roots.error_locations[roots.errors_num++] = (255-r); + } + return &roots; +} + +void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome) { + uint8_t i, j; + + syndrome->data[0] = syndrome->data[1] = syndrome->data[2] = 0; + + for (j = 0; j < 3; j++) { + for (i = 0; i < sizeof(rs_12_9_codeword_t); i++) + syndrome->data[j] = codeword->data[i] ^ rs_12_9_galois_multiplication(rs_12_9_galois_exp_table_get(j+1), syndrome->data[j]); + } +} + +// Returns 1 if syndrome differs from all zeroes. +uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome) { + uint8_t i; + + for (i = 0; i < 3; i++) { + if (syndrome->data[i] != 0) + return 1; + } + + return 0; +} +// Returns 1 if errors have been found and corrected, returns 0 if +// no errors found or errors can't be corrected. +rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found) { + uint8_t r; + uint8_t i; + uint8_t j; + uint8_t err; + rs_12_9_poly_t error_locator_poly; + rs_12_9_poly_t error_evaluator_poly; + rs_12_9_roots_t *roots; + uint8_t num, denom; + + rs_12_9_calculate(syndrome, &error_locator_poly, &error_evaluator_poly); + roots = rs_12_9_find_roots(&error_locator_poly); + *errors_found = roots->errors_num; + + if (roots->errors_num == 0) + return RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND; + + // Error correction is done using the error-evaluator equation on pp 207. + if (roots->errors_num > 0 && roots->errors_num <= RS_12_9_CHECKSUMSIZE) { + // First check for illegal error locations. + for (r = 0; r < roots->errors_num; r++) { + if (roots->error_locations[r] >= RS_12_9_DATASIZE+RS_12_9_CHECKSUMSIZE) + return RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CANT_BE_CORRECTED; + } + + // Evaluates rs_12_9_error_evaluator_poly/rs_12_9_error_locator_poly' at the roots + // alpha^(-i) for error locs i. + for (r = 0; r < roots->errors_num; r++) { + i = roots->error_locations[r]; + + // Evaluate rs_12_9_error_evaluator_poly at alpha^(-i) + num = 0; + for (j = 0; j < RS_12_9_POLY_MAXDEG; j++) + num ^= rs_12_9_galois_multiplication(error_evaluator_poly.data[j], rs_12_9_galois_exp_table_get(((255-i)*j) % 255)); + + // Evaluate rs_12_9_error_evaluator_poly' (derivative) at alpha^(-i). All odd powers disappear. + denom = 0; + for (j = 1; j < RS_12_9_POLY_MAXDEG; j += 2) + denom ^= rs_12_9_galois_multiplication(error_locator_poly.data[j], rs_12_9_galois_exp_table_get(((255-i)*(j-1)) % 255)); + + err = rs_12_9_galois_multiplication(num, rs_12_9_galois_inv(denom)); + //console_log(LOGLEVEL_CODING LOGLEVEL_DEBUG " rs (12,9): error magnitude %#x at byte loc. %u\n", err, sizeof(rs_12_9_codeword_t)-i); + + codeword->data[sizeof(rs_12_9_codeword_t)-i-1] ^= err; + } + return RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CORRECTED; + } + + return RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CANT_BE_CORRECTED; +} + +// Simulates an LFSR with the generator polynomial and calculates checksum bytes for the given data. +rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword) { + // See DMR AI. spec. page 136 for these coefficients. + static uint8_t genpoly[] = { 0x40, 0x38, 0x0e, 0x01 }; + static rs_12_9_checksum_t rs_12_9_checksum; + uint8_t i; + uint8_t feedback; + + rs_12_9_checksum.bytes[0] = rs_12_9_checksum.bytes[1] = rs_12_9_checksum.bytes[2] = 0; + + for (i = 0; i < 9; i++) { + feedback = codeword->data[i] ^ rs_12_9_checksum.bytes[0]; + + rs_12_9_checksum.bytes[0] = rs_12_9_checksum.bytes[1] ^ rs_12_9_galois_multiplication(genpoly[2], feedback); + rs_12_9_checksum.bytes[1] = rs_12_9_checksum.bytes[2] ^ rs_12_9_galois_multiplication(genpoly[1], feedback); + rs_12_9_checksum.bytes[2] = rs_12_9_galois_multiplication(genpoly[0], feedback); + } + return &rs_12_9_checksum; +}