diff --git a/common/wave.h b/common/wave.h index def1296..a2176df 100644 --- a/common/wave.h +++ b/common/wave.h @@ -1,10 +1,19 @@ #ifndef _INCLUDE_WAVE_H_ #define _INCLUDE_WAVE_H_ -// Save signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers. -void save_wav(const float* signal, int num_samples, int sample_rate, const char* path); +#ifdef __cplusplus +extern "C" +{ +#endif -// Load signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers. -int load_wav(float* signal, int* num_samples, int* sample_rate, const char* path); + // Save signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers. + void save_wav(const float* signal, int num_samples, int sample_rate, const char* path); + + // Load signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers. + int load_wav(float* signal, int* num_samples, int* sample_rate, const char* path); + +#ifdef __cplusplus +} +#endif #endif // _INCLUDE_WAVE_H_ diff --git a/ft8/constants.h b/ft8/constants.h index b7531a4..980cc7f 100644 --- a/ft8/constants.h +++ b/ft8/constants.h @@ -3,11 +3,10 @@ #include -typedef enum +#ifdef __cplusplus +extern "C" { - PROTO_FT4, - PROTO_FT8 -} ftx_protocol_t; +#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 @@ -50,32 +49,42 @@ typedef enum #define FT8_CRC_POLYNOMIAL ((uint16_t)0x2757u) ///< CRC-14 polynomial without the leading (MSB) 1 #define FT8_CRC_WIDTH (14) -/// Costas 7x7 tone pattern for synchronization -extern const uint8_t kFT8_Costas_pattern[7]; -extern const uint8_t kFT4_Costas_pattern[4][4]; + typedef enum + { + PROTO_FT4, + PROTO_FT8 + } ftx_protocol_t; -/// Gray code map to encode 8 symbols (tones) -extern const uint8_t kFT8_Gray_map[8]; -extern const uint8_t kFT4_Gray_map[4]; + /// Costas 7x7 tone pattern for synchronization + extern const uint8_t kFT8_Costas_pattern[7]; + extern const uint8_t kFT4_Costas_pattern[4][4]; -extern const uint8_t kFT4_XOR_sequence[10]; + /// Gray code map to encode 8 symbols (tones) + extern const uint8_t kFT8_Gray_map[8]; + extern const uint8_t kFT4_Gray_map[4]; -/// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first) -extern const uint8_t kFTX_LDPC_generator[FTX_LDPC_M][FTX_LDPC_K_BYTES]; + extern const uint8_t kFT4_XOR_sequence[10]; -/// 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]; + /// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first) + extern const uint8_t kFTX_LDPC_generator[FTX_LDPC_M][FTX_LDPC_K_BYTES]; -/// 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]; + /// 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]; -/// Number of rows (columns in C/C++) in the array Nm. -extern const uint8_t kFTX_LDPC_Num_rows[FTX_LDPC_M]; + /// 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 b510122..8585150 100644 --- a/ft8/crc.h +++ b/ft8/crc.h @@ -4,19 +4,28 @@ #include #include -// 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); +#ifdef __cplusplus +extern "C" +{ +#endif -/// 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[]); + // 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); -/// 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[]); + /// 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 #endif // _INCLUDE_CRC_H_ \ No newline at end of file diff --git a/ft8/decode.h b/ft8/decode.h index 22b536f..6ae3d14 100644 --- a/ft8/decode.h +++ b/ft8/decode.h @@ -6,69 +6,78 @@ #include "constants.h" -/// Input structure to ft8_find_sync() function. This structure describes stored waterfall data over the whole message slot. -/// Fields time_osr and freq_osr specify additional oversampling rate for time and frequency resolution. -/// If time_osr=1, FFT magnitude data is collected once for every symbol transmitted, i.e. every 1/6.25 = 0.16 seconds. -/// Values time_osr > 1 mean each symbol is further subdivided in time. -/// If freq_osr=1, each bin in the FFT magnitude data corresponds to 6.25 Hz, which is the tone spacing. -/// Values freq_osr > 1 mean the tone spacing is further subdivided by FFT analysis. -typedef struct +#ifdef __cplusplus +extern "C" { - int max_blocks; ///< number of blocks (symbols) allocated in the mag array - int num_blocks; ///< number of blocks (symbols) stored in the mag array - int num_bins; ///< number of FFT bins in terms of 6.25 Hz - int time_osr; ///< number of time subdivisions - int freq_osr; ///< number of frequency subdivisions - uint8_t* mag; ///< FFT magnitudes stored as uint8_t[blocks][time_osr][freq_osr][num_bins] - int block_stride; ///< Helper value = time_osr * freq_osr * num_bins - ftx_protocol_t protocol; ///< Indicate if using FT4 or FT8 -} waterfall_t; +#endif -/// Output structure of ft8_find_sync() and input structure of ft8_decode(). -/// Holds the position of potential start of a message in time and frequency. -typedef struct -{ - int16_t score; ///< Candidate score (non-negative number; higher score means higher likelihood) - int16_t time_offset; ///< Index of the time block - int16_t freq_offset; ///< Index of the frequency bin - uint8_t time_sub; ///< Index of the time subdivision used - uint8_t freq_sub; ///< Index of the frequency subdivision used -} candidate_t; + /// Input structure to ft8_find_sync() function. This structure describes stored waterfall data over the whole message slot. + /// Fields time_osr and freq_osr specify additional oversampling rate for time and frequency resolution. + /// If time_osr=1, FFT magnitude data is collected once for every symbol transmitted, i.e. every 1/6.25 = 0.16 seconds. + /// Values time_osr > 1 mean each symbol is further subdivided in time. + /// If freq_osr=1, each bin in the FFT magnitude data corresponds to 6.25 Hz, which is the tone spacing. + /// Values freq_osr > 1 mean the tone spacing is further subdivided by FFT analysis. + typedef struct + { + int max_blocks; ///< number of blocks (symbols) allocated in the mag array + int num_blocks; ///< number of blocks (symbols) stored in the mag array + int num_bins; ///< number of FFT bins in terms of 6.25 Hz + int time_osr; ///< number of time subdivisions + int freq_osr; ///< number of frequency subdivisions + uint8_t* mag; ///< FFT magnitudes stored as uint8_t[blocks][time_osr][freq_osr][num_bins] + int block_stride; ///< Helper value = time_osr * freq_osr * num_bins + ftx_protocol_t protocol; ///< Indicate if using FT4 or FT8 + } waterfall_t; -/// Structure that holds the decoded message -typedef struct -{ - // TODO: check again that this size is enough - char text[25]; ///< Plain text - uint16_t hash; ///< Hash value to be used in hash table and quick checking for duplicates -} message_t; + /// Output structure of ft8_find_sync() and input structure of ft8_decode(). + /// Holds the position of potential start of a message in time and frequency. + typedef struct + { + int16_t score; ///< Candidate score (non-negative number; higher score means higher likelihood) + int16_t time_offset; ///< Index of the time block + int16_t freq_offset; ///< Index of the frequency bin + uint8_t time_sub; ///< Index of the time subdivision used + uint8_t freq_sub; ///< Index of the frequency subdivision used + } candidate_t; -/// Structure that contains the status of various steps during decoding of a message -typedef struct -{ - int ldpc_errors; ///< Number of LDPC errors during decoding - uint16_t crc_extracted; ///< CRC value recovered from the message - uint16_t crc_calculated; ///< CRC value calculated over the payload - int unpack_status; ///< Return value of the unpack routine -} decode_status_t; + /// Structure that holds the decoded message + typedef struct + { + // TODO: check again that this size is enough + char text[25]; ///< Plain text + uint16_t hash; ///< Hash value to be used in hash table and quick checking for duplicates + } message_t; -/// Localize top N candidates in frequency and time according to their sync strength (looking at Costas symbols) -/// We treat and organize the candidate list as a min-heap (empty initially). -/// @param[in] power Waterfall data collected during message slot -/// @param[in] sync_pattern Synchronization pattern -/// @param[in] num_candidates Number of maximum candidates (size of heap array) -/// @param[in,out] heap Array of candidate_t type entries (with num_candidates allocated entries) -/// @param[in] min_score Minimal score allowed for pruning unlikely candidates (can be zero for no effect) -/// @return Number of candidates filled in the heap -int ft8_find_sync(const waterfall_t* power, int num_candidates, candidate_t heap[], int min_score); + /// Structure that contains the status of various steps during decoding of a message + typedef struct + { + int ldpc_errors; ///< Number of LDPC errors during decoding + uint16_t crc_extracted; ///< CRC value recovered from the message + uint16_t crc_calculated; ///< CRC value calculated over the payload + int unpack_status; ///< Return value of the unpack routine + } decode_status_t; -/// Attempt to decode a message candidate. Extracts the bit probabilities, runs LDPC decoder, checks CRC and unpacks the message in plain text. -/// @param[in] power Waterfall data collected during message slot -/// @param[in] cand Candidate to decode -/// @param[out] message message_t structure that will receive the decoded message -/// @param[in] max_iterations Maximum allowed LDPC iterations (lower number means faster decode, but less precise) -/// @param[out] status decode_status_t structure that will be filled with the status of various decoding steps -/// @return True if the decoding was successful, false otherwise (check status for details) -bool ft8_decode(const waterfall_t* power, const candidate_t* cand, message_t* message, int max_iterations, decode_status_t* status); + /// Localize top N candidates in frequency and time according to their sync strength (looking at Costas symbols) + /// We treat and organize the candidate list as a min-heap (empty initially). + /// @param[in] power Waterfall data collected during message slot + /// @param[in] sync_pattern Synchronization pattern + /// @param[in] num_candidates Number of maximum candidates (size of heap array) + /// @param[in,out] heap Array of candidate_t type entries (with num_candidates allocated entries) + /// @param[in] min_score Minimal score allowed for pruning unlikely candidates (can be zero for no effect) + /// @return Number of candidates filled in the heap + int ft8_find_sync(const waterfall_t* power, int num_candidates, candidate_t heap[], int min_score); + + /// Attempt to decode a message candidate. Extracts the bit probabilities, runs LDPC decoder, checks CRC and unpacks the message in plain text. + /// @param[in] power Waterfall data collected during message slot + /// @param[in] cand Candidate to decode + /// @param[out] message message_t structure that will receive the decoded message + /// @param[in] max_iterations Maximum allowed LDPC iterations (lower number means faster decode, but less precise) + /// @param[out] status decode_status_t structure that will be filled with the status of various decoding steps + /// @return True if the decoding was successful, false otherwise (check status for details) + bool ft8_decode(const waterfall_t* power, const candidate_t* cand, message_t* message, int max_iterations, decode_status_t* status); + +#ifdef __cplusplus +} +#endif #endif // _INCLUDE_DECODE_H_ diff --git a/ft8/encode.h b/ft8/encode.h index d5e2759..7a07120 100644 --- a/ft8/encode.h +++ b/ft8/encode.h @@ -3,30 +3,39 @@ #include -// typedef struct -// { -// uint8_t tones[FT8_NN]; -// // for waveform readout: -// int n_spsym; // Number of waveform samples per symbol -// float *pulse; // [3 * n_spsym] -// int idx_symbol; // Index of the current symbol -// float f0; // Base frequency, Hertz -// float signal_rate; // Waveform sample rate, Hertz -// } encoder_t; +#ifdef __cplusplus +extern "C" +{ +#endif -// void encoder_init(float signal_rate, float *pulse_buffer); -// void encoder_set_f0(float f0); -// void encoder_process(const message_t *message); // in: message -// void encoder_generate(float *block); // out: block of waveforms + // typedef struct + // { + // uint8_t tones[FT8_NN]; + // // for waveform readout: + // int n_spsym; // Number of waveform samples per symbol + // float *pulse; // [3 * n_spsym] + // int idx_symbol; // Index of the current symbol + // float f0; // Base frequency, Hertz + // float signal_rate; // Waveform sample rate, Hertz + // } encoder_t; -/// Generate FT8 tone sequence from payload data -/// @param[in] payload - 10 byte array consisting of 77 bit payload -/// @param[out] tones - array of FT8_NN (79) bytes to store the generated tones (encoded as 0..7) -void ft8_encode(const uint8_t* payload, uint8_t* tones); + // void encoder_init(float signal_rate, float *pulse_buffer); + // void encoder_set_f0(float f0); + // void encoder_process(const message_t *message); // in: message + // void encoder_generate(float *block); // out: block of waveforms -/// Generate FT4 tone sequence from payload data -/// @param[in] payload - 10 byte array consisting of 77 bit payload -/// @param[out] tones - array of FT4_NN (105) bytes to store the generated tones (encoded as 0..3) -void ft4_encode(const uint8_t* payload, uint8_t* tones); + /// Generate FT8 tone sequence from payload data + /// @param[in] payload - 10 byte array consisting of 77 bit payload + /// @param[out] tones - array of FT8_NN (79) bytes to store the generated tones (encoded as 0..7) + void ft8_encode(const uint8_t* payload, uint8_t* tones); + + /// Generate FT4 tone sequence from payload data + /// @param[in] payload - 10 byte array consisting of 77 bit payload + /// @param[out] tones - array of FT4_NN (105) bytes to store the generated tones (encoded as 0..3) + void ft4_encode(const uint8_t* payload, uint8_t* tones); + +#ifdef __cplusplus +} +#endif #endif // _INCLUDE_ENCODE_H_ diff --git a/ft8/ldpc.h b/ft8/ldpc.h index fd33d7a..41e56ab 100644 --- a/ft8/ldpc.h +++ b/ft8/ldpc.h @@ -3,12 +3,21 @@ #include -// 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); +#ifdef __cplusplus +extern "C" +{ +#endif -void bp_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 #endif // _INCLUDE_LDPC_H_ diff --git a/ft8/pack.h b/ft8/pack.h index 4c03c9e..42ab248 100644 --- a/ft8/pack.h +++ b/ft8/pack.h @@ -3,9 +3,18 @@ #include -// 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 +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 #endif // _INCLUDE_PACK_H_ diff --git a/ft8/text.h b/ft8/text.h index aac9921..f1af398 100644 --- a/ft8/text.h +++ b/ft8/text.h @@ -4,34 +4,43 @@ #include #include -// Utility functions for characters and strings +#ifdef __cplusplus +extern "C" +{ +#endif -const char* trim_front(const char* str); -void trim_back(char* str); -char* trim(char* str); + // Utility functions for characters and strings -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); + const char* trim_front(const char* str); + void trim_back(char* str); + char* trim(char* str); -int char_index(const char* string, char c); + 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); -// Text message formatting: -// - replaces lowercase letters with uppercase -// - merges consecutive spaces into single space -void fmtmsg(char* msg_out, const char* msg_in); + 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 fmtmsg(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); -char charn(int c, int table_idx); -int nchar(char c, int table_idx); + // 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 #endif // _INCLUDE_TEXT_H_ diff --git a/ft8/unpack.h b/ft8/unpack.h index 6e09db8..c3f544c 100644 --- a/ft8/unpack.h +++ b/ft8/unpack.h @@ -3,12 +3,21 @@ #include -// 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); +#ifdef __cplusplus +extern "C" +{ +#endif -// message should have at least 35 bytes allocated (34 characters + zero terminator) -int unpack77(const uint8_t* a77, char* message); + // 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 #endif // _INCLUDE_UNPACK_H_