diff --git a/CMakeLists.txt b/CMakeLists.txt index b462bc1..f612e80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,10 +62,17 @@ enable_testing() add_test(NAME test_horus_rtty_7n2 COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src; sox ${CMAKE_CURRENT_SOURCE_DIR}/samples/rtty_7n2.wav -r 48000 -t raw - | - ./horus_demod -m rtty -c - - | grep OK | wc -l" + ./horus_demod -c -m rtty - - | grep OK | wc -l" ) set_tests_properties(test_horus_rtty_7n2 PROPERTIES PASS_REGULAR_EXPRESSION "6") + add_test(NAME test_horus_rtty_8n2 + COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src; + sox ${CMAKE_CURRENT_SOURCE_DIR}/samples/rtty_8n2.wav -r 48000 -t raw - | + ./horus_demod -c --rate=300 -m rtty8n2 - - | grep OK | wc -l" + ) + set_tests_properties(test_horus_rtty_8n2 PROPERTIES PASS_REGULAR_EXPRESSION "4") + add_test(NAME test_horus_binary_iq COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src; cat ${CMAKE_CURRENT_SOURCE_DIR}/samples/horusb_iq_s16.raw | diff --git a/horusdemodlib/__init__.py b/horusdemodlib/__init__.py index 1276d02..0a8da88 100755 --- a/horusdemodlib/__init__.py +++ b/horusdemodlib/__init__.py @@ -1 +1 @@ -__version__ = "0.1.5" +__version__ = "0.1.6" diff --git a/horusdemodlib/demod.py b/horusdemodlib/demod.py index c3df93d..847ed0a 100644 --- a/horusdemodlib/demod.py +++ b/horusdemodlib/demod.py @@ -64,6 +64,7 @@ class Mode(Enum): BINARY_V1 = 0 RTTY_7N2 = 90 RTTY = 90 + RTTY_8N2 = 91 BINARY_V2_256BIT = 1 BINARY_V2_128BIT = 2 diff --git a/horusdemodlib/payloads.py b/horusdemodlib/payloads.py index 8b28f3a..936d38e 100644 --- a/horusdemodlib/payloads.py +++ b/horusdemodlib/payloads.py @@ -98,11 +98,11 @@ def grab_latest_payload_id_list(url=PAYLOAD_ID_LIST_URL, local_file="payload_id_ return True -def init_payload_id_list(): +def init_payload_id_list(filename="payload_id_list.txt"): """ Initialise and update the local payload ID list. """ - grab_latest_payload_id_list() - HORUS_PAYLOAD_LIST = read_payload_list() + grab_latest_payload_id_list(local_file=filename) + HORUS_PAYLOAD_LIST = read_payload_list(filename=filename) @@ -171,10 +171,10 @@ def grab_latest_custom_field_list(url=HORUS_CUSTOM_FIELD_URL, local_file="custom return True -def init_custom_field_list(): +def init_custom_field_list(filename="custom_field_list.json"): """ Initialise and update the local custom field list """ - grab_latest_custom_field_list() - HORUS_CUSTOM_FIELDS = read_custom_field_list() + grab_latest_custom_field_list(local_file=filename) + HORUS_CUSTOM_FIELDS = read_custom_field_list(filename=filename) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 43b11eb..d3fa5ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "horusdemodlib" -version = "0.1.5" +version = "0.1.6" description = "Project Horus HAB Telemetry Demodulators" authors = ["Mark Jessop"] license = "LGPL-2.1-or-later" diff --git a/samples/rtty_8n2.wav b/samples/rtty_8n2.wav new file mode 100644 index 0000000..f3f283e Binary files /dev/null and b/samples/rtty_8n2.wav differ diff --git a/src/horus_api.c b/src/horus_api.c index f9d18c7..82c4889 100644 --- a/src/horus_api.c +++ b/src/horus_api.c @@ -58,12 +58,24 @@ struct horus { int total_payload_bits; /* num bits rx-ed in last RTTY packet */ }; -/* Unique word for Horus RTTY 7 bit '$' character, 3 sync bits, - repeated 5 times */ + +/* +RTTY Unique word = $ characters, repeated at least 2 times. +$ = (0)010 0100 +Reversed = 0010010(0) +*/ 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, +// 0,0,1,0,0,1,0,1,1,0, +// 0,0,1,0,0,1,0,1,1,0 +}; + +int8_t uw_horus_rtty_8N2[] = { + 0,0,1,0,0,1,0,0,1,1,0, + 0,0,1,0,0,1,0,0,1,1,0, // 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 @@ -104,7 +116,7 @@ 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_7N2) || (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_RTTY_8N2) || (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); @@ -112,10 +124,10 @@ struct horus *horus_open_advanced (int mode, int Rs, int tx_tone_spacing) { hstates->Fs = 48000; hstates->Rs = Rs; hstates->verbose = 0; hstates->mode = mode; if (mode == HORUS_MODE_RTTY_7N2) { - // Parameter setup for RTTY Reception + // Parameter setup for RTTY 7N2 Reception hstates->mFSK = 2; - hstates->max_packet_len = HORUS_RTTY_NUM_BITS; + hstates->max_packet_len = HORUS_RTTY_7N2_NUM_BITS; // If baud rate not provided, use default if (hstates->Rs == -1){ @@ -140,6 +152,35 @@ struct horus *horus_open_advanced (int mode, int Rs, int tx_tone_spacing) { hstates->rx_bits_len = hstates->max_packet_len; } + if (mode == HORUS_MODE_RTTY_8N2) { + // Parameter setup for RTTY 8N2 Reception + + hstates->mFSK = 2; + hstates->max_packet_len = HORUS_RTTY_8N2_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 */ + + for (i=0; iuw[i] = 2*uw_horus_rtty_8N2[i] - 1; + } + hstates->uw_len = sizeof(uw_horus_rtty_8N2); + hstates->uw_thresh = sizeof(uw_horus_rtty_8N2) - 2; /* allow a few bit errors in UW detection */ + hstates->rx_bits_len = hstates->max_packet_len; + } + if (mode == HORUS_MODE_BINARY_V1) { // Parameter setup for the Legacy Horus Binary Mode (22 byte frames, Golay encoding) @@ -324,9 +365,9 @@ int hex2int(char ch) { } -int extract_horus_rtty(struct horus *hstates, char ascii_out[], int uw_loc) { - const int nfield = 7; /* 7 bit ASCII */ - const int npad = 3; /* 3 sync bits between characters */ +int extract_horus_rtty(struct horus *hstates, char ascii_out[], int uw_loc, int ascii_bits, int stop_bits) { + const int nfield = ascii_bits; /* 7 or 8 bit ASCII */ + const int npad = stop_bits + 1; /* N stop bits + start bit between characters */ int st = uw_loc; /* first bit of first char */ int en = hstates->max_packet_len - nfield; /* last bit of max length packet */ @@ -375,7 +416,7 @@ int extract_horus_rtty(struct horus *hstates, char ascii_out[], int uw_loc) { fprintf(stderr, " begin endpacket\n"); } // Only process up to the next 5 characters (checksum + line ending) - en = i + 10*5; + en = i + (ascii_bits+stop_bits+1)*5; } /* build up output array, really only need up to tx crc but @@ -402,7 +443,7 @@ int extract_horus_rtty(struct horus *hstates, char ascii_out[], int uw_loc) { *(ptx_crc+4) = 0; /* terminate ASCII string */ if (crc_ok) { - hstates->total_payload_bits = strlen(ascii_out)*7; + hstates->total_payload_bits = strlen(ascii_out)*ascii_bits; } } else { @@ -703,7 +744,20 @@ int horus_rx(struct horus *hstates, char ascii_out[], short demod_in[], int quad a packet, so lets try to extract valid packets */ if (hstates->mode == HORUS_MODE_RTTY_7N2) { - packet_detected = extract_horus_rtty(hstates, ascii_out, uw_loc); + packet_detected = extract_horus_rtty(hstates, ascii_out, uw_loc, 7, 2); + + if (packet_detected){ + // If we have found a packet, advance the bits enough that we don't detect the + // same packet again, if it has more than 2x $$s. + // NEED TO CHECK THIS DOESN'T CAUSE SEGFAULTS! + for(i=0,j=100; i<100; i++,j++) { + hstates->rx_bits[i] = hstates->rx_bits[j]; + hstates->soft_bits[i] = hstates->soft_bits[j]; + } + } + } + if (hstates->mode == HORUS_MODE_RTTY_8N2) { + packet_detected = extract_horus_rtty(hstates, ascii_out, uw_loc, 8, 2); if (packet_detected){ // If we have found a packet, advance the bits enough that we don't detect the @@ -766,6 +820,9 @@ int horus_get_max_ascii_out_len(struct horus *hstates) { 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_RTTY_8N2) { + return hstates->max_packet_len/11; /* 8 bit ASCII, plus 3 sync bits */ + } if (hstates->mode == HORUS_MODE_BINARY_V1) { return (HORUS_BINARY_V1_NUM_UNCODED_PAYLOAD_BYTES*2+1); /* Hexadecimal encoded */ } diff --git a/src/horus_api.h b/src/horus_api.h index 768a400..8b6ba26 100644 --- a/src/horus_api.h +++ b/src/horus_api.h @@ -40,7 +40,7 @@ #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_7N2 90 // RTTY Decoding - 7N2 -#define HORUS_MODE_RTTY_8N2 90 // RTTY Decoding - 8N2 +#define HORUS_MODE_RTTY_8N2 91 // RTTY Decoding - 8N2 // Settings for Legacy Horus Binary Mode (Golay Encoding) @@ -65,7 +65,9 @@ #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_MAX_CHARS 120 +#define HORUS_RTTY_7N2_NUM_BITS (HORUS_RTTY_MAX_CHARS*10) +#define HORUS_RTTY_8N2_NUM_BITS (HORUS_RTTY_MAX_CHARS*11) #define HORUS_RTTY_DEFAULT_BAUD 100 struct horus; diff --git a/src/horus_demod.c b/src/horus_demod.c index cfbb006..cba1431 100644 --- a/src/horus_demod.c +++ b/src/horus_demod.c @@ -84,6 +84,9 @@ int main(int argc, char *argv[]) { if ((strcmp(optarg, "RTTY") == 0) || (strcmp(optarg, "rtty") == 0)) { mode = HORUS_MODE_RTTY_7N2; } + if ((strcmp(optarg, "RTTY8N2") == 0) || (strcmp(optarg, "rtty8n2") == 0)) { + mode = HORUS_MODE_RTTY_8N2; + } if ((strcmp(optarg, "BINARY") == 0) || (strcmp(optarg, "binary") == 0)) { mode = HORUS_MODE_BINARY_V1; }