From 3502d47cf0774758c0b6f91d30b140816c48c248 Mon Sep 17 00:00:00 2001 From: chrnadig Date: Thu, 5 May 2022 22:03:14 +0200 Subject: [PATCH] Working version without SNR calculation --- decode_ft8.c | 2 +- ft8/constants.h | 15 ------ ft8/crc.h | 33 +++++------- ft8/decode.c | 21 +++----- ft8/encode.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++- ft8/ft8.h | 13 ++--- ft8/ldpc.h | 21 +++----- ft8/pack.c | 18 ++++--- ft8/pack.h | 17 ++----- ft8/text.c | 51 ++----------------- ft8/text.h | 45 +++++----------- ft8/unpack.h | 21 +++----- gen_ft8.c | 4 +- looptest.c | 37 ++++++++++---- 14 files changed, 226 insertions(+), 205 deletions(-) diff --git a/decode_ft8.c b/decode_ft8.c index 36647db..f15fae1 100755 --- a/decode_ft8.c +++ b/decode_ft8.c @@ -74,7 +74,7 @@ int main(int argc, char **argv) for (int i = 0; i < num_samples; i++) { signal[i] = pcm[i] / 32768.0f; } - n += ftx_decode(signal, num_samples, sample_rate, ft8_decode_callback, NULL); + n += ftx_decode(signal, num_samples, sample_rate, PROTO_FT8, ft8_decode_callback, NULL); } printf("Decoded %d messages\n", n); diff --git a/ft8/constants.h b/ft8/constants.h index c940808..39f940f 100644 --- a/ft8/constants.h +++ b/ft8/constants.h @@ -3,11 +3,6 @@ #include -#ifdef __cplusplus -extern "C" -{ -#endif - #define FT8_SYMBOL_PERIOD (0.160f) ///< FT8 symbol duration, defines tone deviation in Hz and symbol rate #define FT8_SLOT_TIME (15.0f) ///< FT8 slot period @@ -63,22 +58,12 @@ extern const uint8_t kFT4_XOR_sequence[10]; extern const uint8_t kFTX_LDPC_generator[FTX_LDPC_M][FTX_LDPC_K_BYTES]; /// LDPC(174,91) parity check matrix, containing 83 rows, -/// each row describes one parity check, -/// each number is an index into the codeword (1-origin). -/// The codeword bits mentioned in each row must xor to zero. -/// From WSJT-X's ldpc_174_91_c_reordered_parity.f90. extern const uint8_t kFTX_LDPC_Nm[FTX_LDPC_M][7]; -/// Mn from WSJT-X's bpdecode174.f90. Each row corresponds to a codeword bit. /// The numbers indicate which three parity checks (rows in Nm) refer to the codeword bit. -/// The numbers use 1 as the origin (first entry). extern const uint8_t kFTX_LDPC_Mn[FTX_LDPC_N][3]; /// Number of rows (columns in C/C++) in the array Nm. extern const uint8_t kFTX_LDPC_Num_rows[FTX_LDPC_M]; -#ifdef __cplusplus -} -#endif - #endif // _INCLUDE_CONSTANTS_H_ diff --git a/ft8/crc.h b/ft8/crc.h index c4cc6a8..626cd31 100644 --- a/ft8/crc.h +++ b/ft8/crc.h @@ -4,28 +4,19 @@ #include #include -#ifdef __cplusplus -extern "C" -{ -#endif +// Compute 14-bit CRC for a sequence of given number of bits using FT8/FT4 CRC polynomial +// [IN] message - byte sequence (MSB first) +// [IN] num_bits - number of bits in the sequence +uint16_t ftx_compute_crc(const uint8_t message[], int num_bits); - // Compute 14-bit CRC for a sequence of given number of bits using FT8/FT4 CRC polynomial - // [IN] message - byte sequence (MSB first) - // [IN] num_bits - number of bits in the sequence - uint16_t ftx_compute_crc(const uint8_t message[], int num_bits); +/// Extract the FT8/FT4 CRC of a packed message (during decoding) +/// @param[in] a91 77 bits of payload data + CRC +/// @return Extracted CRC +uint16_t ftx_extract_crc(const uint8_t a91[]); - /// Extract the FT8/FT4 CRC of a packed message (during decoding) - /// @param[in] a91 77 bits of payload data + CRC - /// @return Extracted CRC - uint16_t ftx_extract_crc(const uint8_t a91[]); - - /// Add FT8/FT4 CRC to a packed message (during encoding) - /// @param[in] payload 77 bits of payload data - /// @param[out] a91 91 bits of payload data + CRC - void ftx_add_crc(const uint8_t payload[], uint8_t a91[]); - -#ifdef __cplusplus -} -#endif +/// Add FT8/FT4 CRC to a packed message (during encoding) +/// @param[in] payload 77 bits of payload data +/// @param[out] a91 91 bits of payload data + CRC +void ftx_add_crc(const uint8_t payload[], uint8_t a91[]); #endif // _INCLUDE_CRC_H_ diff --git a/ft8/decode.c b/ft8/decode.c index db47979..dc8786d 100644 --- a/ft8/decode.c +++ b/ft8/decode.c @@ -227,12 +227,6 @@ static void monitor_process(monitor_t* me, const float* frame) ++me->wf.num_blocks; } -static void monitor_reset(monitor_t* me) -{ - me->wf.num_blocks = 0; - me->max_mag = 0; -} - static int get_index(const waterfall_t* wf, const candidate_t* candidate) { int offset = candidate->time_offset; @@ -696,20 +690,20 @@ static void ft8_extract_symbol(const uint8_t* wf, float* logl) logl[2] = max4(s2[1], s2[3], s2[5], s2[7]) - max4(s2[0], s2[2], s2[4], s2[6]); } -// decode FT8 signal, call callback for every decoded message -int ftx_decode(float *signal, int num_samples, int sample_rate, ft8_decode_callback_t callback, void *ctx) +// decode FT4 or FT8 signal, call callback for every decoded message +int ftx_decode(float *signal, int num_samples, int sample_rate, ftx_protocol_t protocol, ftx_decode_callback_t callback, void *ctx) { - bool is_ft8 = true; + bool is_ft8 = false; // Compute FFT over the whole signal and store it monitor_t mon; monitor_config_t mon_cfg = { - .f_min = 100, - .f_max = 3000, + .f_min = 0.0, + .f_max = 4000.0, .sample_rate = sample_rate, .time_osr = kTime_osr, .freq_osr = kFreq_osr, - .protocol = is_ft8 ? PROTO_FT8 : PROTO_FT4 + .protocol = protocol }; monitor_init(&mon, &mon_cfg); LOG(LOG_DEBUG, "Waterfall allocated %d symbols\n", mon.wf.max_blocks); @@ -740,8 +734,6 @@ int ftx_decode(float *signal, int num_samples, int sample_rate, ft8_decode_callb for (int idx = 0; idx < num_candidates; ++idx) { const candidate_t* cand = &candidate_list[idx]; - if (cand->score < kMin_score) - continue; float freq_hz = (cand->freq_offset + (float)cand->freq_sub / mon.wf.freq_osr) / mon.symbol_period; float time_sec = (cand->time_offset + (float)cand->time_sub / mon.wf.time_osr) * mon.symbol_period; @@ -803,7 +795,6 @@ int ftx_decode(float *signal, int num_samples, int sample_rate, ft8_decode_callb callback(message.text, freq_hz, time_sec, 10.0 * log10f(signal / noise), cand->score, ctx); } } - LOG(LOG_INFO, "Decoded %d messages\n", num_decoded); monitor_free(&mon); diff --git a/ft8/encode.c b/ft8/encode.c index 562f386..b3ee631 100644 --- a/ft8/encode.c +++ b/ft8/encode.c @@ -1,8 +1,102 @@ +#include +#include +#include +#include +#include "ft8.h" #include "constants.h" #include "crc.h" +#include "pack.h" -#include +#define FT8_SYMBOL_BT 2.0f ///< symbol smoothing filter bandwidth factor (BT) +#define FT4_SYMBOL_BT 1.0f ///< symbol smoothing filter bandwidth factor (BT) +#define GFSK_CONST_K 5.336446f ///< == pi * sqrt(2 / log(2)) + +/// Computes a GFSK smoothing pulse. +/// The pulse is theoretically infinitely long, however, here it's truncated at 3 times the symbol length. +/// This means the pulse array has to have space for 3*n_spsym elements. +/// @param[in] n_spsym Number of samples per symbol +/// @param[in] bt Shape parameter (values defined for FT8/FT4) +/// @param[out] pulse Output array of pulse samples +/// +void gfsk_pulse(int n_spsym, float symbol_bt, float* pulse) +{ + for (int i = 0; i < 3 * n_spsym; ++i) + { + float t = i / (float)n_spsym - 1.5f; + float arg1 = GFSK_CONST_K * symbol_bt * (t + 0.5f); + float arg2 = GFSK_CONST_K * symbol_bt * (t - 0.5f); + pulse[i] = (erff(arg1) - erff(arg2)) / 2; + } +} + +/// Synthesize waveform data using GFSK phase shaping. +/// The output waveform will contain n_sym symbols. +/// @param[in] symbols Array of symbols (tones) (0-7 for FT8) +/// @param[in] n_sym Number of symbols in the symbol array +/// @param[in] f0 Audio frequency in Hertz for the symbol 0 (base frequency) +/// @param[in] symbol_bt Symbol smoothing filter bandwidth (2 for FT8, 1 for FT4) +/// @param[in] symbol_period Symbol period (duration), seconds +/// @param[in] signal_rate Sample rate of synthesized signal, Hertz +/// @param[out] signal Output array of signal waveform samples (should have space for n_sym*n_spsym samples) +/// +void synth_gfsk(const uint8_t* symbols, int n_sym, float f0, float symbol_bt, float symbol_period, int signal_rate, float* signal) +{ + int n_spsym = (int)(0.5f + signal_rate * symbol_period); // Samples per symbol + int n_wave = n_sym * n_spsym; // Number of output samples + float hmod = 1.0f; + + // Compute the smoothed frequency waveform. + // Length = (nsym+2)*n_spsym samples, first and last symbols extended + float *dphi = malloc((n_wave + 2 * n_spsym) * sizeof(float)); + if (dphi != NULL) { + float dphi_peak = 2 * M_PI * hmod / n_spsym; + + // Shift frequency up by f0 + for (int i = 0; i < n_wave + 2 * n_spsym; ++i) + { + dphi[i] = fmodf(2 * M_PI * f0 / signal_rate, 2.0 * M_PI); + } + + float pulse[3 * n_spsym]; + gfsk_pulse(n_spsym, symbol_bt, pulse); + + for (int i = 0; i < n_sym; ++i) + { + int ib = i * n_spsym; + for (int j = 0; j < 3 * n_spsym; ++j) + { + dphi[j + ib] += dphi_peak * symbols[i] * pulse[j]; + } + } + + // Add dummy symbols at beginning and end with tone values equal to 1st and last symbol, respectively + for (int j = 0; j < 2 * n_spsym; ++j) + { + dphi[j] += dphi_peak * pulse[j + n_spsym] * symbols[0]; + dphi[j + n_sym * n_spsym] += dphi_peak * pulse[j] * symbols[n_sym - 1]; + } + + // Calculate and insert the audio waveform + float phi = 0; + for (int k = 0; k < n_wave; ++k) + { // Don't include dummy symbols + signal[k] = sinf(phi); + phi = fmodf(phi + dphi[k + n_spsym], 2 * M_PI); + } + + // Apply envelope shaping to the first and last symbols + int n_ramp = n_spsym / 8; + for (int i = 0; i < n_ramp; ++i) + { + float env = (1 - cosf(2 * M_PI * i / (2 * n_ramp))) / 2; + signal[i] *= env; + signal[n_wave - 1 - i] *= env; + } + + free(dphi); + } +} // Returns 1 if an odd number of bits are set in x, zero otherwise static uint8_t parity8(uint8_t x) { @@ -192,3 +286,40 @@ void ft4_encode(const uint8_t* payload, uint8_t* tones) } } } + +// generate FT4 or FT8 signal for message +int ftx_encode(char *message, float *signal, int num_samples, float frequency, int sample_rate, ftx_protocol_t protocol) +{ + // First, pack the text data into binary message + uint8_t packed[FTX_LDPC_K_BYTES]; + int rc = pack77(message, packed); + + if (rc >= 0) { + // Second, encode the binary message as a sequence of FSK tones + const int num_tones = protocol == PROTO_FT4 ? FT4_NN : FT8_NN; + const float symbol_period = protocol == PROTO_FT4 ? FT4_SYMBOL_PERIOD : FT8_SYMBOL_PERIOD; + const float symbol_bt = protocol == PROTO_FT4 ? FT4_SYMBOL_BT : FT8_SYMBOL_BT; + int real_num_samples = num_tones * symbol_period * sample_rate + 0.5; + uint8_t tones[num_tones]; + + // check if we have enough space + if (num_samples >= real_num_samples) { + + if (protocol == PROTO_FT4) { + ft4_encode(packed, tones); + } else { + ft8_encode(packed, tones); + } + + // Third, convert the FSK tones into an audio signal + // Synthesize waveform data (signal) and save it as WAV file + synth_gfsk(tones, num_tones, frequency, symbol_bt, symbol_period, sample_rate, signal); + + // clear extra samples + memset(signal + real_num_samples, 0, (num_samples - real_num_samples) * sizeof(float)); + + return 0; + } + } + return -1; +} diff --git a/ft8/ft8.h b/ft8/ft8.h index c6bcdf1..0c7bb59 100644 --- a/ft8/ft8.h +++ b/ft8/ft8.h @@ -6,15 +6,12 @@ typedef enum { PROTO_FT4, PROTO_FT8 } ftx_protocol_t; // decoder callback -typedef void (*ft8_decode_callback_t)(char *message, float frequency, float time_dev, float snr, int score, void *ctx); +typedef void (*ftx_decode_callback_t)(char *message, float frequency, float time_dev, float snr, int score, void *ctx); -// decode FT8 signal, call callback for every decoded message -int ftx_decode(float *signal, int num_samples, int sample_rate, ft8_decode_callback_t callback, void *ctx); +// decode FT4 or FT8 signal +int ftx_decode(float *signal, int num_samples, int sample_rate, ftx_protocol_t protocol, ftx_decode_callback_t callback, void *ctx); -// generate FT4 signal for message -int ft4_encode(char *message, float *signal, int num_samples, float frequency, int sample_rate); - -// generate FT8 signal for message -int ft8_encode(char *message, float *signal, int num_samples, float frequency, int sample_rate); +// generate FT4 or FT8 signal for message +int ftx_encode(char *message, float *signal, int num_samples, float frequency, int sample_rate, ftx_protocol_t protocol); #endif // _INCLUDE_FT8_H_ diff --git a/ft8/ldpc.h b/ft8/ldpc.h index 2b4047e..9d4f402 100644 --- a/ft8/ldpc.h +++ b/ft8/ldpc.h @@ -3,22 +3,13 @@ #include -#ifdef __cplusplus -extern "C" -{ -#endif +// codeword is 174 log-likelihoods. +// plain is a return value, 174 ints, to be 0 or 1. +// iters is how hard to try. +// ok == 87 means success. +void ldpc_decode(float codeword[], int max_iters, uint8_t plain[], int *ok); - // codeword is 174 log-likelihoods. - // plain is a return value, 174 ints, to be 0 or 1. - // iters is how hard to try. - // ok == 87 means success. - void ldpc_decode(float codeword[], int max_iters, uint8_t plain[], int* ok); - - void bp_decode(float codeword[], int max_iters, uint8_t plain[], int* ok); - -#ifdef __cplusplus -} -#endif +void bp_decode(float codeword[], int max_iters, uint8_t plain[], int *ok); #endif // _INCLUDE_LDPC_H_ diff --git a/ft8/pack.c b/ft8/pack.c index 8244436..e80c61c 100644 --- a/ft8/pack.c +++ b/ft8/pack.c @@ -5,6 +5,7 @@ #include #include #include +#include #define NTOKENS ((uint32_t)2063592L) #define MAX22 ((uint32_t)4194304L) @@ -53,7 +54,7 @@ int32_t pack28(const char* callsign) memcpy(c6, "3D0", 3); memcpy(c6 + 3, callsign + 4, length - 4); } - else if (starts_with(callsign, "3X") && is_letter(callsign[2]) && length <= 7) + else if (starts_with(callsign, "3X") && isalpha(callsign[2]) && length <= 7) { // Work-around for Guinea prefixes: 3XA0XYZ -> QA0XYZ memcpy(c6, "Q", 1); @@ -61,12 +62,12 @@ int32_t pack28(const char* callsign) } else { - if (is_digit(callsign[2]) && length <= 6) + if (isdigit(callsign[2]) && length <= 6) { // AB0XYZ memcpy(c6, callsign, length); } - else if (is_digit(callsign[1]) && length <= 5) + else if (isdigit(callsign[1]) && length <= 5) { // A0XYZ -> " A0XYZ" memcpy(c6 + 1, callsign, length); @@ -128,15 +129,16 @@ uint16_t packgrid(const char* grid4) } // Take care of special cases - if (equals(grid4, "RRR")) + if (strcmp(grid4, "RRR") == 0) { return MAXGRID4 + 2; - if (equals(grid4, "RR73")) + } else if (strcmp(grid4, "RR73") == 0) { return MAXGRID4 + 3; - if (equals(grid4, "73")) + } else if (strcmp(grid4, "73") == 0) { return MAXGRID4 + 4; - + } + // Check for standard 4 letter grid - if (in_range(grid4[0], 'A', 'R') && in_range(grid4[1], 'A', 'R') && is_digit(grid4[2]) && is_digit(grid4[3])) + if (in_range(grid4[0], 'A', 'R') && in_range(grid4[1], 'A', 'R') && isdigit(grid4[2]) && isdigit(grid4[3])) { uint16_t igrid4 = (grid4[0] - 'A'); igrid4 = igrid4 * 18 + (grid4[1] - 'A'); diff --git a/ft8/pack.h b/ft8/pack.h index 42ab248..4c03c9e 100644 --- a/ft8/pack.h +++ b/ft8/pack.h @@ -3,18 +3,9 @@ #include -#ifdef __cplusplus -extern "C" -{ -#endif - - // Pack FT8 text message into 72 bits - // [IN] msg - FT8 message (e.g. "CQ TE5T KN01") - // [OUT] c77 - 10 byte array to store the 77 bit payload (MSB first) - int pack77(const char* msg, uint8_t* c77); - -#ifdef __cplusplus -} -#endif +// Pack FT8 text message into 72 bits +// [IN] msg - FT8 message (e.g. "CQ TE5T KN01") +// [OUT] c77 - 10 byte array to store the 77 bit payload (MSB first) +int pack77(const char* msg, uint8_t* c77); #endif // _INCLUDE_PACK_H_ diff --git a/ft8/text.c b/ft8/text.c index a624844..81e1e67 100644 --- a/ft8/text.c +++ b/ft8/text.c @@ -1,6 +1,6 @@ #include "text.h" - #include +#include const char* trim_front(const char* str) { @@ -15,7 +15,7 @@ const char* trim_front(const char* str) void trim_back(char* str) { // Skip trailing whitespace by replacing it with '\0' characters - int idx = strlen(str) - 1; + size_t idx = strlen(str) - 1; while (idx >= 0 && str[idx] == ' ') { str[idx--] = '\0'; @@ -32,26 +32,6 @@ char* trim(char* str) return str; } -char to_upper(char c) -{ - return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c; -} - -bool is_digit(char c) -{ - return (c >= '0') && (c <= '9'); -} - -bool is_letter(char c) -{ - return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')); -} - -bool is_space(char c) -{ - return (c == ' '); -} - bool in_range(char c, char min, char max) { return (c >= min) && (c <= max); @@ -62,11 +42,6 @@ bool starts_with(const char* string, const char* prefix) return 0 == memcmp(string, prefix, strlen(prefix)); } -bool equals(const char* string1, const char* string2) -{ - return 0 == strcmp(string1, string2); -} - int char_index(const char* string, char c) { for (int i = 0; *string; ++i, ++string) @@ -79,26 +54,6 @@ int char_index(const char* string, char c) return -1; // Not found } -// Text message formatting: -// - replaces lowercase letters with uppercase -// - merges consecutive spaces into single space -void format_message(char* msg_out, const char* msg_in) -{ - char c; - char last_out = 0; - while ((c = *msg_in)) - { - if (c != ' ' || last_out != ' ') - { - last_out = to_upper(c); - *msg_out = last_out; - ++msg_out; - } - ++msg_in; - } - *msg_out = 0; // Add zero termination -} - // Parse a 2 digit integer from string int dd_to_int(const char* str, int length) { @@ -120,7 +75,7 @@ int dd_to_int(const char* str, int length) { if (str[i] == 0) break; - if (!is_digit(str[i])) + if (!isdigit(str[i])) break; result *= 10; result += (str[i] - '0'); diff --git a/ft8/text.h b/ft8/text.h index 050e8b7..84e98dd 100644 --- a/ft8/text.h +++ b/ft8/text.h @@ -4,44 +4,25 @@ #include #include -#ifdef __cplusplus -extern "C" -{ -#endif +// Utility functions for characters and strings - // Utility functions for characters and strings +const char *trim_front(const char* str); +void trim_back(char *str); +char *trim(char *str); - const char* trim_front(const char* str); - void trim_back(char* str); - char* trim(char* str); +bool in_range(char c, char min, char max); +bool starts_with(const char* string, const char* prefix); - char to_upper(char c); - bool is_digit(char c); - bool is_letter(char c); - bool is_space(char c); - bool in_range(char c, char min, char max); - bool starts_with(const char* string, const char* prefix); - bool equals(const char* string1, const char* string2); +int char_index(const char* string, char c); - int char_index(const char* string, char c); +// Parse a 2 digit integer from string +int dd_to_int(const char* str, int length); - // Text message formatting: - // - replaces lowercase letters with uppercase - // - merges consecutive spaces into single space - void format_message(char* msg_out, const char* msg_in); +// Convert a 2 digit integer to string +void int_to_dd(char* str, int value, int width, bool full_sign); - // Parse a 2 digit integer from string - int dd_to_int(const char* str, int length); - - // Convert a 2 digit integer to string - void int_to_dd(char* str, int value, int width, bool full_sign); - - char charn(int c, int table_idx); - int nchar(char c, int table_idx); - -#ifdef __cplusplus -} -#endif +char charn(int c, int table_idx); +int nchar(char c, int table_idx); #endif // _INCLUDE_TEXT_H_ diff --git a/ft8/unpack.h b/ft8/unpack.h index c3f544c..6e09db8 100644 --- a/ft8/unpack.h +++ b/ft8/unpack.h @@ -3,21 +3,12 @@ #include -#ifdef __cplusplus -extern "C" -{ -#endif +// field1 - at least 14 bytes +// field2 - at least 14 bytes +// field3 - at least 7 bytes +int unpack77_fields(const uint8_t* a77, char* field1, char* field2, char* field3); - // field1 - at least 14 bytes - // field2 - at least 14 bytes - // field3 - at least 7 bytes - int unpack77_fields(const uint8_t* a77, char* field1, char* field2, char* field3); - - // message should have at least 35 bytes allocated (34 characters + zero terminator) - int unpack77(const uint8_t* a77, char* message); - -#ifdef __cplusplus -} -#endif +// message should have at least 35 bytes allocated (34 characters + zero terminator) +int unpack77(const uint8_t* a77, char* message); #endif // _INCLUDE_UNPACK_H_ diff --git a/gen_ft8.c b/gen_ft8.c index 25df8c2..9f1ce9f 100644 --- a/gen_ft8.c +++ b/gen_ft8.c @@ -6,7 +6,7 @@ #include "common/wave.h" #include "ft8.h" -#define FT4_SLOT_TIME 7.0f // total length of output waveform in seconds +#define FT4_SLOT_TIME 6.0f // total length of output waveform in seconds #define FT8_SLOT_TIME 15.0f // total length of output waveform in seconds int main(int argc, char **argv) @@ -25,7 +25,7 @@ int main(int argc, char **argv) frequency = atof(argv[3]); } - int rc = is_ft4 ? ft4_encode(argv[1], signal, num_samples, frequency, sample_rate) : ft8_encode(argv[1], signal, num_samples, 1000.0, 8000.0); + int rc = ftx_encode(argv[1], signal, num_samples, frequency, sample_rate, is_ft4 ? PROTO_FT4 : PROTO_FT8); if (rc == 0) { save_wav(signal, num_samples, sample_rate, argv[2]); } else { diff --git a/looptest.c b/looptest.c index 6972b28..69100ce 100644 --- a/looptest.c +++ b/looptest.c @@ -6,7 +6,12 @@ #include "ft8.h" -#define FT4_SLOT_TIME 7.0f // total length of output waveform in seconds + +#warning remove again +#include +#include + +#define FT4_SLOT_TIME 6.0f // total length of output waveform in seconds #define FT8_SLOT_TIME 15.0f // total length of output waveform in seconds // white noise added - decoding errors start to show up around 12.0 @@ -20,7 +25,7 @@ struct context { float frequency; }; -static char* random_callsign(char* callsign) +static char *random_callsign(char *callsign) { int x = rand(); switch (x >> 29) { @@ -40,14 +45,14 @@ static char* random_callsign(char* callsign) return callsign; } -static char* random_locator(char* locator) +static char *random_locator(char* locator) { int x = rand(); sprintf(locator, "%c%c%d%d", 'A' + RP(x, 1, 18), 'A' + RP(x, 18, 18), RP(x, 180, 10), RP(x, 1800, 10)); return locator; } -static char* random_message(char* message) +static char *random_message(char* message) { int x = rand(); char callsign1[8], callsign2[8], locator[5]; @@ -79,38 +84,48 @@ static void ft8_decode_callback(char *message, float frequency, float time_dev, { struct context *context = ctx; bool ok = strcmp(context->message, message) == 0; - printf("%-8s000000 %3d %+4.2f %4.0f ~ %s (%s)\n", ok ? "OK" : "ERROR", score, time_dev, frequency, message, context->message); + // printf("%-8s000000 %3d %+4.2f %4.0f ~ %s (%s)\n", ok ? "OK" : "ERROR", score, time_dev, frequency, message, context->message); } int main(int argc, char *argv[]) { + int iterations = 1000; int sample_rate = 8000; float frequency = 1200.0; - int num_samples = FT8_SLOT_TIME * sample_rate; + ftx_protocol_t protocol = PROTO_FT4; + int num_samples = (protocol == PROTO_FT4 ? FT4_SLOT_TIME : FT8_SLOT_TIME) * sample_rate; float signal[num_samples]; - + + // start time measurement + clock_t start = clock(); + // initialize random number generator srand((unsigned int)time(NULL)); + // run loop test - for (int i = 0; i < 100; i++) { + for (int i = 0; i < iterations; i++) { struct context ctx; + int r; // generate random but valid message random_message(ctx.message); ctx.frequency = frequency; - if (ft8_encode(ctx.message, signal, num_samples, frequency, sample_rate) == 0) { + if (ftx_encode(ctx.message, signal, num_samples, frequency, sample_rate, protocol) == 0) { // add noise for (float* fp = signal; fp < signal + num_samples; fp++) { *fp = (*fp + 2.0 * NOISE_AMPLITUDE * rand() / RAND_MAX - NOISE_AMPLITUDE) / (1.0 + NOISE_AMPLITUDE); } - if (ftx_decode(signal, num_samples, sample_rate, ft8_decode_callback, &ctx) != 1) { - printf("*** ERROR decoding (%s)\n", ctx.message); + r = ftx_decode(signal, num_samples, sample_rate, protocol, ft8_decode_callback, &ctx); + if (r != 1) { + printf("*** ERROR decoding (%s, %d)\n", ctx.message, r); } } else { printf("*** ERROR encoding (%s)\n", ctx.message); } } + + printf("Time per decode: %f ms\n", 1000.0 * (clock() - start) / CLOCKS_PER_SEC / iterations); }