/* Reference encoder for proposed coded AO-40 telemetry format - v1.0 7 Jan 2002 * Copyright 2002, Phil Karn, KA9Q * This software may be used under the terms of the GNU Public License (GPL) */ #define SYNC_POLY 0x48 /* Sync vector polynomial */ #define SCRAMBLER_POLY 0x95 /* Scrambler polynomial */ #define CPOLYA 0x4f /* First convolutional encoder polynomial */ #define CPOLYB 0x6d /* Second convolutional encoder polynomial */ #define GF_POLY 0x187 /* GF(256) field generator polynomial */ #define A0 255 /* log(0) element of GF(256) in index form */ /* Beginning of read-only tables */ /* The log/antilog lookup tables used for Galois field multiplication * in the Reed-Solomon encoder. */ static unsigned char Index_of[256]; static unsigned char Alpha_to[256]; /* The code generator polynomial coefficients in index (logarithmic) form * for the CCSDS standard (255,223) RS code * Only coefficients C1-C16 are given since the polynomial is palindromic and C0 and 32 * are 0 */ static unsigned char RS_poly[] = { 249, 59, 66, 4, 43,126,251, 97, 30, 3,213, 50, 66,170, 5, 24 }; /* End of read-only tables */ /* Beginning of encoder internal state */ /* The two Reed-Solomon encoder shift registers */ unsigned char RS_block[2][32]; /* How many bytes have already been given to encode_data() for this frame */ static int Nbytes; /* Interleaver write pointers */ static unsigned char Bmask; static int Bindex; /* Scrambler shift register state */ static unsigned char Scrambler; /* Convolutional encoder shift register state */ static unsigned char Conv_sr; /* End of encoder internal state */ /* This is the encoder's output buffer. It is ready for transmission after * the last call to encode_parity(). * It should be sent in order, the most significant bit of Interleaver[0] * first. */ unsigned char Interleaver[650]; /* Internal functions */ /* Reduce argument modulo 255 without a divide * In this file, x is never greater than 508 so the while could be an if */ static inline int mod255(int x){ while (x >= 255) x -= 255; return x; } /* Return 1 if x has odd parity, 0 if even * This function is used rather heavily, so it is worth some effort to optimize * There may be more efficient machine-specific ways to do this, or it could * be implemented with a lookup table */ static inline int parity(int x){ x ^= x >> 4; x ^= x >> 2; x ^= x >> 1; return x & 1; } /* Internal function to write one binary channel symbol into the block interleaver * and update the pointers */ static void interleave_symbol(int c){ if(c) Interleaver[Bindex] |= Bmask; else Interleaver[Bindex] &= ~Bmask; Bindex += 10; /* Move forward 80 bits */ if(Bindex >= 650){ /* Past end of interleaver, step to the next row */ Bindex -= 650; Bmask >>= 1; if(Bmask == 0){ /* End of this byte, go to the next */ Bmask = 0x80; Bindex++; } } } /* Internal function to convolutionally encode and block-interleave one byte (no scrambling) */ static void encode_and_interleave(unsigned char c,int cnt){ while(cnt-- != 0){ Conv_sr = (Conv_sr << 1) | (c >> 7); c <<= 1; interleave_symbol(parity(Conv_sr & CPOLYA)); interleave_symbol(!parity(Conv_sr & CPOLYB)); /* Second encoder symbol is inverted */ } } /* Internal function to scramble a byte, convolutionally encode and block interleave */ static void scramble_and_encode(unsigned char c){ int i; /* Scramble byte */ c ^= Scrambler; /* Update scrambler * It might be more efficient to use the Galois form of the PN generator * instead of the Fibonacci form if the parity function is not fast. * If RAM is available, a simple 320-byte lookup table indexed by Nbytes * could be used instead. */ for(i=0;i<8;i++) Scrambler = (Scrambler << 1) | parity(Scrambler & SCRAMBLER_POLY); encode_and_interleave(c,8); } /* End of internal functions, beginning of user entry points */ /* This function is called before each frame to reset the encoder for a new frame */ void reset_encoder(void){ int i,j; Nbytes = 0; Conv_sr = 0; Scrambler = 0xff; Bmask = 0x40; Bindex = 0; for(j=0;j<2;j++) for(i=0;i<32;i++) RS_block[j][i] = 0; } /* This function initializes the encoder. It only has to be called once * at program start */ void init_encoder(void){ int i,sr; /* Build Galois field log/antilog tables */ Index_of[0] = A0; /* log(zero) = -inf */ Alpha_to[A0] = 0; /* alpha**-inf = 0 */ sr = 1; for(i=0;i<255;i++){ Index_of[sr] = i; Alpha_to[i] = sr; sr <<= 1; if(sr & 256) sr ^= GF_POLY; sr &= 255; } /* Clear interleaver */ for(i=0;i<650;i++) Interleaver[i] = 0; /* Generate sync vector here, place into Interleaver */ sr = 0x7f; for(i=0;i<65;i++){ if(sr & 64) Interleaver[10*i] |= 0x80; /* Every 80th bit is a sync bit */ sr = (sr << 1) | parity(sr & SYNC_POLY); } reset_encoder(); } /* This function is called with each user data byte to be encoded into the * current frame. It should be called in sequence 256 times per frame, followed * by 64 calls to encode_parity(). The Interleaver array is then ready for transmission. */ void encode_byte(unsigned char c){ unsigned char *rp; int i; unsigned char feedback; /* Update the appropriate Reed-Solomon codeword */ rp = RS_block[Nbytes & 1]; /* Compute feedback term */ feedback = Index_of[c ^ rp[0]]; /* If feedback is non-zero, multiply by each generator polynomial coefficient and * add to corresponding shift register elements */ if(feedback != A0){ int j; /* This loop exploits the palindromic nature of the generator polynomial * to halve the number of discrete multiplications */ for(j=0;j<15;j++){ unsigned char t; t = Alpha_to[mod255(feedback + RS_poly[j])]; rp[j+1] ^= t; rp[31-j] ^= t; } rp[16] ^= Alpha_to[mod255(feedback + RS_poly[15])]; } /* Shift register one position down */ for(i=0;i<31;i++) rp[i] = rp[i+1]; /* Handle highest order coefficient, which is unity */ if(feedback != A0){ rp[31] = Alpha_to[feedback]; } else { rp[31] = 0; } scramble_and_encode(c); Nbytes++; } /* This function should be called 64 times after the 256 data bytes * have been passed to update_encoder. Each call scrambles, encodes and * interleaves one byte of Reed-Solomon parity. * * If this routine executes quickly enough to meet real-time * requirements, it could be incorporated into a loop at the end of * update_encoder() that is called when Nbytes reaches 256. */ void encode_parity(void){ unsigned char c; c = RS_block[Nbytes & 1][(Nbytes-256)>>1]; scramble_and_encode(c); if(++Nbytes == 320){ /* Tail off the convolutional encoder */ encode_and_interleave(0,6); } }