esp32-ogn-tracker/utils/ldpc.h

426 wiersze
17 KiB
C++

#ifndef __LDPC_H__
#define __LDPC_H__
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <limits>
#include "bitcount.h"
#ifndef __AVR__
// #include <stdio.h>
#include <math.h>
#endif
#ifdef __AVR__
#include <avr/pgmspace.h>
#endif
// extern const uint32_t LDPC_ParityGen_n208k160[48][5];
// extern const uint32_t LDPC_ParityCheck_n208k160[48][7];
// extern const uint8_t LDPC_ParityCheckIndex_n208k160[48][24];
// extern const uint8_t LDPC_BitWeight_n208k160[208];
#ifdef WITH_PPM
extern const uint32_t LDPC_ParityGen_n354k160[194][5];
extern const uint32_t LDPC_ParityCheck_n354k160[194][12];
#endif
#ifdef __AVR__
// encode Parity from Data: Data is 5x 32-bit words = 160 bits, Parity is 1.5x 32-bit word = 48 bits
void LDPC_Encode(const uint32_t *Data, uint32_t *Parity, const uint32_t ParityGen[48][5]);
void LDPC_Encode(const uint32_t *Data, uint32_t *Parity);
// encode Parity from Data: Data is 20 bytes = 160 bits, Parity is 6 bytes = 48 bits
void LDPC_Encode(const uint8_t *Data, uint8_t *Parity, const uint32_t ParityGen[48][5]);
void LDPC_Encode(const uint8_t *Data, uint8_t *Parity);
void LDPC_Encode( uint8_t *Data);
// check Data against Parity (run 48 parity checks) - return number of failed checks
uint8_t LDPC_Check(const uint8_t *Data); // 20 data bytes followed by 6 parity bytes
uint8_t LDPC_Check(const uint32_t *Packet);
#else // if not 8-bit AVR
void LDPC_Encode(const uint8_t *Data, uint8_t *Parity, const uint32_t ParityGen[48][5]);
void LDPC_Encode(const uint8_t *Data, uint8_t *Parity);
void LDPC_Encode( uint8_t *Data);
// encode Parity from Data: Data is 5x 32-bit words = 160 bits, Parity is 1.5x 32-bit word = 48 bits
// void LDPC_Encode(const uint32_t *Data, uint32_t *Parity, const uint32_t ParityGen[48][5]);
// void LDPC_Encode(const uint32_t *Data, uint32_t *Parity, uint8_t DataWords, uint8_t Checks, const uint32_t *ParityGen);
// inline void LDPC_Encode(const uint32_t *Data, uint32_t *Parity) { LDPC_Encode(Data, Parity, 5, 48, (uint32_t *)LDPC_ParityGen_n208k160); }
// inline void LDPC_Encode( uint32_t *Data) { LDPC_Encode(Data, Data+5, 5, 48, (uint32_t *)LDPC_ParityGen_n208k160); }
// inline void LDPC_Encode_n394k160(const uint32_t *Data, uint32_t *Parity) { LDPC_Encode(Data, Parity, 5, 194, (uint32_t *)LDPC_ParityGen_n354k160); }
// inline void LDPC_Encode_n394k160( uint32_t *Data) { LDPC_Encode(Data, Data+5, 5, 194, (uint32_t *)LDPC_ParityGen_n354k160); }
void LDPC_Encode(const uint32_t *Data, uint32_t *Parity);
void LDPC_Encode( uint32_t *Data);
#ifdef WITH_PPM
void LDPC_Encode_n354k160(const uint32_t *Data, uint32_t *Parity);
void LDPC_Encode_n354k160( uint32_t *Data);
#endif
// check Data against Parity (run 48 parity checks) - return number of failed checks
uint8_t LDPC_Check(const uint32_t *Data, const uint32_t *Parity); // Data and Parity are 32-bit words
uint8_t LDPC_Check(const uint32_t *Data);
uint8_t LDPC_Check(const uint8_t *Data); // 20 data bytes followed by 6 parity bytes
#ifdef WITH_PPM
uint8_t LDPC_Check_n354k160(const uint32_t *Data, const uint32_t *Parity); // Data and Parity are 32-bit words
uint8_t LDPC_Check_n354k160(const uint32_t *Data);
#endif
#endif // __AVR__
#ifndef __AVR__
extern const uint8_t LDPC_ParityCheckIndex_n208k160[48][24];
class LDPC_Decoder
{ public:
const static uint8_t UserBits = 160; // 5 32-bit bits = 20 bytes
const static uint8_t UserWords = UserBits/32;
const static uint8_t ParityBits = 48; // 6 bytes (total packet is 26 bytes)
const static uint8_t CodeBits = UserBits+ParityBits; // 160+48 = 208 code bits = 26 bytes
const static uint8_t CodeBytes = (CodeBits+ 7)/ 8; //
const static uint8_t CodeWords = (CodeBits+31)/32; //
const static uint8_t MaxCheckWeight = 24;
// const static uint8_t MaxBitWeight = 8;
public:
int16_t InpBit[CodeBits]; // a-priori bits
int16_t ExtBit[CodeBits]; // extrinsic inf.
int16_t OutBit[CodeBits]; // a-posteriori bits
void Input(const uint8_t *Data, const uint8_t *Err)
{ uint8_t Mask=1; uint8_t Idx=0; uint8_t DataByte=0; uint8_t ErrByte=0;
for(uint8_t Bit=0; Bit<CodeBits; Bit++)
{ if(Mask==1) { DataByte=Data[Idx]; ErrByte=Err[Idx]; }
int16_t Inp;
if(ErrByte&Mask) Inp=0;
else Inp=(DataByte&Mask) ? +128:-128;
OutBit[Bit] = InpBit[Bit] = Inp; ExtBit[Bit]=0;
Mask<<=1; if(Mask==0) { Idx++; Mask=1; }
}
}
void Input(const uint32_t Data[CodeWords])
{ uint32_t Mask=1; uint8_t Idx=0; uint32_t Word=Data[Idx];
for(uint8_t Bit=0; Bit<CodeBits; Bit++)
{ OutBit[Bit] = InpBit[Bit] = (Word&Mask) ? +128:-128;
ExtBit[Bit]=0;
Mask<<=1; if(Mask==0) { Word=Data[++Idx]; Mask=1; }
}
}
void Input(const float *Data, float RefAmpl=1.0)
{ for(int Bit=0; Bit<CodeBits; Bit++)
{ int Inp = floor(128*Data[Bit^7]/RefAmpl+0.5);
if(Inp>32767) Inp=32767; else if(Inp<(-32767)) Inp=(-32767);
OutBit[Bit] = InpBit[Bit] = Inp;
ExtBit[Bit]=0; }
}
void Output(uint32_t Data[CodeWords])
{ uint32_t Mask=1; uint8_t Idx=0; uint32_t Word=0;
for(uint8_t Bit=0; Bit<CodeBits; Bit++)
{ if(OutBit[Bit]>0) Word|=Mask;
Mask<<=1; if(Mask==0) { Data[Idx++]=Word; Word=0; Mask=1; }
} if(Mask>1) Data[Idx++]=Word;
}
void Output(uint8_t Data[CodeBytes])
{ uint8_t Mask=1; uint8_t Idx=0; uint8_t Byte=0;
for(uint8_t Bit=0; Bit<CodeBits; Bit++)
{ if(OutBit[Bit]>0) Byte|=Mask;
Mask<<=1; if(Mask==0) { Data[Idx++]=Byte; Byte=0; Mask=1; }
} if(Mask>1) Data[Idx++]=Byte;
}
int8_t ProcessChecks(void)
{ for(uint8_t Bit=0; Bit<CodeBits; Bit++)
ExtBit[Bit]=0;
uint8_t Count=0;
for(uint8_t Row=0; Row<ParityBits; Row++)
{ int16_t Ret=ProcessCheck(Row);
if(Ret<=0) Count++; }
// printf("%d parity checks fail\n", Count);
if(Count==0) return 0;
for(uint8_t Bit=0; Bit<CodeBits; Bit++)
{ OutBit[Bit] = InpBit[Bit] + (ExtBit[Bit]>>1); }
return Count; }
int16_t ProcessCheck(uint8_t Row)
{ int16_t MinAmpl=32767; uint8_t MinBit=0; int16_t MinAmpl2=MinAmpl;
uint32_t Word=0; uint32_t Mask=1;
const uint8_t *CheckIndex = LDPC_ParityCheckIndex_n208k160[Row];
uint8_t CheckWeight = *CheckIndex++;
for(uint8_t Bit=0; Bit<CheckWeight; Bit++)
{ uint8_t BitIdx=CheckIndex[Bit];
int16_t Ampl=OutBit[BitIdx];
if(Ampl>0) Word|=Mask;
Mask<<=1;
if(Ampl<0) Ampl=(-Ampl);
if(Ampl<MinAmpl) { MinAmpl2=MinAmpl; MinAmpl=Ampl; MinBit=Bit; }
else if(Ampl<MinAmpl2) { MinAmpl2=Ampl; }
}
uint8_t CheckFails = Count1s(Word)&1;
Mask=1;
for(uint8_t Bit=0; Bit<CheckWeight; Bit++)
{ uint8_t BitIdx=CheckIndex[Bit];
int16_t Ampl = Bit==MinBit ? MinAmpl2 : MinAmpl;
if(CheckFails) Ampl=(-Ampl);
ExtBit[BitIdx] += (Word&Mask) ? Ampl:-Ampl;
Mask<<=1; }
return CheckFails?-MinAmpl:MinAmpl; }
} ;
template <class Float=float>
class LDPC_FloatDecoder
{ public:
const static int MaxCodeBits=512;
const static int MaxParityBits=256;
const static int MaxParityWeight=32; //
int CodeBits; // number of code bits
int ParityBits; // number of parity bits
uint16_t ParityCheckIndex[MaxParityBits][MaxParityWeight]; // list of 1's in the ParityCheck matrix
uint8_t ParityCheckRowWeight[MaxParityBits]; // number of 1's in ParityCheck rows
uint8_t ParityCheckColWeight[MaxCodeBits]; // number of 1's in ParityCheck columns
Float InpBit[MaxCodeBits]; // a-priori bits
Float ExtBit[MaxCodeBits]; // extrinsic inf.
Float OutBit[MaxCodeBits]; // a-posteriori bits
Float Feedback;
public:
LDPC_FloatDecoder()
{ CodeBits=0; ParityBits=0; Feedback=0.33; }
void Clear(void)
{ for(int Bit=0; Bit<CodeBits; Bit++)
{ OutBit[Bit] = InpBit[Bit] = ExtBit[Bit]=0; }
}
int Configure(int NewCodeBits, int NewParityBits, const uint32_t *PackedParityCheck )
{ if(CodeBits>MaxCodeBits) return -1;
CodeBits=NewCodeBits;
if(ParityBits>MaxParityBits) return -1;
ParityBits=NewParityBits;
for(int Bit=0; Bit<CodeBits; Bit++)
ParityCheckColWeight[Bit]=0;
const uint32_t *Check=PackedParityCheck;
for(int ParBit=0; ParBit<ParityBits; ParBit++)
{ int RowWeight=0;
uint32_t Word=0; uint32_t Mask=0;
for(int Bit=0; Bit<CodeBits; Bit++)
{ if(Mask==0) { Mask=1; Word=(*Check++); }
if(Word&Mask)
{ ParityCheckIndex[ParBit][RowWeight++]=Bit;
ParityCheckColWeight[Bit]++; }
Mask<<=1;
}
ParityCheckRowWeight[ParBit]=RowWeight;
}
return 1; }
void PrintConfig(void) const
{ printf("LDPC_FloatDecoder[%d,%d] Check index table:\n", CodeBits, ParityBits);
for(int ParBit=0; ParBit<ParityBits; ParBit++)
{ printf("Check[%3d]:", ParityCheckRowWeight[ParBit]);
for(int Bit=0; Bit<ParityCheckRowWeight[ParBit]; Bit++)
{ printf(" %3d", ParityCheckIndex[ParBit][Bit]); }
printf("\n");
}
printf("ColWeight[%d]:\n", CodeBits);
int Bit;
for(Bit=0; Bit<CodeBits; Bit++)
{ if((Bit&0x1F)==0x00) printf("%03d:", Bit);
printf(" %d", ParityCheckColWeight[Bit]);
if((Bit&0x1F)==0x1F) printf("\n"); }
if((Bit&0x1F)!=0x00) printf("\n");
}
void PrintOutBits(void)
{ printf("OutBit[%d]\n", CodeBits);
for(int Bit=0; Bit<CodeBits; Bit++)
{ if((Bit&0xF)==0x0) printf("%03d:", Bit);
printf(" %+6.3f", OutBit[Bit]);
if((Bit&0xF)==0xF) printf("\n"); }
}
void addInput(int Bit, Float Ampl)
{ InpBit[Bit]+=Ampl; OutBit[Bit] = InpBit[Bit]; }
void Input(const uint8_t *Data, const uint8_t *Err, Float Ampl=1.0) // get bits from series of bytes and the error pattern (from Manchester decoder)
{ uint8_t Mask=1; int Idx=0; uint8_t DataByte=0; uint8_t ErrByte=0;
for(int Bit=0; Bit<CodeBits; Bit++)
{ if(Mask==1) { DataByte=Data[Idx]; ErrByte=Err[Idx]; }
Float Inp;
if(ErrByte&Mask) Inp=0;
else Inp=(DataByte&Mask) ? +Ampl:-Ampl;
OutBit[Bit] = InpBit[Bit] = Inp; ExtBit[Bit]=0;
Mask<<=1; if(Mask==0) { Idx++; Mask=1; }
}
}
void Input(const uint32_t *Data, Float Ampl=1.0) // get bits from a series of 32-bit words
{ uint32_t Mask=0; int Idx=0; uint32_t Word=0;
for(int Bit=0; Bit<CodeBits; Bit++)
{ if(Mask==0) { Word=Data[Idx++]; Mask=1; }
OutBit[Bit] = InpBit[Bit] = (Word&Mask) ? +Ampl:-Ampl; ExtBit[Bit]=0;
Mask<<=1;
}
}
void Output(uint32_t *Data) // format decoded bits as a series of 32-bit words
{ uint32_t Mask=1; int Idx=0; uint32_t Word=0;
for(int Bit=0; Bit<CodeBits; Bit++)
{ if(OutBit[Bit]>0) Word|=Mask;
Mask<<=1; if(Mask==0) { Data[Idx++]=Word; Word=0; Mask=1; }
} if(Mask>1) Data[Idx++]=Word;
}
void Output(uint8_t *Data) // format decoded bits as a series of bytes
{ uint8_t Mask=1; int Idx=0; uint8_t Byte=0;
for(int Bit=0; Bit<CodeBits; Bit++)
{ if(OutBit[Bit]>0) Byte|=Mask;
Mask<<=1; if(Mask==0) { Data[Idx++]=Byte; Byte=0; Mask=1; }
} if(Mask>1) Data[Idx++]=Byte;
}
int ProcessChecks(void)
{ for(int Bit=0; Bit<CodeBits; Bit++) // clear the extrinsic inf. for bits
ExtBit[Bit]=0;
int Count=0;
for(int Row=0; Row<ParityBits; Row++) // process all parity checks and count how many have failed
{ Float Ret=ProcessCheck(Row);
if(Ret<=0) Count++; }
// printf("%d parity checks fail\n", Count);
if(Count==0) return 0; // if all passed, then return
for(int Bit=0; Bit<CodeBits; Bit++) // add Input+Extrinsic and store in Output
{ OutBit[Bit] = InpBit[Bit] + Feedback*ExtBit[Bit]; }
return Count; }
Float ProcessCheck(uint8_t Row)
{ Float MinAmpl=std::numeric_limits<Float>::max(); int MinBit=0; Float MinAmpl2=MinAmpl; // look for 1st and 2nd smallest LL
uint32_t Word=0; uint32_t Mask=1;
const uint16_t *CheckIndex = ParityCheckIndex[Row]; // indeces of bits in this parity check
int CheckWeight = ParityCheckRowWeight[Row]; // number of bits in this parity check
for(int Bit=0; Bit<CheckWeight; Bit++) // loop over bits in the parity check
{ int BitIdx=CheckIndex[Bit]; // index of the bit
Float Ampl=OutBit[BitIdx]; // LL of the bit
if(Ampl>0) Word|=Mask; // store hard bits in the Word
Mask<<=1;
if(Ampl<0) Ampl=(-Ampl); // strip the LL sign
if(Ampl<MinAmpl) { MinAmpl2=MinAmpl; MinAmpl=Ampl; MinBit=Bit; } // find 1st and 2nd smallest
else if(Ampl<MinAmpl2) { MinAmpl2=Ampl; }
}
int CheckFails = __builtin_parityl(Word); // tell if this parity check failed
Mask=1;
for(int Bit=0; Bit<CheckWeight; Bit++) // loop over bits in this parity check
{ int BitIdx=CheckIndex[Bit]; // inndex of the bit
Float Ampl = Bit==MinBit ? MinAmpl2 : MinAmpl; // if this is the weakest bit, then use 2nd smallest LL, otherwise 1st
if(CheckFails) Ampl=(-Ampl);
ExtBit[BitIdx] += (Word&Mask) ? Ampl:-Ampl; // add to the extrinsic inf. with the correct sign
Mask<<=1; }
return CheckFails?-MinAmpl:MinAmpl; }
int CountErrors(void)
{ int Count=0;
for(int Idx=0; Idx<CodeBits; Idx++)
{ bool Inp=InpBit[Idx]>0;
bool Out=OutBit[Idx]>0;
if(Inp!=Out) Count++; }
return Count; }
} ;
#ifdef WITH_PPM
template <class Float>
class OGN_PPM_Decoder
{ public:
static const int DataBits = 32*5; // 5 words = 160 data bits = OGN packet
static const int ParityBits = 194; // 194 parity bits (Gallager code)
static const int CodeBits = DataBits+ParityBits; // 354 total bits per Gallager code block
static const int BitsPerSymbol = 6; // 6 bits per symbol for PPM modulation
static const int PulsesPerSlot = 1<<BitsPerSymbol; // 64 (possible) pulses per time slot = 1 symbol = 6 bits
static const int CodeSymbols = CodeBits/BitsPerSymbol; // 59 time slots to form complete packet
LDPC_FloatDecoder<Float> LDPC_Decoder; // inner LDPC code decoder
Float InpSymb[CodeSymbols][PulsesPerSlot]; // input from the demodulator
Float ExtSymb[CodeSymbols][PulsesPerSlot]; // output from the LDPC decoder
Float OutSymb[CodeSymbols][PulsesPerSlot]; // input x extrinsic inf.
public:
OGN_PPM_Decoder()
{ LDPC_Decoder.Configure(CodeBits, ParityBits, (uint32_t *)LDPC_ParityCheck_n354k160);
Clear(); }
void Clear(void)
{ for(int Symb=0; Symb<CodeSymbols; Symb++)
{ Float Ext=1.0/PulsesPerSlot;
for(int Pulse=0; Pulse<PulsesPerSlot; Pulse++)
{ InpSymb[Symb][Pulse]=0; ExtSymb[Symb][Pulse]=Ext; OutSymb[Symb][Pulse]=0; }
}
}
void addSymbol(unsigned int Slot, unsigned int Symbol, Float Power=1.0)
{ if( (Slot>=CodeSymbols) || (Symbol>=PulsesPerSlot) ) return;
InpSymb[Slot][Symbol]+=Power; }
int Process(int Loops=48)
{ LDPC_Decoder.Clear();
for(int Symb=0; Symb<CodeSymbols; Symb++)
{ for(int Pulse=0; Pulse<PulsesPerSlot; Pulse++)
{ Float Pwr=InpSymb[Symb][Pulse]*ExtSymb[Symb][Pulse];
if(Pwr==0) continue;
Pwr=Pwr*Pwr;
int Bin=Binary(Pulse);
int Idx=Symb;
for(int Bit=0; Bit<BitsPerSymbol; Bit++, Idx+=CodeSymbols)
{ LDPC_Decoder.addInput(Idx, (Bin&1) ? +Pwr:-Pwr);
Bin>>=1; }
}
}
int CheckErr=0;
for( int Loop=0; Loop<Loops; Loop++)
{ CheckErr=LDPC_Decoder.ProcessChecks();
printf("%3d: OGN_PPM_Decoder.Process() => %3d\n", Loop, CheckErr);
if(CheckErr==0) break; }
return CheckErr; }
static uint8_t Gray(uint8_t Binary) { return Binary ^ (Binary>>1); }
static uint8_t Binary(uint8_t Gray)
{ Gray = Gray ^ (Gray >> 4);
Gray = Gray ^ (Gray >> 2);
Gray = Gray ^ (Gray >> 1);
return Gray; }
void NormExtSymb(Float Norm=1.0)
{ for(int Symb=0; Symb<CodeSymbols; Symb++)
{ NormExtSymb(Symb, Norm); }
}
void NormExtSymp(int Symb, Float Norm=1.0)
{ Float Sum=0;
for(int Pulse=0; Pulse<PulsesPerSlot; Pulse++)
{ Sum+=ExtSymb[Symb][Pulse]; }
Sum=Norm/Sum;
for(int Pulse=0; Pulse<PulsesPerSlot; Pulse++)
{ ExtSymb[Symb][Pulse]*=Sum; }
}
} ;
#endif // WITH_PPM
#endif // __AVR__
#endif // of __LDPC_H__