Working version without SNR calculation

pull/21/head
chrnadig 2022-05-05 22:03:14 +02:00
rodzic ea7604f936
commit 3502d47cf0
14 zmienionych plików z 226 dodań i 205 usunięć

Wyświetl plik

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

Wyświetl plik

@ -3,11 +3,6 @@
#include <stdint.h>
#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_

Wyświetl plik

@ -4,28 +4,19 @@
#include <stdint.h>
#include <stdbool.h>
#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_

Wyświetl plik

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

Wyświetl plik

@ -1,8 +1,102 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "ft8.h"
#include "constants.h"
#include "crc.h"
#include "pack.h"
#include <stdio.h>
#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;
}

Wyświetl plik

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

Wyświetl plik

@ -3,22 +3,13 @@
#include <stdint.h>
#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_

Wyświetl plik

@ -5,6 +5,7 @@
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#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');

Wyświetl plik

@ -3,18 +3,9 @@
#include <stdint.h>
#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_

Wyświetl plik

@ -1,6 +1,6 @@
#include "text.h"
#include <string.h>
#include <ctype.h>
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');

Wyświetl plik

@ -4,44 +4,25 @@
#include <stdbool.h>
#include <stdint.h>
#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_

Wyświetl plik

@ -3,21 +3,12 @@
#include <stdint.h>
#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_

Wyświetl plik

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

Wyświetl plik

@ -6,7 +6,12 @@
#include "ft8.h"
#define FT4_SLOT_TIME 7.0f // total length of output waveform in seconds
#warning remove again
#include <fcntl.h>
#include <unistd.h>
#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);
}