diff --git a/src/horus_api.c b/src/horus_api.c index 38a1cd3..3576919 100644 --- a/src/horus_api.c +++ b/src/horus_api.c @@ -36,9 +36,9 @@ #include "horus_l2.h" #define MAX_UW_LENGTH 100 -#define HORUS_API_VERSION 1 /* unique number that is bumped if API changes */ -#define HORUS_BINARY_NUM_BITS 360 /* fixed number of bytes in binary payload */ -#define HORUS_BINARY_NUM_PAYLOAD_BYTES 22 /* fixed number of bytes in binary payload */ +#define HORUS_API_VERSION 2 /* unique number that is bumped if API changes */ + + struct horus { int mode; @@ -68,16 +68,41 @@ int8_t uw_horus_rtty[] = { 0,0,1,0,0,1,0,1,1,0 }; -/* Unique word for Horus Binary */ +/* Unique word for Horus Binary V1 */ -int8_t uw_horus_binary[] = { +int8_t uw_horus_binary_v1[] = { 0,0,1,0,0,1,0,0, 0,0,1,0,0,1,0,0 }; -struct horus *horus_open (int mode, int Rs) { - int i; - assert((mode == HORUS_MODE_RTTY) || (mode == HORUS_MODE_BINARY)); + +/* Unique word for Horus Binary V2 128/256 bit modes (Last row in the 32x32 Hadamard matrix) */ + +int8_t uw_horus_binary_v2[] = { + 1, 0, 0, 1, 0, 1, 1, 0, // 0x96 + 0, 1, 1, 0, 1, 0, 0, 1, // 0x69 + 0, 1, 1, 0, 1, 0, 0, 1, // 0x69 + 1, 0, 0, 1, 0, 1, 1, 0 // 0x96 +}; + + +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){ + // RTTY Mode defaults - 100 baud, no assumptions about tone spacing. + return horus_open_advanced(HORUS_MODE_RTTY, 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. + return horus_open_advanced(HORUS_MODE_BINARY_V1, HORUS_BINARY_V1_DEFAULT_BAUD, -1); + } +} + + +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)); struct horus *hstates = (struct horus *)malloc(sizeof(struct horus)); assert(hstates != NULL); @@ -85,8 +110,23 @@ struct horus *horus_open (int mode, int Rs) { hstates->Fs = 48000; hstates->Rs = Rs; hstates->verbose = 0; hstates->mode = mode; if (mode == HORUS_MODE_RTTY) { + // Parameter setup for RTTY Reception + hstates->mFSK = 2; - hstates->max_packet_len = 1000; + hstates->max_packet_len = HORUS_RTTY_NUM_BITS; + + // If baud rate not provided, use default + if (hstates->Rs == -1){ + hstates->Rs = HORUS_RTTY_DEFAULT_BAUD; + } + + if (tx_tone_spacing == -1){ + // No tone spacing provided. Use dummy value to keep fsk modem happy, and disable mask estimation. + tx_tone_spacing = 2*hstates->Rs; + mask = 0; + } else { + mask = 1; + } /* map UW to make it easier to search for */ @@ -98,19 +138,42 @@ struct horus *horus_open (int mode, int Rs) { hstates->rx_bits_len = hstates->max_packet_len; } - if (mode == HORUS_MODE_BINARY) { + if (mode == HORUS_MODE_BINARY_V1) { + // Parameter setup for the Legacy Horus Binary Mode (22 byte frames, Golay encoding) + hstates->mFSK = 4; - hstates->max_packet_len = HORUS_BINARY_NUM_BITS; - for (i=0; iuw[i] = 2*uw_horus_binary[i] - 1; + hstates->max_packet_len = HORUS_BINARY_V1_NUM_CODED_BITS; + + // If baud rate not provided, use default + if (hstates->Rs == -1){ + hstates->Rs = HORUS_BINARY_V1_DEFAULT_BAUD; } - hstates->uw_len = sizeof(uw_horus_binary); - hstates->uw_thresh = sizeof(uw_horus_binary) - 2; /* allow a few bit errors in UW detection */ + + 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_V1_DEFAULT_TONE_SPACING; + mask = 0; + } else { + // Tone spacing provided, enable mask estimation. + mask = 1; + } + + for (i=0; iuw[i] = 2*uw_horus_binary_v1[i] - 1; + } + hstates->uw_len = sizeof(uw_horus_binary_v1); + hstates->uw_thresh = sizeof(uw_horus_binary_v1) - 2; /* allow a few bit errors in UW detection */ horus_l2_init(); hstates->rx_bits_len = hstates->max_packet_len; } - - hstates->fsk = fsk_create(hstates->Fs, hstates->Rs, hstates->mFSK, 1000, 2*hstates->Rs); + // TODO: Horus 256/128-bit modes here. + + // Create the FSK modedm struct. Note that the low-tone-frequency parameter is unused. + #define UNUSED 1000 + hstates->fsk = fsk_create(hstates->Fs, hstates->Rs, hstates->mFSK, UNUSED, tx_tone_spacing); + + // Set/disable the mask estimator depending on if tx_tone_spacing was provided (refer above) + fsk_set_freq_est_alg(hstates->fsk, mask); /* allocate enough room for two packets so we know there will be one complete packet if we find a UW at start */ @@ -275,7 +338,7 @@ int extract_horus_rtty(struct horus *hstates, char ascii_out[], int uw_loc) { } -int extract_horus_binary(struct horus *hstates, char hex_out[], int uw_loc) { +int extract_horus_binary_v1(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 */ @@ -313,13 +376,13 @@ int extract_horus_binary(struct horus *hstates, char hex_out[], int uw_loc) { fprintf(stderr, "\n"); } - uint8_t payload_bytes[HORUS_BINARY_NUM_PAYLOAD_BYTES]; - horus_l2_decode_rx_packet(payload_bytes, rxpacket, HORUS_BINARY_NUM_PAYLOAD_BYTES); + uint8_t payload_bytes[HORUS_BINARY_V1_NUM_UNCODED_PAYLOAD_BYTES]; + horus_l2_decode_rx_packet(payload_bytes, rxpacket, HORUS_BINARY_V1_NUM_UNCODED_PAYLOAD_BYTES); uint16_t crc_tx, crc_rx; - crc_rx = horus_l2_gen_crc16(payload_bytes, HORUS_BINARY_NUM_PAYLOAD_BYTES-2); - crc_tx = (uint16_t)payload_bytes[HORUS_BINARY_NUM_PAYLOAD_BYTES-2] + - ((uint16_t)payload_bytes[HORUS_BINARY_NUM_PAYLOAD_BYTES-1]<<8); + crc_rx = horus_l2_gen_crc16(payload_bytes, HORUS_BINARY_V1_NUM_UNCODED_PAYLOAD_BYTES-2); + crc_tx = (uint16_t)payload_bytes[HORUS_BINARY_V1_NUM_UNCODED_PAYLOAD_BYTES-2] + + ((uint16_t)payload_bytes[HORUS_BINARY_V1_NUM_UNCODED_PAYLOAD_BYTES-1]<<8); if (hstates->verbose) { fprintf(stderr, " extract_horus_binary crc_tx: %04X crc_rx: %04X\n", crc_tx, crc_rx); @@ -329,7 +392,7 @@ int extract_horus_binary(struct horus *hstates, char hex_out[], int uw_loc) { hex_out[0] = 0; char hex[3]; - for (b=0; bcrc_ok = (crc_tx == crc_rx); if ( hstates->crc_ok) { - hstates->total_payload_bits += HORUS_BINARY_NUM_PAYLOAD_BYTES; + hstates->total_payload_bits += HORUS_BINARY_V1_NUM_UNCODED_PAYLOAD_BYTES; } return hstates->crc_ok; } @@ -377,13 +440,13 @@ int horus_rx(struct horus *hstates, char ascii_out[], short demod_in[], int quad COMP *demod_in_comp = (COMP*)malloc(sizeof(COMP)*hstates->fsk->nin); for (i=0; ifsk->nin; i++) { - if (quadrature) { - demod_in_comp[i].real = demod_in[i * 2]; - demod_in_comp[i].imag = demod_in[i * 2 + 1]; - } else { - demod_in_comp[i].real = demod_in[i]; - demod_in_comp[i].imag = 0; - } + if (quadrature) { + demod_in_comp[i].real = demod_in[i * 2]; + demod_in_comp[i].imag = demod_in[i * 2 + 1]; + } else { + demod_in_comp[i].real = demod_in[i]; + demod_in_comp[i].imag = 0; + } } fsk_demod(hstates->fsk, &hstates->rx_bits[rx_bits_len-Nbits], demod_in_comp); free(demod_in_comp); @@ -402,8 +465,8 @@ int horus_rx(struct horus *hstates, char ascii_out[], short demod_in[], int quad if (hstates->mode == HORUS_MODE_RTTY) { packet_detected = extract_horus_rtty(hstates, ascii_out, uw_loc); } - if (hstates->mode == HORUS_MODE_BINARY) { - packet_detected = extract_horus_binary(hstates, ascii_out, uw_loc); + if (hstates->mode == HORUS_MODE_BINARY_V1) { + packet_detected = extract_horus_binary_v1(hstates, ascii_out, uw_loc); //#define DUMP_BINARY_PACKET #ifdef DUMP_BINARY_PACKET FILE *f = fopen("packetbits.txt", "wt"); assert(f != NULL); @@ -448,9 +511,14 @@ int horus_get_max_ascii_out_len(struct horus *hstates) { if (hstates->mode == HORUS_MODE_RTTY) { return hstates->max_packet_len/10; /* 7 bit ASCII, plus 3 sync bits */ } - if (hstates->mode == HORUS_MODE_BINARY) { - return (HORUS_BINARY_NUM_PAYLOAD_BYTES*2+1); /* Hexadecimal encoded */ - //return HORUS_BINARY_NUM_PAYLOAD_BYTES; + if (hstates->mode == HORUS_MODE_BINARY_V1) { + return (HORUS_BINARY_V1_NUM_UNCODED_PAYLOAD_BYTES*2+1); /* Hexadecimal encoded */ + } + if (hstates->mode == HORUS_MODE_BINARY_V2_256BIT) { + return (HORUS_BINARY_V2_256BIT_NUM_UNCODED_PAYLOAD_BYTES*2+1); /* Hexadecimal encoded */ + } + if (hstates->mode == HORUS_MODE_BINARY_V2_128BIT) { + return (HORUS_BINARY_V2_128BIT_NUM_UNCODED_PAYLOAD_BYTES*2+1); /* Hexadecimal encoded */ } assert(0); /* should never get here */ return 0; diff --git a/src/horus_api.h b/src/horus_api.h index ae712e7..107954d 100644 --- a/src/horus_api.h +++ b/src/horus_api.h @@ -35,20 +35,78 @@ #include #include "modem_stats.h" -#define HORUS_MODE_BINARY 0 -#define HORUS_MODE_RTTY 1 +/* Horus API Modes */ +#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 + + +// Settings for Legacy Horus Binary Mode (Golay Encoding) +#define HORUS_BINARY_V1_NUM_CODED_BITS 360 +#define HORUS_BINARY_V1_NUM_UNCODED_PAYLOAD_BYTES 22 +#define HORUS_BINARY_V1_DEFAULT_BAUD 100 +#define HORUS_BINARY_V1_DEFAULT_TONE_SPACING 270 // This is the minimum tone spacing possible on the RS41 + // reference implementation of this modem. + // Note that mask estimation is turned off by default for + // 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_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_UNCODED_PAYLOAD_BYTES 16 +#define HORUS_BINARY_V2_128BIT_DEFAULT_BAUD 25 +#define HORUS_BINARY_V2_128BIT_DEFAULT_TONE_SPACING 270 + +// Settings for RTTY Decoder +#define HORUS_RTTY_NUM_BITS 1000 // Limit the RTTY decoder to 100 characters per line (frame). +#define HORUS_RTTY_DEFAULT_BAUD 100 struct horus; struct MODEM_STATS; -struct horus *horus_open (int mode, int Rs); +/* + * Create an Horus Demod config/state struct using default mode parameters. + * + * int mode - Horus Mode Type (refer list above) + */ +struct horus *horus_open (int mode); + +/* + * Create an Horus Demod config/state struct with more customizations. + * + * int mode - Horus Mode Type (refer list above) + * int Rs - Symbol Rate (Hz). Set to -1 to use the default value for the mode (refer above) + * int tx_tone_spacing - FSK Tone Spacing, to configure mask estimator. Set to -1 to disable mask estimator. + */ + +struct horus *horus_open_advanced (int mode, int Rs, int tx_tone_spacing); + +/* + * Close a Horus demodulator struct and free memory. + */ void horus_close (struct horus *hstates); /* call before horus_rx() to determine how many shorts to pass in */ uint32_t horus_nin (struct horus *hstates); -/* returns 1 if ascii_out[] is valid */ +/* + * Demodulate some number of Horus modem samples. The number of samples to be + * demodulated can be found by calling horus_nin(). + * + * Returns 1 if the data in ascii_out[] is valid. + * + * struct horus *hstates - Horus API config/state struct, set up by horus_open / horus_open_advanced + * char ascii_out[] - Buffer for returned packet / text. + * short fsk_in[] - nin samples of modulated FSK. + * int quadrature - Set to 1 if input samples are complex samples. + */ int horus_rx (struct horus *hstates, char ascii_out[], short demod_in[], int quadrature); diff --git a/src/horus_demod.c b/src/horus_demod.c index b3d8af8..fca8d33 100644 --- a/src/horus_demod.c +++ b/src/horus_demod.c @@ -55,7 +55,8 @@ int main(int argc, char *argv[]) { int quadrature = 0; int fsk_lower = -1; int fsk_upper = -1; - int Rs = 100; + int Rs = -1; + int tone_spacing = -1; stats_loop = 0; stats_rate = 8; @@ -69,6 +70,7 @@ int main(int argc, char *argv[]) { {"help", no_argument, 0, 'h'}, {"mode", required_argument, 0, 'm'}, {"rate", optional_argument, 0, 'r'}, + {"tonespacing", optional_argument, 0, 's'}, {"stats", optional_argument, 0, 't'}, {"fsk_lower", optional_argument, 0, 'b'}, {"fsk_upper", optional_argument, 0, 'u'}, @@ -83,8 +85,15 @@ int main(int argc, char *argv[]) { mode = HORUS_MODE_RTTY; } if ((strcmp(optarg, "BINARY") == 0) || (strcmp(optarg, "binary") == 0)) { - mode = HORUS_MODE_BINARY; + 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 (mode == -1) { fprintf(stderr, "use --mode RTTY or --mode binary\n"); exit(1); @@ -127,6 +136,11 @@ int main(int argc, char *argv[]) { Rs = atoi(optarg); } break; + case 's': + if (optarg != NULL){ + tone_spacing = atoi(optarg); + } + break; break; } } @@ -138,7 +152,7 @@ int main(int argc, char *argv[]) { goto helpmsg; } - if( (argc - dx) > 5) { + if( (argc - dx) > 6) { fprintf(stderr, "Too many arguments\n"); helpmsg: fprintf(stderr,"usage: %s -m RTTY|binary [-q] [-v] [-c] [-t [r]] InputModemRawFile OutputAsciiFile\n",argv[0]); @@ -146,7 +160,8 @@ int main(int argc, char *argv[]) { fprintf(stderr,"InputModemRawFile 48 kHz 16 bit shorts real modem signal from radio\n"); fprintf(stderr," -m RTTY|binary\n"); fprintf(stderr,"--mode=RTTY|binary RTTY or binary Horus protcols\n"); - fprintf(stderr,"--rate=[Rs] Modem baud rate. Default: 100\n"); + fprintf(stderr,"--rate=[Rs] Customise modem baud rate. Default: (depends on mode) \n"); + fprintf(stderr,"--tonespacing=[tone_spacing] Transmitter Tone Spacing (Hz) Default: Not used.\n"); fprintf(stderr," -t[r] --stats=[r] Print out modem statistics to stderr in JSON.\n"); fprintf(stderr," r, if provided, sets the number of modem frames\n" " between statistic printouts\n"); @@ -180,7 +195,7 @@ int main(int argc, char *argv[]) { /* end command line processing */ - hstates = horus_open(mode, Rs); + hstates = horus_open_advanced(mode, Rs, tone_spacing); horus_set_verbose(hstates, verbose); if (hstates == NULL) {