mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-30 12:30:20 -04:00 
			
		
		
		
	
		
			
	
	
		
			358 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			358 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||
|  | // Copyright (C) 2020 Edouard Griffiths, F4EXB                                   //
 | ||
|  | //                                                                               //
 | ||
|  | // Inspired by: https://github.com/myriadrf/LoRa-SDR                             //
 | ||
|  | //                                                                               //
 | ||
|  | // 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                  //
 | ||
|  | // (at your option) any later version.                                           //
 | ||
|  | //                                                                               //
 | ||
|  | // 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 <http://www.gnu.org/licenses/>.          //
 | ||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||
|  | 
 | ||
|  | #ifndef INCLUDE_CHIRPCHATDEMODDECODERLORA_H
 | ||
|  | #define INCLUDE_CHIRPCHATDEMODDECODERLORA_H
 | ||
|  | 
 | ||
|  | #include <vector>
 | ||
|  | #include <QByteArray>
 | ||
|  | 
 | ||
|  | class ChirpChatDemodDecoderLoRa | ||
|  | { | ||
|  | public: | ||
|  |     enum ParityStatus | ||
|  |     { | ||
|  |         ParityUndefined, | ||
|  |         ParityError, | ||
|  |         ParityCorrected, | ||
|  |         ParityOK | ||
|  |     }; | ||
|  | 
 | ||
|  |     static void decodeBytes( | ||
|  |         QByteArray& bytes, | ||
|  |         const std::vector<unsigned short>& inSymbols, | ||
|  |         unsigned int nbSymbolBits, | ||
|  |         bool hasHeader, | ||
|  |         bool& hasCRC, | ||
|  |         unsigned int& nbParityBits, | ||
|  |         unsigned int& packetLength, | ||
|  |         bool& earlyEOM, | ||
|  |         int& headerParityStatus, | ||
|  |         bool& headerCRCStatus, | ||
|  |         int& payloadParityStatus, | ||
|  |         bool& payloadCRCStatus | ||
|  |     ); | ||
|  | 
 | ||
|  |     static void getCodingMetrics( | ||
|  |         unsigned int nbSymbolBits, | ||
|  |         unsigned int nbParityBits, | ||
|  |         unsigned int packetLength, | ||
|  |         bool hasHeader, | ||
|  |         bool hasCRC, | ||
|  |         unsigned int& numSymbols, | ||
|  |         unsigned int& numCodewords | ||
|  |     ); | ||
|  | 
 | ||
|  | private: | ||
|  |     static void decodeHeader( | ||
|  |         const std::vector<unsigned short>& inSymbols, | ||
|  |         unsigned int nbSymbolBits, | ||
|  |         bool& hasCRC, | ||
|  |         unsigned int& nbParityBits, | ||
|  |         unsigned int& packetLength, | ||
|  |         int& headerParityStatus, | ||
|  |         bool& headerCRCStatus | ||
|  |     ); | ||
|  | 
 | ||
|  |     static const unsigned int headerParityBits = 4; | ||
|  |     static const unsigned int headerSymbols = 8; | ||
|  |     static const unsigned int headerCodewords = 5; | ||
|  | 
 | ||
|  |     /***********************************************************************
 | ||
|  |      * Round functions | ||
|  |      **********************************************************************/ | ||
|  |     static inline unsigned roundUp(unsigned num, unsigned factor) | ||
|  |     { | ||
|  |         return ((num + factor - 1) / factor) * factor; | ||
|  |     } | ||
|  | 
 | ||
|  |     /***********************************************************************
 | ||
|  |      *  https://en.wikipedia.org/wiki/Gray_code
 | ||
|  |      **********************************************************************/ | ||
|  | 
 | ||
|  |     /*
 | ||
|  |     * This function converts an unsigned binary | ||
|  |     * number to reflected binary Gray code. | ||
|  |     * | ||
|  |     * The operator >> is shift right. The operator ^ is exclusive or. | ||
|  |     */ | ||
|  |     static inline unsigned short binaryToGray16(unsigned short num) | ||
|  |     { | ||
|  |         return num ^ (num >> 1); | ||
|  |     } | ||
|  | 
 | ||
|  |     /***********************************************************************
 | ||
|  |      * Diagonal deinterleaver | ||
|  |      **********************************************************************/ | ||
|  |     static inline void diagonalDeinterleaveSx( | ||
|  |         const uint16_t *symbols, | ||
|  |         const unsigned int numSymbols, | ||
|  |         uint8_t *codewords, | ||
|  |         const unsigned int nbSymbolBits, | ||
|  |         const unsigned int nbParityBits) | ||
|  |     { | ||
|  |         for (unsigned int x = 0; x < numSymbols / (4 + nbParityBits); x++) | ||
|  |         { | ||
|  |             const unsigned int cwOff = x*nbSymbolBits; | ||
|  |             const unsigned int symOff = x*(4 + nbParityBits); | ||
|  | 
 | ||
|  |             for (unsigned int k = 0; k < 4 + nbParityBits; k++) | ||
|  |             { | ||
|  |                 for (unsigned int m = 0; m < nbSymbolBits; m++) | ||
|  |                 { | ||
|  |                     const unsigned int i = (m + k) % nbSymbolBits; | ||
|  |                     const auto bit = (symbols[symOff + k] >> m) & 0x1; | ||
|  |                     codewords[cwOff + i] |= (bit << k); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /***********************************************************************
 | ||
|  |      *  Whitening generator reverse engineered from Sx1272 data stream. | ||
|  |      *  Same as above but using the actual interleaved LFSRs. | ||
|  |      **********************************************************************/ | ||
|  |     static inline void Sx1272ComputeWhiteningLfsr(uint8_t *buffer, uint16_t bufferSize, const int bitOfs, const unsigned int nbParityBits) | ||
|  |     { | ||
|  |         static const uint64_t seed1[2] = {0x6572D100E85C2EFF,0xE85C2EFFFFFFFFFF};   // lfsr start values
 | ||
|  |         static const uint64_t seed2[2] = {0x05121100F8ECFEEF,0xF8ECFEEFEFEFEFEF};   // lfsr start values for single parity mode (1 == nbParityBits)
 | ||
|  |         const uint8_t m = 0xff >> (4 - nbParityBits); | ||
|  |         uint64_t r[2] = {(1 == nbParityBits)?seed2[0]:seed1[0],(1 == nbParityBits)?seed2[1]:seed1[1]}; | ||
|  |         int i,j; | ||
|  | 
 | ||
|  |         for (i = 0; i < bitOfs;i++) | ||
|  |         { | ||
|  |             r[i & 1] = (r[i & 1] >> 8) | (((r[i & 1] >> 32) ^ (r[i & 1] >> 24) ^ (r[i & 1] >> 16) ^ r[i & 1]) << 56);   // poly: 0x1D
 | ||
|  |         } | ||
|  | 
 | ||
|  |         for (j = 0; j < bufferSize; j++,i++) | ||
|  |         { | ||
|  |             buffer[j] ^= r[i & 1] & m; | ||
|  |             r[i & 1] = (r[i & 1] >> 8) | (((r[i & 1] >> 32) ^ (r[i & 1] >> 24) ^ (r[i & 1] >> 16) ^ r[i & 1]) << 56); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /***********************************************************************
 | ||
|  |      * Decode 8 bits into a 4 bit word with single bit correction. | ||
|  |      * Non standard version used in sx1272. | ||
|  |      * Set error to true when a parity error was detected | ||
|  |      * Set bad to true when the result could not be corrected | ||
|  |      **********************************************************************/ | ||
|  |     static inline unsigned char decodeHamming84sx(const unsigned char b, bool &error, bool &bad) | ||
|  |     { | ||
|  |         auto b0 = (b >> 0) & 0x1; | ||
|  |         auto b1 = (b >> 1) & 0x1; | ||
|  |         auto b2 = (b >> 2) & 0x1; | ||
|  |         auto b3 = (b >> 3) & 0x1; | ||
|  |         auto b4 = (b >> 4) & 0x1; | ||
|  |         auto b5 = (b >> 5) & 0x1; | ||
|  |         auto b6 = (b >> 6) & 0x1; | ||
|  |         auto b7 = (b >> 7) & 0x1; | ||
|  | 
 | ||
|  |         auto p0 = (b0 ^ b1 ^ b2 ^ b4); | ||
|  |         auto p1 = (b1 ^ b2 ^ b3 ^ b5); | ||
|  |         auto p2 = (b0 ^ b1 ^ b3 ^ b6); | ||
|  |         auto p3 = (b0 ^ b2 ^ b3 ^ b7); | ||
|  | 
 | ||
|  |         auto parity = (p0 << 0) | (p1 << 1) | (p2 << 2) | (p3 << 3); | ||
|  |         if (parity != 0) error = true; | ||
|  |         switch (parity & 0xf) | ||
|  |         { | ||
|  |             case 0xD: return (b ^ 1) & 0xf; | ||
|  |             case 0x7: return (b ^ 2) & 0xf; | ||
|  |             case 0xB: return (b ^ 4) & 0xf; | ||
|  |             case 0xE: return (b ^ 8) & 0xf; | ||
|  |             case 0x0: | ||
|  |             case 0x1: | ||
|  |             case 0x2: | ||
|  |             case 0x4: | ||
|  |             case 0x8: return b & 0xf; | ||
|  |             default: bad = true; return b & 0xf; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /***********************************************************************
 | ||
|  |      * Simple 8-bit checksum routine | ||
|  |      **********************************************************************/ | ||
|  |     static inline uint8_t checksum8(const uint8_t *p, const size_t len) | ||
|  |     { | ||
|  |         uint8_t acc = 0; | ||
|  | 
 | ||
|  |         for (size_t i = 0; i < len; i++) | ||
|  |         { | ||
|  |             acc = (acc >> 1) + ((acc & 0x1) << 7); //rotate
 | ||
|  |             acc += p[i]; //add
 | ||
|  |         } | ||
|  | 
 | ||
|  |         return acc; | ||
|  |     } | ||
|  | 
 | ||
|  |     static inline uint8_t headerChecksum(const uint8_t *h) | ||
|  |     { | ||
|  |         auto a0 = (h[0] >> 4) & 0x1; | ||
|  |         auto a1 = (h[0] >> 5) & 0x1; | ||
|  |         auto a2 = (h[0] >> 6) & 0x1; | ||
|  |         auto a3 = (h[0] >> 7) & 0x1; | ||
|  | 
 | ||
|  |         auto b0 = (h[0] >> 0) & 0x1; | ||
|  |         auto b1 = (h[0] >> 1) & 0x1; | ||
|  |         auto b2 = (h[0] >> 2) & 0x1; | ||
|  |         auto b3 = (h[0] >> 3) & 0x1; | ||
|  | 
 | ||
|  |         auto c0 = (h[1] >> 0) & 0x1; | ||
|  |         auto c1 = (h[1] >> 1) & 0x1; | ||
|  |         auto c2 = (h[1] >> 2) & 0x1; | ||
|  |         auto c3 = (h[1] >> 3) & 0x1; | ||
|  | 
 | ||
|  |         uint8_t res; | ||
|  |         res = (a0 ^ a1 ^ a2 ^ a3) << 4; | ||
|  |         res |= (a3 ^ b1 ^ b2 ^ b3 ^ c0) << 3; | ||
|  |         res |= (a2 ^ b0 ^ b3 ^ c1 ^ c3) << 2; | ||
|  |         res |= (a1 ^ b0 ^ b2 ^ c0 ^ c1 ^ c2) << 1; | ||
|  |         res |= a0 ^ b1 ^ c0 ^ c1 ^ c2 ^ c3; | ||
|  | 
 | ||
|  |         return res; | ||
|  |     } | ||
|  | 
 | ||
|  |     /***********************************************************************
 | ||
|  |      * Check parity for 5/4 code. | ||
|  |      * return true if parity is valid. | ||
|  |      **********************************************************************/ | ||
|  |     static inline unsigned char checkParity54(const unsigned char b, bool &error) | ||
|  |     { | ||
|  |         auto x = b ^ (b >> 2); | ||
|  |         x = x ^ (x >> 1) ^ (b >> 4); | ||
|  | 
 | ||
|  |         if (x & 1) { | ||
|  |             error = true; | ||
|  |         } | ||
|  | 
 | ||
|  |         return b & 0xf; | ||
|  |     } | ||
|  | 
 | ||
|  |     /***********************************************************************
 | ||
|  |     * Check parity for 6/4 code. | ||
|  |     * return true if parity is valid. | ||
|  |     **********************************************************************/ | ||
|  |     static inline unsigned char checkParity64(const unsigned char b, bool &error) | ||
|  |     { | ||
|  |         auto x = b ^ (b >> 1) ^ (b >> 2); | ||
|  |         auto y = x ^ b ^ (b >> 3); | ||
|  |         x ^= b >> 4; | ||
|  |         y ^= b >> 5; | ||
|  | 
 | ||
|  |         if ((x | y) & 1) { | ||
|  |             error = true; | ||
|  |         } | ||
|  | 
 | ||
|  |         return b & 0xf; | ||
|  |     } | ||
|  | 
 | ||
|  |     /***********************************************************************
 | ||
|  |      * Decode 7 bits into a 4 bit word with single bit correction. | ||
|  |      * Non standard version used in sx1272. | ||
|  |      * Set error to true when a parity error was detected | ||
|  |      * Non correctable errors are indistinguishable from single or no errors | ||
|  |      * therefore no 'bad' variable is proposed | ||
|  |      **********************************************************************/ | ||
|  |     static inline unsigned char decodeHamming74sx(const unsigned char b, bool &error) | ||
|  |     { | ||
|  |         auto b0 = (b >> 0) & 0x1; | ||
|  |         auto b1 = (b >> 1) & 0x1; | ||
|  |         auto b2 = (b >> 2) & 0x1; | ||
|  |         auto b3 = (b >> 3) & 0x1; | ||
|  |         auto b4 = (b >> 4) & 0x1; | ||
|  |         auto b5 = (b >> 5) & 0x1; | ||
|  |         auto b6 = (b >> 6) & 0x1; | ||
|  | 
 | ||
|  |         auto p0 = (b0 ^ b1 ^ b2 ^ b4); | ||
|  |         auto p1 = (b1 ^ b2 ^ b3 ^ b5); | ||
|  |         auto p2 = (b0 ^ b1 ^ b3 ^ b6); | ||
|  | 
 | ||
|  |         auto parity = (p0 << 0) | (p1 << 1) | (p2 << 2); | ||
|  | 
 | ||
|  |         if (parity != 0) { | ||
|  |             error = true; | ||
|  |         } | ||
|  | 
 | ||
|  |         switch (parity) | ||
|  |         { | ||
|  |             case 0x5: return (b ^ 1) & 0xf; | ||
|  |             case 0x7: return (b ^ 2) & 0xf; | ||
|  |             case 0x3: return (b ^ 4) & 0xf; | ||
|  |             case 0x6: return (b ^ 8) & 0xf; | ||
|  |             case 0x0: | ||
|  |             case 0x1: | ||
|  |             case 0x2: | ||
|  |             case 0x4: break; | ||
|  |         } | ||
|  | 
 | ||
|  |         return b & 0xf; | ||
|  |     } | ||
|  | 
 | ||
|  |     /***********************************************************************
 | ||
|  |      *  CRC reverse engineered from Sx1272 data stream. | ||
|  |      *  Modified CCITT crc with masking of the output with an 8bit lfsr | ||
|  |      **********************************************************************/ | ||
|  |     static inline uint16_t crc16sx(uint16_t crc, const uint16_t poly) | ||
|  |     { | ||
|  |         for (int i = 0; i < 8; i++) | ||
|  |         { | ||
|  |             if (crc & 0x8000) { | ||
|  |                 crc = (crc << 1) ^ poly; | ||
|  |             } else { | ||
|  |                 crc <<= 1; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         return crc; | ||
|  |     } | ||
|  | 
 | ||
|  |     static inline uint8_t xsum8(uint8_t t) | ||
|  |     { | ||
|  |         t ^= t >> 4; | ||
|  |         t ^= t >> 2; | ||
|  |         t ^= t >> 1; | ||
|  | 
 | ||
|  |         return (t & 1); | ||
|  |     } | ||
|  | 
 | ||
|  |     static inline uint16_t sx1272DataChecksum(const uint8_t *data, int length) | ||
|  |     { | ||
|  |         uint16_t res = 0; | ||
|  |         uint8_t v = 0xff; | ||
|  |         uint16_t crc = 0; | ||
|  | 
 | ||
|  |         for (int i = 0; i < length; i++) | ||
|  |         { | ||
|  |             crc = crc16sx(res, 0x1021); | ||
|  |             v = xsum8(v & 0xB8) | (v << 1); | ||
|  |             res = crc ^ data[i]; | ||
|  |         } | ||
|  | 
 | ||
|  |         res ^= v; | ||
|  |         v = xsum8(v & 0xB8) | (v << 1); | ||
|  |         res ^= v << 8; | ||
|  | 
 | ||
|  |         return res; | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | #endif // INCLUDE_CHIRPCHATDEMODDECODERLORA_H
 |