diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8523f5c..640522b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,7 @@ set(horus_srcs kiss_fftr.c mpdecode_core.c H_256_768_22.c + H_128_384_23.c golay23.c phi0.c horus_api.c diff --git a/src/H_128_384_23.h b/src/H_128_384_23.h index c9bf4e7..846397a 100644 --- a/src/H_128_384_23.h +++ b/src/H_128_384_23.h @@ -6,11 +6,15 @@ #define H_128_384_23_NUMBERPARITYBITS 256 #define H_128_384_23_MAX_ROW_WEIGHT 3 +#define H_128_384_23_DATA_BYTES 16 +#define H_128_384_23_PARITY_BYTES 32 +#define H_128_384_23_BITS_PER_PACKET ((H_128_384_23_DATA_BYTES + H_128_384_23_PARITY_BYTES) * 8) #define H_128_384_23_CODELENGTH 384 #define H_128_384_23_NUMBERROWSHCOLS 128 #define H_128_384_23_MAX_COL_WEIGHT 5 #define H_128_384_23_DEC_TYPE 0 #define H_128_384_23_MAX_ITER 100 +#define H_128_384_23_COPRIME 347 extern const uint16_t H_128_384_23_H_rows[]; extern const uint16_t H_128_384_23_H_cols[]; diff --git a/src/H_256_768_22.h b/src/H_256_768_22.h index a92ce5c..77a025f 100644 --- a/src/H_256_768_22.h +++ b/src/H_256_768_22.h @@ -6,11 +6,15 @@ #define H_256_768_22_NUMBERPARITYBITS 512 #define H_256_768_22_MAX_ROW_WEIGHT 2 +#define H_256_768_22_DATA_BYTES 32 +#define H_256_768_22_PARITY_BYTES 64 +#define H_256_768_22_BITS_PER_PACKET ((H_256_768_22_DATA_BYTES + H_256_768_22_PARITY_BYTES) * 8) #define H_256_768_22_CODELENGTH 768 #define H_256_768_22_NUMBERROWSHCOLS 256 #define H_256_768_22_MAX_COL_WEIGHT 4 #define H_256_768_22_DEC_TYPE 0 #define H_256_768_22_MAX_ITER 100 +#define H_256_768_22_COPRIME 347 extern const uint16_t H_256_768_22_H_rows[]; extern const uint16_t H_256_768_22_H_cols[]; diff --git a/src/fsk.h b/src/fsk.h index c21ec33..07e128e 100644 --- a/src/fsk.h +++ b/src/fsk.h @@ -43,7 +43,7 @@ /* default internal parameters */ #define FSK_DEFAULT_P 8 -#define FSK_DEFAULT_NSYM 50 +#define FSK_DEFAULT_NSYM 50 struct FSK { /* Static parameters set up by fsk_init */ @@ -188,10 +188,22 @@ void fsk_demod(struct FSK *fsk, uint8_t rx_bits[],COMP fsk_in[]); * demodulated can be found by calling fsk_nin(). * * struct FSK *fsk - FSK config/state struct, set up by fsk_create - * float rx_bits[] - Buffer for Nbits soft decision bits to be written + * float rx_sd[] - Buffer for Nbits soft decision bits to be written * float fsk_in[] - nin samples of modualted FSK */ -void fsk_demod_sd(struct FSK *fsk, float rx_bits[],COMP fsk_in[]); +void fsk_demod_sd(struct FSK *fsk, float rx_sd[], COMP fsk_in[]); + +/* + * " Why not both? " + * Demodulate some number of FSK samples. The number of samples to be + * demodulated can be found by calling fsk_nin(). + * + * struct FSK *fsk - FSK config/state struct, set up by fsk_create + * float rx_bits[] - Buffer for Nbits soft decision bits to be written + * float rx_sd[] - Buffer for Nbits soft decision bits to be written + * float fsk_in[] - nin samples of modualted FSK + */ +void fsk_demod_core(struct FSK *fsk, uint8_t rx_bits[], float rx_sd[], COMP fsk_in[]); /* enables/disables normalisation of eye diagram samples */ diff --git a/src/horus_api.c b/src/horus_api.c index 3576919..e0b9384 100644 --- a/src/horus_api.c +++ b/src/horus_api.c @@ -52,6 +52,7 @@ struct horus { int uw_len; /* length of unique word */ int max_packet_len; /* max length of a telemetry packet */ uint8_t *rx_bits; /* buffer of received bits */ + float *soft_bits; /* buffer of soft decision outputs */ int rx_bits_len; /* length of rx_bits buffer */ int crc_ok; /* most recent packet checksum results */ int total_payload_bits; /* num bits rx-ed in last RTTY packet */ @@ -60,7 +61,7 @@ struct horus { /* Unique word for Horus RTTY 7 bit '$' character, 3 sync bits, repeated 5 times */ -int8_t uw_horus_rtty[] = { +int8_t uw_horus_rtty_7N2[] = { 0,0,1,0,0,1,0,1,1,0, 0,0,1,0,0,1,0,1,1,0, 0,0,1,0,0,1,0,1,1,0, @@ -86,12 +87,13 @@ int8_t uw_horus_binary_v2[] = { }; -struct horus *horus_open (int mode) { - assert((mode == HORUS_MODE_RTTY) || (mode == HORUS_MODE_BINARY_V1) || (mode == HORUS_MODE_BINARY_V2_256BIT) || (mode == HORUS_MODE_BINARY_V2_128BIT)); - if (mode == HORUS_MODE_RTTY){ +struct horus *horus_open (int mode) { + assert((mode == HORUS_MODE_RTTY_7N2) || (mode == HORUS_MODE_BINARY_V1) || (mode == HORUS_MODE_BINARY_V2_256BIT) || (mode == HORUS_MODE_BINARY_V2_128BIT)); + + if (mode == HORUS_MODE_RTTY_7N2){ // RTTY Mode defaults - 100 baud, no assumptions about tone spacing. - return horus_open_advanced(HORUS_MODE_RTTY, HORUS_RTTY_DEFAULT_BAUD, -1); + return horus_open_advanced(HORUS_MODE_RTTY_7N2, HORUS_RTTY_DEFAULT_BAUD, -1); } else { // Placeholder until we have more definition of the new modes. // Legacy Horus Binary Mode defaults - 100 baud, Disable mask estimation. @@ -102,14 +104,14 @@ struct horus *horus_open (int mode) { struct horus *horus_open_advanced (int mode, int Rs, int tx_tone_spacing) { int i, mask; - assert((mode == HORUS_MODE_RTTY) || (mode == HORUS_MODE_BINARY_V1) || (mode == HORUS_MODE_BINARY_V2_256BIT) || (mode == HORUS_MODE_BINARY_V2_128BIT)); + assert((mode == HORUS_MODE_RTTY_7N2) || (mode == HORUS_MODE_BINARY_V1) || (mode == HORUS_MODE_BINARY_V2_256BIT) || (mode == HORUS_MODE_BINARY_V2_128BIT)); struct horus *hstates = (struct horus *)malloc(sizeof(struct horus)); assert(hstates != NULL); hstates->Fs = 48000; hstates->Rs = Rs; hstates->verbose = 0; hstates->mode = mode; - if (mode == HORUS_MODE_RTTY) { + if (mode == HORUS_MODE_RTTY_7N2) { // Parameter setup for RTTY Reception hstates->mFSK = 2; @@ -130,11 +132,11 @@ struct horus *horus_open_advanced (int mode, int Rs, int tx_tone_spacing) { /* map UW to make it easier to search for */ - for (i=0; iuw[i] = 2*uw_horus_rtty[i] - 1; + for (i=0; iuw[i] = 2*uw_horus_rtty_7N2[i] - 1; } - hstates->uw_len = sizeof(uw_horus_rtty); - hstates->uw_thresh = sizeof(uw_horus_rtty) - 2; /* allow a few bit errors in UW detection */ + hstates->uw_len = sizeof(uw_horus_rtty_7N2); + hstates->uw_thresh = sizeof(uw_horus_rtty_7N2) - 2; /* allow a few bit errors in UW detection */ hstates->rx_bits_len = hstates->max_packet_len; } @@ -166,7 +168,66 @@ struct horus *horus_open_advanced (int mode, int Rs, int tx_tone_spacing) { horus_l2_init(); hstates->rx_bits_len = hstates->max_packet_len; } - // TODO: Horus 256/128-bit modes here. + + if (mode == HORUS_MODE_BINARY_V2_256BIT) { + // Parameter setup for the Legacy Horus Binary Mode (22 byte frames, Golay encoding) + + hstates->mFSK = 4; + hstates->max_packet_len = HORUS_BINARY_V2_256BIT_NUM_CODED_BITS ; + + // If baud rate not provided, use default + if (hstates->Rs == -1){ + hstates->Rs = HORUS_BINARY_V2_256BIT_DEFAULT_BAUD; + } + + if (tx_tone_spacing == -1){ + // No tone spacing provided. Disable mask estimation, and use the default tone spacing value as a dummy value. + tx_tone_spacing = HORUS_BINARY_V2_256BIT_DEFAULT_TONE_SPACING; + mask = 0; + } else { + // Tone spacing provided, enable mask estimation. + mask = 1; + } + + for (i=0; iuw[i] = 2*uw_horus_binary_v2[i] - 1; + } + hstates->uw_len = sizeof(uw_horus_binary_v2); + hstates->uw_thresh = sizeof(uw_horus_binary_v2) - 2; /* allow a few bit errors in UW detection */ + // TODO: Any initialization required? + // horus_l2_init(); + hstates->rx_bits_len = hstates->max_packet_len; + } + + if (mode == HORUS_MODE_BINARY_V2_128BIT) { + // Parameter setup for the Legacy Horus Binary Mode (22 byte frames, Golay encoding) + + hstates->mFSK = 4; + hstates->max_packet_len = HORUS_BINARY_V2_128BIT_NUM_CODED_BITS ; + + // If baud rate not provided, use default + if (hstates->Rs == -1){ + hstates->Rs = HORUS_BINARY_V2_128BIT_DEFAULT_BAUD; + } + + if (tx_tone_spacing == -1){ + // No tone spacing provided. Disable mask estimation, and use the default tone spacing value as a dummy value. + tx_tone_spacing = HORUS_BINARY_V2_128BIT_DEFAULT_TONE_SPACING; + mask = 0; + } else { + // Tone spacing provided, enable mask estimation. + mask = 1; + } + + for (i=0; iuw[i] = 2*uw_horus_binary_v2[i] - 1; + } + hstates->uw_len = sizeof(uw_horus_binary_v2); + hstates->uw_thresh = sizeof(uw_horus_binary_v2) - 2; /* allow a few bit errors in UW detection */ + // TODO: Any initialization required? + // horus_l2_init(); + hstates->rx_bits_len = hstates->max_packet_len; + } // Create the FSK modedm struct. Note that the low-tone-frequency parameter is unused. #define UNUSED 1000 @@ -185,6 +246,13 @@ struct horus *horus_open_advanced (int mode, int Rs, int tx_tone_spacing) { hstates->rx_bits[i] = 0; } + // and the same for soft-bits + hstates->soft_bits = (float*)malloc(sizeof(float) * hstates->rx_bits_len); + assert(hstates->soft_bits != NULL); + for(i=0; irx_bits_len; i++) { + hstates->soft_bits[i] = 0.0; + } + hstates->crc_ok = 0; hstates->total_payload_bits = 0; @@ -412,6 +480,83 @@ int extract_horus_binary_v1(struct horus *hstates, char hex_out[], int uw_loc) { } +int extract_horus_binary_v2_128(struct horus *hstates, char hex_out[], int uw_loc) { + const int nfield = 8; /* 8 bit binary */ + int st = uw_loc; /* first bit of first char */ + int en = uw_loc + hstates->max_packet_len; /* last bit of max length packet */ + + int j, b, nout; + uint8_t rxpacket[hstates->max_packet_len]; + uint8_t rxbyte, *pout; + + /* convert bits to a packet of bytes */ + + pout = rxpacket; nout = 0; + + for (b=st; brx_bits[b+j] <= 1); + rxbyte <<= 1; + rxbyte |= hstates->rx_bits[b+j]; + } + + /* build up output array */ + + *pout++ = rxbyte; + nout++; + } + + if (hstates->verbose) { + fprintf(stderr, " extract_horus_binary_v2_128 nout: %d\n Received Packet before decoding:\n ", nout); + for (b=0; bsoft_bits + uw_loc + sizeof(uw_horus_binary_v2); + horus_ldpc_decode( payload_bytes, softbits , HORUS_MODE_BINARY_V2_128BIT); + + uint16_t crc_tx, crc_rx; + crc_rx = horus_l2_gen_crc16(payload_bytes, HORUS_BINARY_V2_128BIT_NUM_UNCODED_PAYLOAD_BYTES-2); + crc_tx = (uint16_t)payload_bytes[HORUS_BINARY_V2_128BIT_NUM_UNCODED_PAYLOAD_BYTES-2] + + ((uint16_t)payload_bytes[HORUS_BINARY_V2_128BIT_NUM_UNCODED_PAYLOAD_BYTES-1]<<8); + + if (hstates->verbose) { + fprintf(stderr, " extract_horus_binary_v2_128 crc_tx: %04X crc_rx: %04X\n", crc_tx, crc_rx); + } + + /* convert to ASCII string of hex characters */ + + hex_out[0] = 0; + char hex[3]; + for (b=0; bverbose) { + fprintf(stderr, " nout: %d Decoded Payload bytes:\n %s\n", nout, hex_out); + } + + /* With noise input to FSK demod we can get occasinal UW matches, + so a good idea to only pass on any packets that pass CRC */ + + hstates->crc_ok = (crc_tx == crc_rx); + if ( hstates->crc_ok) { + hstates->total_payload_bits += HORUS_BINARY_V2_128BIT_NUM_UNCODED_PAYLOAD_BYTES; + } + + return hstates->crc_ok; + +} + + int horus_rx(struct horus *hstates, char ascii_out[], short demod_in[], int quadrature) { int i, j, uw_loc, packet_detected; @@ -430,6 +575,7 @@ int horus_rx(struct horus *hstates, char ascii_out[], short demod_in[], int quad for(i=0,j=Nbits; jrx_bits[i] = hstates->rx_bits[j]; + hstates->soft_bits[i] = hstates->soft_bits[j]; } /* demodulate latest bits */ @@ -448,7 +594,8 @@ int horus_rx(struct horus *hstates, char ascii_out[], short demod_in[], int quad demod_in_comp[i].imag = 0; } } - fsk_demod(hstates->fsk, &hstates->rx_bits[rx_bits_len-Nbits], demod_in_comp); + + fsk_demod_core(hstates->fsk, &hstates->rx_bits[rx_bits_len-Nbits], &hstates->soft_bits[rx_bits_len-Nbits], demod_in_comp); free(demod_in_comp); /* UW search to see if we can find the start of a packet in the buffer */ @@ -462,7 +609,7 @@ int horus_rx(struct horus *hstates, char ascii_out[], short demod_in[], int quad /* OK we have found a unique word, and therefore the start of a packet, so lets try to extract valid packets */ - if (hstates->mode == HORUS_MODE_RTTY) { + if (hstates->mode == HORUS_MODE_RTTY_7N2) { packet_detected = extract_horus_rtty(hstates, ascii_out, uw_loc); } if (hstates->mode == HORUS_MODE_BINARY_V1) { @@ -477,8 +624,11 @@ int horus_rx(struct horus *hstates, char ascii_out[], short demod_in[], int quad exit(0); #endif } + if (hstates->mode == HORUS_MODE_BINARY_V2_128BIT){ + packet_detected = extract_horus_binary_v2_128(hstates, ascii_out, uw_loc); + } } - + return packet_detected; } @@ -508,7 +658,7 @@ int horus_get_max_demod_in(struct horus *hstates) { int horus_get_max_ascii_out_len(struct horus *hstates) { assert(hstates != NULL); - if (hstates->mode == HORUS_MODE_RTTY) { + if (hstates->mode == HORUS_MODE_RTTY_7N2) { return hstates->max_packet_len/10; /* 7 bit ASCII, plus 3 sync bits */ } if (hstates->mode == HORUS_MODE_BINARY_V1) { diff --git a/src/horus_api.h b/src/horus_api.h index 107954d..768a400 100644 --- a/src/horus_api.h +++ b/src/horus_api.h @@ -39,7 +39,8 @@ #define HORUS_MODE_BINARY_V1 0 // Legacy binary mode #define HORUS_MODE_BINARY_V2_256BIT 1 // New 256-bit LDPC-encoded mode #define HORUS_MODE_BINARY_V2_128BIT 2 // New 128-bit LDPC-encoded mode -#define HORUS_MODE_RTTY 99 // RTTY Decoding +#define HORUS_MODE_RTTY_7N2 90 // RTTY Decoding - 7N2 +#define HORUS_MODE_RTTY_8N2 90 // RTTY Decoding - 8N2 // Settings for Legacy Horus Binary Mode (Golay Encoding) @@ -52,15 +53,15 @@ // this mode, and hence this spacing is not used. // Settings for Horus Binary 256-bit mode (LDPC Encoding, r=1/3) -#define HORUS_BINARY_V2_256BIT_NUM_CODED_BITS 768 +#define HORUS_BINARY_V2_256BIT_NUM_CODED_BITS (768+32) #define HORUS_BINARY_V2_256BIT_NUM_UNCODED_PAYLOAD_BYTES 32 #define HORUS_BINARY_V2_256BIT_DEFAULT_BAUD 100 #define HORUS_BINARY_V2_256BIT_DEFAULT_TONE_SPACING 270 // Settings for Horus Binary 128-bit mode (LDPC Encoding, r=1/3) -#define HORUS_BINARY_V2_128BIT_NUM_CODED_BITS 384 +#define HORUS_BINARY_V2_128BIT_NUM_CODED_BITS (384+32) #define HORUS_BINARY_V2_128BIT_NUM_UNCODED_PAYLOAD_BYTES 16 -#define HORUS_BINARY_V2_128BIT_DEFAULT_BAUD 25 +#define HORUS_BINARY_V2_128BIT_DEFAULT_BAUD 100 #define HORUS_BINARY_V2_128BIT_DEFAULT_TONE_SPACING 270 // Settings for RTTY Decoder diff --git a/src/horus_demod.c b/src/horus_demod.c index fca8d33..cfbb006 100644 --- a/src/horus_demod.c +++ b/src/horus_demod.c @@ -82,18 +82,17 @@ int main(int argc, char *argv[]) { switch(o) { case 'm': if ((strcmp(optarg, "RTTY") == 0) || (strcmp(optarg, "rtty") == 0)) { - mode = HORUS_MODE_RTTY; + mode = HORUS_MODE_RTTY_7N2; } if ((strcmp(optarg, "BINARY") == 0) || (strcmp(optarg, "binary") == 0)) { mode = HORUS_MODE_BINARY_V1; } - // Commented out until these are implemented. - // if ((strcmp(optarg, "256BIT") == 0) || (strcmp(optarg, "256bit") == 0)) { - // mode = HORUS_MODE_BINARY_256BIT; - // } - // if ((strcmp(optarg, "128BIT") == 0) || (strcmp(optarg, "128bit") == 0)) { - // mode = HORUS_MODE_BINARY_128BIT; - // } + if ((strcmp(optarg, "256BIT") == 0) || (strcmp(optarg, "256bit") == 0)) { + mode = HORUS_MODE_BINARY_V2_256BIT; + } + if ((strcmp(optarg, "128BIT") == 0) || (strcmp(optarg, "128bit") == 0)) { + mode = HORUS_MODE_BINARY_V2_128BIT; + } if (mode == -1) { fprintf(stderr, "use --mode RTTY or --mode binary\n"); exit(1); @@ -228,6 +227,7 @@ int main(int argc, char *argv[]) { if (verbose) { fprintf(stderr, "read nin %d\n", horus_nin(hstates)); } + if (horus_rx(hstates, ascii_out, demod_in, quadrature)) { fprintf(stdout, "%s", ascii_out); if (crc_results) { diff --git a/src/horus_l2.c b/src/horus_l2.c index 78fa85a..6d92783 100644 --- a/src/horus_l2.c +++ b/src/horus_l2.c @@ -59,14 +59,18 @@ #include #include #include +#include "mpdecode_core.h" #include "horus_l2.h" #include "golay23.h" +#include "H_128_384_23.h" +#include "H_256_768_22.h" #ifdef HORUS_L2_UNITTEST #define HORUS_L2_RX #endif -static char uw[] = {'$','$'}; +static const char uw[] = {'$','$'}; // UW for Horus Binary v1 +static const char uw_v2[] = {0x96, 0x69, 0x69, 0x96}; // UW for Horus Binary v2 modes /* Function Prototypes ------------------------------------------------*/ @@ -780,6 +784,43 @@ struct TBinaryPacket uint16_t Checksum; // CRC16-CCITT Checksum. } __attribute__ ((packed)); + +struct V2SmallBinaryPacket +{ +// 4 byte preamble for high error rates ("0x96696996") +// - to improve soft bit prediction +uint8_t PayloadID; // Legacy list +uint8_t Counter; // 8 bit counter +uint16_t BiSeconds; // Time of day / 2 +uint8_t Latitude[3]; // (int)(float * 1.0e7) / (1<<8) +uint8_t Longitude[3]; // ( better than 10m precision ) +uint16_t Altitude; // 0 - 65 km +uint8_t Voltage; // scaled 5.0v in 255 range +uint8_t User; // Temp / Sats + // Temperature 6 bits MSB => (+30 to -32) + // Satellites 2 bits LSB => 0,4,8,12 is good enough +uint16_t Checksum; // CRC16-CCITT Checksum. +} __attribute__ ((packed)); + + +struct V2LargeBinaryPacket +{ + uint16_t PayloadID; + uint16_t Counter; + uint8_t Hours; + uint8_t Minutes; + uint8_t Seconds; + float Latitude; + float Longitude; + uint16_t Altitude; + uint8_t Speed; // Speed in Knots (1-255 knots) + uint8_t Sats; + int8_t Temp; // Twos Complement Temp value. + uint8_t BattVoltage; // 0 = 0.5v, 255 = 2.0V, linear steps in-between. + uint8_t User[9]; + uint16_t Checksum; // CRC16-CCITT Checksum. +} __attribute__ ((packed)); + #ifdef GEN_TX_BITS /* generate a file of tx_bits to modulate using fsk_horus.m for modem simulations */ @@ -888,42 +929,103 @@ int main(void) { } #endif - #ifdef GEN_TX_BITSTREAM /* Generate a stream of encoded Horus packets in a format suitable to feed into fsk_mod */ int main(int argc,char *argv[]) { - int nbytes = sizeof(struct TBinaryPacket); - struct TBinaryPacket input_payload; - int num_tx_data_bytes = horus_l2_get_num_tx_data_bytes(nbytes); - unsigned char tx[num_tx_data_bytes]; - int i, framecnt; - if(argc != 2){ - fprintf(stderr,"usage: %s numFrames\n",argv[0]); + int i, framecnt, mode; + int num_tx_data_bytes = 0; + + if(argc != 3){ + fprintf(stderr,"usage: %s numFrames mode\n",argv[0]); exit(1); } framecnt = atoi(argv[1]); + mode = atoi(argv[2]); - /* all zeros is nastiest sequence for demod before scrambling */ - memset(&input_payload, 0, nbytes); - input_payload.Checksum = horus_l2_gen_crc16((unsigned char*)&input_payload, nbytes-2); + if (mode == 0){ + // Horus Mode v1 + int nbytes = sizeof(struct TBinaryPacket); + struct TBinaryPacket input_payload; + int num_tx_data_bytes = horus_l2_get_num_tx_data_bytes(nbytes); + unsigned char tx[num_tx_data_bytes]; - horus_l2_encode_tx_packet(tx, (unsigned char*)&input_payload, nbytes); + memset(&input_payload, 0, nbytes); + input_payload.Checksum = horus_l2_gen_crc16((unsigned char*)&input_payload, nbytes-2); - int b; - uint8_t tx_bit; - while(framecnt >= 0){ - for(i=0; i> (7-b)) & 0x1; /* msb first */ - fwrite(&tx_bit,sizeof(uint8_t),1,stdout); - fflush(stdout); + horus_l2_encode_tx_packet(tx, (unsigned char*)&input_payload, nbytes); + + int b; + uint8_t tx_bit; + while(framecnt >= 0){ + for(i=0; i> (7-b)) & 0x1; /* msb first */ + fwrite(&tx_bit,sizeof(uint8_t),1,stdout); + fflush(stdout); + } } + framecnt -= 1; + } + + } else if (mode == 1) { + // 32-byte horus mode + int nbytes = sizeof(struct V2LargeBinaryPacket); + struct V2LargeBinaryPacket input_payload; + + int num_tx_data_bytes = sizeof(uw_v2) + H_256_768_22_DATA_BYTES + H_256_768_22_PARITY_BYTES; + unsigned char tx[num_tx_data_bytes]; + memset(&input_payload, 0, nbytes); + + input_payload.PayloadID = 1; + input_payload.Counter = 2; + input_payload.Checksum = horus_l2_gen_crc16((unsigned char*)&input_payload, nbytes-2); + + int ldpc_tx_bytes = ldpc_encode_packet(tx, (unsigned char*)&input_payload, 2); + + int b; + uint8_t tx_bit; + while(framecnt >= 0){ + for(i=0; i> (7-b)) & 0x1; /* msb first */ + fwrite(&tx_bit,sizeof(uint8_t),1,stdout); + fflush(stdout); + } + } + framecnt -= 1; + } + + } else if (mode == 2) { + // 16-byte horus mode + int nbytes = sizeof(struct V2SmallBinaryPacket); + struct V2SmallBinaryPacket input_payload; + + int num_tx_data_bytes = sizeof(uw_v2) + H_128_384_23_DATA_BYTES + H_128_384_23_PARITY_BYTES; + unsigned char tx[num_tx_data_bytes]; + memset(&input_payload, 0, nbytes); + input_payload.PayloadID = 1; + input_payload.Counter = 2; + + input_payload.Checksum = horus_l2_gen_crc16((unsigned char*)&input_payload, nbytes-2); + + int ldpc_tx_bytes = ldpc_encode_packet(tx, (unsigned char*)&input_payload, 2); + + int b; + uint8_t tx_bit; + while(framecnt >= 0){ + for(i=0; i> (7-b)) & 0x1; /* msb first */ + fwrite(&tx_bit,sizeof(uint8_t),1,stdout); + fflush(stdout); + } + } + framecnt -= 1; } - framecnt -= 1; } return 0; @@ -944,3 +1046,236 @@ unsigned short horus_l2_gen_crc16(unsigned char* data_p, unsigned char length) { return crc; } + +// Take payload data bytes, prepend a unique word and append parity bits +int ldpc_encode_packet(unsigned char *out_data, unsigned char *in_data, int mode) { + unsigned int i, last = 0; + unsigned char *pout; + + unsigned int data_bytes, parity_bytes, number_parity_bits, max_row_weight; + + if (mode == 1){ + // 32-byte Mode. + data_bytes = H_256_768_22_DATA_BYTES; + parity_bytes = H_256_768_22_PARITY_BYTES; + number_parity_bits = H_256_768_22_NUMBERPARITYBITS; + max_row_weight = H_256_768_22_MAX_ROW_WEIGHT; + } else { + // 16-byte Mode. + data_bytes = H_128_384_23_DATA_BYTES; + parity_bytes = H_128_384_23_PARITY_BYTES; + number_parity_bits = H_128_384_23_NUMBERPARITYBITS; + max_row_weight = H_128_384_23_MAX_ROW_WEIGHT; + } + + pout = out_data; + memcpy(pout, uw_v2, sizeof(uw_v2)); + pout += sizeof(uw_v2); + memcpy(pout, in_data, data_bytes); + pout += data_bytes; + memset(pout, 0, parity_bytes); + + // process parity bit offsets + for (i = 0; i < number_parity_bits; i++) { + unsigned int shift, j; + uint8_t tmp; + + for(j = 0; j < max_row_weight; j++) { + // This is a bit silly, move this out of this loop. + if (mode == 1){ + uint8_t tmp = H_256_768_22_H_rows[i + number_parity_bits * j]; + } else if (mode == 2) { + uint8_t tmp = H_128_384_23_H_rows[i + number_parity_bits * j]; + } + if (!tmp) + continue; + tmp--; + shift = 7 - (tmp & 7); // MSB + last ^= in_data[tmp >> 3] >> shift; + } + shift = 7 - (i & 7); // MSB + pout[i >> 3] |= (last & 1) << shift; + } + + pout = out_data + sizeof(uw_v2); + interleave(pout, data_bytes + parity_bytes, 0); + scramble(pout, data_bytes + parity_bytes); + + return data_bytes + parity_bytes + sizeof(uw_v2); +} + +/* Scramble and interleave are 8bit lsb, but bitstream is sent msb */ +#define LSB2MSB(X) (X + 7 - 2 * (X & 7) ) + +/* Invert bits - ldpc expects negative floats for high hits */ +void soft_unscramble(float *in, float* out, int nbits) { + int i, ibit; + uint16_t scrambler = 0x4a80; /* init additive scrambler at start of every frame */ + uint16_t scrambler_out; + + for ( i = 0; i < nbits; i++ ) { + scrambler_out = ( (scrambler >> 1) ^ scrambler) & 0x1; + + /* modify i-th bit by xor-ing with scrambler output sequence */ + ibit = LSB2MSB(i); + if ( scrambler_out ) { + out[ibit] = in[ibit]; + } else { + out[ibit] = -in[ibit]; + } + + scrambler >>= 1; + scrambler |= scrambler_out << 14; + } +} + +// soft bit deinterleave +void soft_deinterleave(float *in, float* out, int mode) { + int n, i, j, bits_per_packet, coprime; + + if (mode == 1) { + // 256_768 + bits_per_packet = H_256_768_22_BITS_PER_PACKET; + coprime = H_256_768_22_COPRIME; + } else { + bits_per_packet = H_128_384_23_BITS_PER_PACKET; + coprime = H_128_384_23_COPRIME; + } + + + for ( n = 0; n < bits_per_packet; n++ ) { + i = LSB2MSB(n); + j = LSB2MSB( (coprime * n) % bits_per_packet); + out[i] = in[j]; + } +} + +// // packed bit deinterleave - same as Golay version , but different Coprime +// void bitwise_deinterleave(uint8_t *inout, int nbytes) +// { +// uint16_t nbits = (uint16_t)nbytes*8; +// uint32_t i, j, ibit, ibyte, ishift, jbyte, jshift; +// uint8_t out[nbytes]; + +// memset(out, 0, nbytes); +// for(j = 0; j < nbits; j++) { +// i = (COPRIME * j) % nbits; + +// /* read bit i */ +// ibyte = i>>3; +// ishift = i&7; +// ibit = (inout[ibyte] >> ishift) & 0x1; + +// /* write bit i to bit j position */ +// jbyte = j>>3; +// jshift = j&7; +// out[jbyte] |= ibit << jshift; +// } + +// memcpy(inout, out, nbytes); +// } + +// /* Compare detected bits to corrected bits */ +// void ldpc_errors( const uint8_t *outbytes, uint8_t *rx_bytes ) { +// int length = DATA_BYTES + PARITY_BYTES; +// uint8_t temp[length]; +// int i, percentage, count = 0; +// memcpy(temp, rx_bytes, length); + +// scramble(temp, length); // use scrambler from Golay code +// bitwise_deinterleave(temp, length); + +// // count bits changed during error correction +// for(i = 0; i < BITS_PER_PACKET; i++) { +// int x, y, offset, shift; + +// shift = i & 7; +// offset = i >> 3; +// x = temp[offset] >> shift; +// y = outbytes[offset] >> shift; +// count += (x ^ y) & 1; +// } + +// // scale errors against a maximum of 20% BER +// percentage = (count * 5 * 100) / BITS_PER_PACKET; +// if (percentage > 100) +// percentage = 100; +// set_error_count( percentage ); +// } + +/* LDPC decode */ +void horus_ldpc_decode(uint8_t *payload, float *sd, int mode) { + float sum, mean, sumsq, estEsN0, x; + int bits_per_packet; + + if(mode == 1){ + bits_per_packet = H_256_768_22_BITS_PER_PACKET; + } else { + bits_per_packet = H_128_384_23_BITS_PER_PACKET; + } + + float llr[bits_per_packet]; + float temp[bits_per_packet]; + uint8_t outbits[bits_per_packet]; + + int b, i, parityCC; + struct LDPC ldpc; + + /* normalise bitstream to log-like */ + sum = 0.0; + for ( i = 0; i < bits_per_packet; i++ ) + sum += fabs(sd[i]); + mean = sum / bits_per_packet; + + sumsq = 0.0; + for ( i = 0; i < bits_per_packet; i++ ) { + x = fabs(sd[i]) / mean - 1.0; + sumsq += x * x; + } + estEsN0 = 2.0 * bits_per_packet / (sumsq + 1.0e-3) / mean; + for ( i = 0; i < bits_per_packet; i++ ) + llr[i] = estEsN0 * sd[i]; + + /* reverse whitening and re-order bits */ + soft_unscramble(llr, temp, bits_per_packet); + soft_deinterleave(temp, llr, mode); + + /* correct errors */ + if (mode == 1){ + // 32-byte mode H_256_768_22 + ldpc.max_iter = H_256_768_22_MAX_ITER; + ldpc.dec_type = 0; + ldpc.q_scale_factor = 1; + ldpc.r_scale_factor = 1; + ldpc.CodeLength = H_256_768_22_CODELENGTH; + ldpc.NumberParityBits = H_256_768_22_NUMBERPARITYBITS; + ldpc.NumberRowsHcols = H_256_768_22_NUMBERROWSHCOLS; + ldpc.max_row_weight = H_256_768_22_MAX_ROW_WEIGHT; + ldpc.max_col_weight = H_256_768_22_MAX_COL_WEIGHT; + ldpc.H_rows = (uint16_t *)H_256_768_22_H_rows; + ldpc.H_cols = (uint16_t *)H_256_768_22_H_cols; + } else { + // 16-byte mode + ldpc.max_iter = H_128_384_23_MAX_ITER; + ldpc.dec_type = 0; + ldpc.q_scale_factor = 1; + ldpc.r_scale_factor = 1; + ldpc.CodeLength = H_128_384_23_CODELENGTH; + ldpc.NumberParityBits = H_128_384_23_NUMBERPARITYBITS; + ldpc.NumberRowsHcols = H_128_384_23_NUMBERROWSHCOLS; + ldpc.max_row_weight = H_128_384_23_MAX_ROW_WEIGHT; + ldpc.max_col_weight = H_128_384_23_MAX_COL_WEIGHT; + ldpc.H_rows = (uint16_t *)H_128_384_23_H_rows; + ldpc.H_cols = (uint16_t *)H_128_384_23_H_cols; + } + + i = run_ldpc_decoder(&ldpc, outbits, llr, &parityCC); + + /* convert MSB bits to a packet of bytes */ + for (b = 0; b < (bits_per_packet/8); b++) { + uint8_t rxbyte = 0; + for(i=0; i<8; i++) + rxbyte |= outbits[b*8+i] << (7 - i); + payload[b] = rxbyte; + } +} diff --git a/src/horus_l2.h b/src/horus_l2.h index 6cb42ba..d5b4f0b 100644 --- a/src/horus_l2.h +++ b/src/horus_l2.h @@ -27,4 +27,10 @@ void horus_l2_decode_rx_packet(unsigned char *output_payload_data, unsigned short horus_l2_gen_crc16(unsigned char* data_p, unsigned char length); +int ldpc_encode_packet(uint8_t *buff_mfsk, uint8_t *FSK, int mode); + +void soft_unscramble(float *in, float* out, int nbits); +void soft_deinterleave(float *in, float* out, int mode); +void horus_ldpc_decode(uint8_t *payload, float *sd, int mode); + #endif diff --git a/src/ldpc.c b/src/ldpc.c new file mode 100644 index 0000000..c11c855 --- /dev/null +++ b/src/ldpc.c @@ -0,0 +1,199 @@ +/* ldpc interface to decoder + * + * It is expected that the switch to ldpc will give a 60% speed improvement + * over golay code, with no loss of performance over white noise - the use of + * soft-bit detection and longer codewords compensating for the expected 2dB loss + * from reducing the number of parity bits. + * + * Golay code can reliably correct a 10% BER, equivalent to a 20% loss of signal + * during deep fading. It is not clear how well ldpc will cope with deep fading, + * but the shorter packers are bound to be more badly affected. + */ + + +#include +#include "math.h" +#include "string.h" +#include "mpdecode_core.h" +#include "horus_l2.h" +#include "H_128_384_23.h" +#include "H_256_768_22.h" + + +#define MAX_ITER 20 + +/* Scramble and interleave are 8bit lsb, but bitstream is sent msb */ +#define LSB2MSB(X) (X + 7 - 2 * (X & 7) ) + +/* Invert bits - ldpc expects negative floats for high hits */ +void soft_unscramble(float *in, float* out, int nbits) { + int i, ibit; + uint16_t scrambler = 0x4a80; /* init additive scrambler at start of every frame */ + uint16_t scrambler_out; + + for ( i = 0; i < nbits; i++ ) { + scrambler_out = ( (scrambler >> 1) ^ scrambler) & 0x1; + + /* modify i-th bit by xor-ing with scrambler output sequence */ + ibit = LSB2MSB(i); + if ( scrambler_out ) { + out[ibit] = in[ibit]; + } else { + out[ibit] = -in[ibit]; + } + + scrambler >>= 1; + scrambler |= scrambler_out << 14; + } +} + +// soft bit deinterleave +void soft_deinterleave(float *in, float* out, int mode) { + int n, i, j, bits_per_packet, coprime; + + if (mode == 1) { + // 256_768 + bits_per_packet = H_256_768_22_BITS_PER_PACKET; + coprime = H_256_768_22_COPRIME; + } else { + bits_per_packet = H_128_384_23_BITS_PER_PACKET; + coprime = H_128_384_23_COPRIME; + } + + + for ( n = 0; n < bits_per_packet; n++ ) { + i = LSB2MSB(n); + j = LSB2MSB( (coprime * n) % bits_per_packet); + out[i] = in[j]; + } +} + +// // packed bit deinterleave - same as Golay version , but different Coprime +// void bitwise_deinterleave(uint8_t *inout, int nbytes) +// { +// uint16_t nbits = (uint16_t)nbytes*8; +// uint32_t i, j, ibit, ibyte, ishift, jbyte, jshift; +// uint8_t out[nbytes]; + +// memset(out, 0, nbytes); +// for(j = 0; j < nbits; j++) { +// i = (COPRIME * j) % nbits; + +// /* read bit i */ +// ibyte = i>>3; +// ishift = i&7; +// ibit = (inout[ibyte] >> ishift) & 0x1; + +// /* write bit i to bit j position */ +// jbyte = j>>3; +// jshift = j&7; +// out[jbyte] |= ibit << jshift; +// } + +// memcpy(inout, out, nbytes); +// } + +// /* Compare detected bits to corrected bits */ +// void ldpc_errors( const uint8_t *outbytes, uint8_t *rx_bytes ) { +// int length = DATA_BYTES + PARITY_BYTES; +// uint8_t temp[length]; +// int i, percentage, count = 0; +// memcpy(temp, rx_bytes, length); + +// scramble(temp, length); // use scrambler from Golay code +// bitwise_deinterleave(temp, length); + +// // count bits changed during error correction +// for(i = 0; i < BITS_PER_PACKET; i++) { +// int x, y, offset, shift; + +// shift = i & 7; +// offset = i >> 3; +// x = temp[offset] >> shift; +// y = outbytes[offset] >> shift; +// count += (x ^ y) & 1; +// } + +// // scale errors against a maximum of 20% BER +// percentage = (count * 5 * 100) / BITS_PER_PACKET; +// if (percentage > 100) +// percentage = 100; +// set_error_count( percentage ); +// } + +/* LDPC decode */ +void horus_ldpc_decode(uint8_t *payload, float *sd, int mode) { + float sum, mean, sumsq, estEsN0, x; + int bits_per_packet; + + if(mode == 1){ + bits_per_packet = H_256_768_22_BITS_PER_PACKET; + } else { + bits_per_packet = H_128_384_23_BITS_PER_PACKET; + } + + float llr[bits_per_packet]; + float temp[bits_per_packet]; + uint8_t outbits[bits_per_packet]; + + int b, i, parityCC; + struct LDPC ldpc; + + /* normalise bitstream to log-like */ + sum = 0.0; + for ( i = 0; i < bits_per_packet; i++ ) + sum += fabs(sd[i]); + mean = sum / bits_per_packet; + + sumsq = 0.0; + for ( i = 0; i < bits_per_packet; i++ ) { + x = fabs(sd[i]) / mean - 1.0; + sumsq += x * x; + } + estEsN0 = 2.0 * bits_per_packet / (sumsq + 1.0e-3) / mean; + for ( i = 0; i < bits_per_packet; i++ ) + llr[i] = estEsN0 * sd[i]; + + /* reverse whitening and re-order bits */ + soft_unscramble(llr, temp, bits_per_packet); + soft_deinterleave(temp, llr, mode); + + /* correct errors */ + if (mode == 1){ + // 32-byte mode H_256_768_22 + ldpc.max_iter = H_256_768_22_MAX_ITER; + ldpc.dec_type = 0; + ldpc.q_scale_factor = 1; + ldpc.r_scale_factor = 1; + ldpc.CodeLength = H_256_768_22_CODELENGTH; + ldpc.NumberParityBits = H_256_768_22_NUMBERPARITYBITS; + ldpc.NumberRowsHcols = H_256_768_22_NUMBERROWSHCOLS; + ldpc.max_row_weight = H_256_768_22_MAX_ROW_WEIGHT; + ldpc.max_col_weight = H_256_768_22_MAX_COL_WEIGHT; + ldpc.H_rows = (uint16_t *)H_256_768_22_H_rows; + ldpc.H_cols = (uint16_t *)H_256_768_22_H_cols; + } else { + // 16-byte mode + ldpc.max_iter = H_128_384_23_MAX_ITER; + ldpc.dec_type = 0; + ldpc.q_scale_factor = 1; + ldpc.r_scale_factor = 1; + ldpc.CodeLength = H_128_384_23_CODELENGTH; + ldpc.NumberParityBits = H_128_384_23_NUMBERPARITYBITS; + ldpc.NumberRowsHcols = H_128_384_23_NUMBERROWSHCOLS; + ldpc.max_row_weight = H_128_384_23_MAX_ROW_WEIGHT; + ldpc.max_col_weight = H_128_384_23_MAX_COL_WEIGHT; + ldpc.H_rows = (uint16_t *)H_128_384_23_H_rows; + ldpc.H_cols = (uint16_t *)H_128_384_23_H_cols; + } + + i = run_ldpc_decoder(&ldpc, outbits, llr, &parityCC); + + /* convert MSB bits to a packet of bytes */ + for (b = 0; b < (bits_per_packet/8); b++) { + uint8_t rxbyte = 0; + for(i=0; i<8; i++) + rxbyte |= outbits[b*8+i] << (7 - i); + payload[b] = rxbyte; + } +}