Initial LDPC encoding/decoding

pull/1/head
Mark Jessop 2020-06-27 23:06:27 +09:30
rodzic 90cc397988
commit a97f62da15
10 zmienionych plików z 765 dodań i 53 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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[];

Wyświetl plik

@ -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[];

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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; i<sizeof(uw_horus_rtty); i++) {
hstates->uw[i] = 2*uw_horus_rtty[i] - 1;
for (i=0; i<sizeof(uw_horus_rtty_7N2); i++) {
hstates->uw[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; i<sizeof(uw_horus_binary_v2); i++) {
hstates->uw[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; i<sizeof(uw_horus_binary_v2); i++) {
hstates->uw[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; i<hstates->rx_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; b<en; b+=nfield) {
/* assemble bytes MSB to LSB */
rxbyte = 0;
for(j=0; j<nfield; j++) {
assert(hstates->rx_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; b<nout; b++) {
fprintf(stderr, "%02X", rxpacket[b]);
}
fprintf(stderr, "\n");
}
uint8_t payload_bytes[HORUS_BINARY_V2_128BIT_NUM_UNCODED_PAYLOAD_BYTES];
float *softbits = hstates->soft_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; b<HORUS_BINARY_V2_128BIT_NUM_UNCODED_PAYLOAD_BYTES; b++) {
sprintf(hex, "%02X", payload_bytes[b]);
strcat(hex_out, hex);
}
if (hstates->verbose) {
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; j<rx_bits_len; i++,j++) {
hstates->rx_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) {

Wyświetl plik

@ -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

Wyświetl plik

@ -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) {

Wyświetl plik

@ -59,14 +59,18 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#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<num_tx_data_bytes; i++) {
for(b=0; b<8; b++) {
tx_bit = (tx[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<num_tx_data_bytes; i++) {
for(b=0; b<8; b++) {
tx_bit = (tx[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<num_tx_data_bytes; i++) {
for(b=0; b<8; b++) {
tx_bit = (tx[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<num_tx_data_bytes; i++) {
for(b=0; b<8; b++) {
tx_bit = (tx[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;
}
}

Wyświetl plik

@ -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

199
src/ldpc.c 100644
Wyświetl plik

@ -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 <stdint.h>
#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;
}
}