diff --git a/.clang-format b/.clang-format index 0e24b80..9d0668c 100644 --- a/.clang-format +++ b/.clang-format @@ -2,6 +2,7 @@ BasedOnStyle: WebKit # Cpp11BracedListStyle: false # ColumnLimit: 120 IndentCaseLabels: false +IndentExternBlock: false IndentWidth: 4 TabWidth: 8 UseTab: Never diff --git a/decode_ft8.c b/decode_ft8.c index 8b84b87..6aa1246 100644 --- a/decode_ft8.c +++ b/decode_ft8.c @@ -69,7 +69,7 @@ void waterfall_init(waterfall_t* me, int max_blocks, int num_bins, int time_osr, me->time_osr = time_osr; me->freq_osr = freq_osr; me->block_stride = (time_osr * freq_osr * num_bins); - me->mag = (uint8_t *)malloc(mag_size); + me->mag = (uint8_t*)malloc(mag_size); LOG(LOG_DEBUG, "Waterfall size = %zu\n", mag_size); } @@ -94,14 +94,16 @@ typedef struct typedef struct { float symbol_period; ///< FT4/FT8 symbol period in seconds - int block_size; ///< Number of samples per symbol (block) - int subblock_size; ///< Analysis shift size (number of samples) - int nfft; ///< FFT size - float fft_norm; ///< FFT normalization factor - float* window; ///< Window function for STFT analysis (nfft samples) - float* last_frame; ///< Current STFT analysis frame (nfft samples) - waterfall_t wf; ///< Waterfall object - float max_mag; ///< Maximum detected magnitude (debug stats) + int min_bin; + int max_bin; + int block_size; ///< Number of samples per symbol (block) + int subblock_size; ///< Analysis shift size (number of samples) + int nfft; ///< FFT size + float fft_norm; ///< FFT normalization factor + float* window; ///< Window function for STFT analysis (nfft samples) + float* last_frame; ///< Current STFT analysis frame (nfft samples) + waterfall_t wf; ///< Waterfall object + float max_mag; ///< Maximum detected magnitude (debug stats) // KISS FFT housekeeping variables void* fft_work; ///< Work area required by Kiss FFT @@ -119,7 +121,7 @@ void monitor_init(monitor_t* me, const monitor_config_t* cfg) me->fft_norm = 2.0f / me->nfft; // const int len_window = 1.8f * me->block_size; // hand-picked and optimized - me->window = (float *)malloc(me->nfft * sizeof(me->window[0])); + me->window = (float*)malloc(me->nfft * sizeof(me->window[0])); for (int i = 0; i < me->nfft; ++i) { // window[i] = 1; @@ -128,7 +130,7 @@ void monitor_init(monitor_t* me, const monitor_config_t* cfg) // me->window[i] = hamming_i(i, me->nfft); // me->window[i] = (i < len_window) ? hann_i(i, len_window) : 0; } - me->last_frame = (float *)malloc(me->nfft * sizeof(me->last_frame[0])); + me->last_frame = (float*)malloc(me->nfft * sizeof(me->last_frame[0])); size_t fft_work_size; kiss_fftr_alloc(me->nfft, 0, 0, &fft_work_size); @@ -141,10 +143,16 @@ void monitor_init(monitor_t* me, const monitor_config_t* cfg) me->fft_work = malloc(fft_work_size); me->fft_cfg = kiss_fftr_alloc(me->nfft, 0, me->fft_work, &fft_work_size); + // Allocate enough blocks to fit the entire FT8/FT4 slot in memory const int max_blocks = (int)(slot_time / symbol_period); - const int num_bins = (int)(cfg->sample_rate * symbol_period / 2); + // Keep only FFT bins in the specified frequency range (f_min/f_max) + me->min_bin = (int)(cfg->f_min * symbol_period); + me->max_bin = (int)(cfg->f_max * symbol_period) + 1; + const int num_bins = me->max_bin - me->min_bin; + waterfall_init(&me->wf, max_blocks, num_bins, cfg->time_osr, cfg->freq_osr); me->wf.protocol = cfg->protocol; + me->symbol_period = symbol_period; me->max_mag = -120.0f; @@ -196,7 +204,7 @@ void monitor_process(monitor_t* me, const float* frame) // Loop over two possible frequency bin offsets (for averaging) for (int freq_sub = 0; freq_sub < me->wf.freq_osr; ++freq_sub) { - for (int bin = 0; bin < me->wf.num_bins; ++bin) + for (int bin = me->min_bin; bin < me->max_bin; ++bin) { int src_bin = (bin * me->wf.freq_osr) + freq_sub; float mag2 = (freqdata[src_bin].i * freqdata[src_bin].i) + (freqdata[src_bin].r * freqdata[src_bin].r); @@ -322,12 +330,12 @@ int main(int argc, char** argv) if (cand->score < kMin_score) continue; - float freq_hz = (cand->freq_offset + (float)cand->freq_sub / mon.wf.freq_osr) / mon.symbol_period; + float freq_hz = (mon.min_bin + 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; message_t message; decode_status_t status; - if (!ft8_decode(&mon.wf, cand, &message, kLDPC_iterations, &status)) + if (!ft8_decode(&mon.wf, cand, &message, kLDPC_iterations, NULL, &status)) { // printf("000000 %3d %+4.2f %4.0f ~ ---\n", cand->score, time_sec, freq_hz); if (status.ldpc_errors > 0) @@ -377,8 +385,8 @@ int main(int argc, char** argv) ++num_decoded; // Fake WSJT-X-like output for now - int snr = 0; // TODO: compute SNR - printf("000000 %3d %+4.2f %4.0f ~ %s\n", cand->score, time_sec, freq_hz, message.text); + float snr = cand->score * 0.5f; // TODO: compute better approximation of SNR + printf("000000 %2.1f %+4.2f %4.0f ~ %s\n", snr, time_sec, freq_hz, message.text); } } LOG(LOG_INFO, "Decoded %d messages\n", num_decoded); diff --git a/ft8/constants.h b/ft8/constants.h index 980cc7f..2f16bf8 100644 --- a/ft8/constants.h +++ b/ft8/constants.h @@ -49,39 +49,39 @@ extern "C" #define FT8_CRC_POLYNOMIAL ((uint16_t)0x2757u) ///< CRC-14 polynomial without the leading (MSB) 1 #define FT8_CRC_WIDTH (14) - typedef enum - { - PROTO_FT4, - PROTO_FT8 - } ftx_protocol_t; +typedef enum +{ + PROTO_FT4, + PROTO_FT8 +} ftx_protocol_t; - /// Costas 7x7 tone pattern for synchronization - extern const uint8_t kFT8_Costas_pattern[7]; - extern const uint8_t kFT4_Costas_pattern[4][4]; +/// Costas 7x7 tone pattern for synchronization +extern const uint8_t kFT8_Costas_pattern[7]; +extern const uint8_t kFT4_Costas_pattern[4][4]; - /// Gray code map to encode 8 symbols (tones) - extern const uint8_t kFT8_Gray_map[8]; - extern const uint8_t kFT4_Gray_map[4]; +/// Gray code map to encode 8 symbols (tones) +extern const uint8_t kFT8_Gray_map[8]; +extern const uint8_t kFT4_Gray_map[4]; - extern const uint8_t kFT4_XOR_sequence[10]; +extern const uint8_t kFT4_XOR_sequence[10]; - /// 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]; +/// 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]; - /// 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]; +/// 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]; +/// 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]; +/// Number of rows (columns in C/C++) in the array Nm. +extern const uint8_t kFTX_LDPC_Num_rows[FTX_LDPC_M]; #ifdef __cplusplus } diff --git a/ft8/crc.h b/ft8/crc.h index 8585150..f9bc7e0 100644 --- a/ft8/crc.h +++ b/ft8/crc.h @@ -9,20 +9,20 @@ 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[]); +/// 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 } diff --git a/ft8/decode.c b/ft8/decode.c index ea14992..e30121e 100644 --- a/ft8/decode.c +++ b/ft8/decode.c @@ -32,13 +32,70 @@ static void ft4_extract_symbol(const uint8_t* wf, float* logl); static void ft8_extract_symbol(const uint8_t* wf, float* logl); static void ft8_decode_multi_symbols(const uint8_t* wf, int num_bins, int n_syms, int bit_idx, float* log174); -static int get_index(const waterfall_t* wf, const candidate_t* candidate) +static const uint8_t* get_cand_mag(const waterfall_t* wf, const candidate_t* candidate) { int offset = candidate->time_offset; offset = (offset * wf->time_osr) + candidate->time_sub; offset = (offset * wf->freq_osr) + candidate->freq_sub; offset = (offset * wf->num_bins) + candidate->freq_offset; - return offset; + return wf->mag + offset; +} + +int ft8_snr(const waterfall_t* wf, const candidate_t* candidate) +{ + int sum_signal = 0; + int sum_noise = 0; + int num_average = 0; + + // Get the pointer to symbol 0 of the candidate + const uint8_t* mag_cand = get_cand_mag(wf, candidate); + + if (wf->protocol == PROTO_FT4) + { + } + + // Compute average score over sync symbols (m+k = 0-7, 36-43, 72-79) + for (int block = 0; block < FT8_NN; ++block) + { + int block_abs = candidate->time_offset + block; // relative to the captured signal + // Check for time boundaries + if (block_abs < 0) + continue; + if (block_abs >= wf->num_blocks) + break; + + // Get the pointer to symbol 'block' of the candidate + const uint8_t* p8 = mag_cand + (block * wf->block_stride); + + int k = block % FT8_SYNC_OFFSET; + int sm = -1; + if (k < 7) + { + // Check only the neighbors of the expected symbol frequency- and time-wise + sm = kFT8_Costas_pattern[k]; // Index of the expected bin + } + else + { + int max; + for (int m = 0; m < 8; ++m) + { + if ((sm == -1) || (p8[m] > max)) + { + sm = m; + max = p8[m]; + } + } + } + + if (sm != -1) + { + sum_signal += p8[sm]; + sum_noise += (3 + (int)p8[0] + (int)p8[1] + (int)p8[2] + (int)p8[3] + (int)p8[4] + (int)p8[5] + (int)p8[6] + (int)p8[7] - (int)p8[sm]) / 7; + ++num_average; + } + } + // return num_average; + return (sum_signal - sum_noise) / num_average; } static int ft8_sync_score(const waterfall_t* wf, const candidate_t* candidate) @@ -47,7 +104,7 @@ static int ft8_sync_score(const waterfall_t* wf, const candidate_t* candidate) int num_average = 0; // Get the pointer to symbol 0 of the candidate - const uint8_t* mag_cand = wf->mag + get_index(wf, candidate); + const uint8_t* mag_cand = get_cand_mag(wf, candidate); // Compute average score over sync symbols (m+k = 0-7, 36-43, 72-79) for (int m = 0; m < FT8_NUM_SYNC; ++m) @@ -113,7 +170,7 @@ static int ft4_sync_score(const waterfall_t* wf, const candidate_t* candidate) int num_average = 0; // Get the pointer to symbol 0 of the candidate - const uint8_t* mag_cand = wf->mag + get_index(wf, candidate); + const uint8_t* mag_cand = get_cand_mag(wf, candidate); // Compute average score over sync symbols (block = 1-4, 34-37, 67-70, 100-103) for (int m = 0; m < FT4_NUM_SYNC; ++m) @@ -193,6 +250,7 @@ int ft8_find_sync(const waterfall_t* wf, int num_candidates, candidate_t heap[], else { candidate.score = ft8_sync_score(wf, &candidate); + // candidate.score = ft8_snr(wf, &candidate); } if (candidate.score < min_score) @@ -235,7 +293,7 @@ int ft8_find_sync(const waterfall_t* wf, int num_candidates, candidate_t heap[], static void ft4_extract_likelihood(const waterfall_t* wf, const candidate_t* cand, float* log174) { - const uint8_t* mag_cand = wf->mag + get_index(wf, cand); + const uint8_t* mag_cand = get_cand_mag(wf, cand); // Go over FSK tones and skip Costas sync symbols for (int k = 0; k < FT4_ND; ++k) @@ -264,7 +322,7 @@ static void ft4_extract_likelihood(const waterfall_t* wf, const candidate_t* can static void ft8_extract_likelihood(const waterfall_t* wf, const candidate_t* cand, float* log174) { - const uint8_t* mag_cand = wf->mag + get_index(wf, cand); + const uint8_t* mag_cand = get_cand_mag(wf, cand); // Go over FSK tones and skip Costas sync symbols for (int k = 0; k < FT8_ND; ++k) @@ -313,7 +371,7 @@ static void ftx_normalize_logl(float* log174) } } -bool ft8_decode(const waterfall_t* wf, const candidate_t* cand, message_t* message, int max_iterations, decode_status_t* status) +bool ft8_decode(const waterfall_t* wf, const candidate_t* cand, message_t* message, int max_iterations, const unpack_hash_interface_t* hash_if, decode_status_t* status) { float log174[FTX_LDPC_N]; // message bits encoded as likelihood if (wf->protocol == PROTO_FT4) @@ -362,7 +420,7 @@ bool ft8_decode(const waterfall_t* wf, const candidate_t* cand, message_t* messa } } - status->unpack_status = unpack77(a91, message->text); + status->unpack_status = unpack77(a91, message->text, hash_if); if (status->unpack_status < 0) { diff --git a/ft8/decode.h b/ft8/decode.h index 6ae3d14..67031b7 100644 --- a/ft8/decode.h +++ b/ft8/decode.h @@ -5,76 +5,78 @@ #include #include "constants.h" +#include "unpack.h" #ifdef __cplusplus extern "C" { #endif - /// 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; +/// 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; - /// 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; +/// 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 + int16_t snr; +} candidate_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; +/// 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; - /// 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 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; - /// 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); +/// 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); +/// 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, const unpack_hash_interface_t* hash_if, decode_status_t* status); #ifdef __cplusplus } diff --git a/ft8/encode.h b/ft8/encode.h index 7a07120..dd3712f 100644 --- a/ft8/encode.h +++ b/ft8/encode.h @@ -8,31 +8,31 @@ extern "C" { #endif - // 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; +// 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; - // 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 +// 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 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 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); +/// 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 } diff --git a/ft8/ldpc.h b/ft8/ldpc.h index 41e56ab..02f4a99 100644 --- a/ft8/ldpc.h +++ b/ft8/ldpc.h @@ -8,13 +8,13 @@ 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); +void bp_decode(float codeword[], int max_iters, uint8_t plain[], int* ok); #ifdef __cplusplus } diff --git a/ft8/pack.c b/ft8/pack.c index c00a0d1..05594bc 100644 --- a/ft8/pack.c +++ b/ft8/pack.c @@ -10,16 +10,9 @@ #define MAX22 ((uint32_t)4194304L) #define MAXGRID4 ((uint16_t)32400) -// TODO: This is wasteful, should figure out something more elegant -const char A0[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"; -const char A1[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -const char A2[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -const char A3[] = "0123456789"; -const char A4[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - // Pack a special token, a 22-bit hash code, or a valid base call // into a 28-bit integer. -int32_t pack28(const char* callsign) +static int32_t pack28(const char* callsign) { // Check for special tokens first if (starts_with(callsign, "DE ")) @@ -74,8 +67,13 @@ int32_t pack28(const char* callsign) } // Check for standard callsign - int i0, i1, i2, i3, i4, i5; - if ((i0 = char_index(A1, c6[0])) >= 0 && (i1 = char_index(A2, c6[1])) >= 0 && (i2 = char_index(A3, c6[2])) >= 0 && (i3 = char_index(A4, c6[3])) >= 0 && (i4 = char_index(A4, c6[4])) >= 0 && (i5 = char_index(A4, c6[5])) >= 0) + int i0 = nchar(c6[0], FT8_CHAR_TABLE_ALPHANUM_SPACE); + int i1 = nchar(c6[1], FT8_CHAR_TABLE_ALPHANUM); + int i2 = nchar(c6[2], FT8_CHAR_TABLE_NUMERIC); + int i3 = nchar(c6[3], FT8_CHAR_TABLE_LETTERS_SPACE); + int i4 = nchar(c6[4], FT8_CHAR_TABLE_LETTERS_SPACE); + int i5 = nchar(c6[5], FT8_CHAR_TABLE_LETTERS_SPACE); + if ((i0 >= 0) && (i1 >= 0) && (i2 >= 0) && (i3 >= 0) && (i4 >= 0) && (i5 >= 0)) { // This is a standard callsign int32_t n28 = i0; @@ -87,8 +85,8 @@ int32_t pack28(const char* callsign) return NTOKENS + MAX22 + n28; } - //char text[13]; - //if (length > 13) return -1; + // char text[13]; + // if (length > 13) return -1; // TODO: // Treat this as a nonstandard callsign: compute its 22-bit hash @@ -98,7 +96,7 @@ int32_t pack28(const char* callsign) // Check if a string could be a valid standard callsign or a valid // compound callsign. // Return base call "bc" and a logical "cok" indicator. -bool chkcall(const char* call, char* bc) +static bool chkcall(const char* call, char* bc) { int length = strlen(call); // n1=len_trim(w) if (length > 11) @@ -119,7 +117,7 @@ bool chkcall(const char* call, char* bc) return true; } -uint16_t packgrid(const char* grid4) +static uint16_t packgrid(const char* grid4) { if (grid4 == 0) { @@ -164,14 +162,14 @@ uint16_t packgrid(const char* grid4) } // Pack Type 1 (Standard 77-bit message) and Type 2 (ditto, with a "/P" call) -int pack77_1(const char* msg, uint8_t* b77) +static int pack77_1(const char* msg, uint8_t* b77) { // Locate the first delimiter const char* s1 = strchr(msg, ' '); if (s1 == 0) return -1; - const char* call1 = msg; // 1st call + const char* call1 = msg; // 1st call const char* call2 = s1 + 1; // 2nd call int32_t n28a = pack28(call1); @@ -217,7 +215,7 @@ int pack77_1(const char* msg, uint8_t* b77) return 0; } -void packtext77(const char* text, uint8_t* b77) +static void packtext77(const char* text, uint8_t* b77) { int length = strlen(text); @@ -254,7 +252,7 @@ void packtext77(const char* text, uint8_t* b77) // Get the index of the current char if (j < length) { - int q = char_index(A0, text[j]); + int q = nchar(text[j], FT8_CHAR_TABLE_FULL); x = (q > 0) ? q : 0; } else diff --git a/ft8/pack.h b/ft8/pack.h index 42ab248..0fb3096 100644 --- a/ft8/pack.h +++ b/ft8/pack.h @@ -8,10 +8,11 @@ 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); +/// Parse and pack FT8/FT4 text message into 77 bit binary payload +/// @param[in] msg FT8/FT4 message (e.g. "CQ TE5T KN01") +/// @param[out] c77 10 byte array to store the 77 bit payload (MSB first) +/// @return Parsing result (0 - success, otherwise error) +int pack77(const char* msg, uint8_t* c77); #ifdef __cplusplus } diff --git a/ft8/text.c b/ft8/text.c index 68393a9..0bb05e7 100644 --- a/ft8/text.c +++ b/ft8/text.c @@ -67,18 +67,6 @@ 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) - { - if (c == *string) - { - return i; - } - } - return -1; // Not found -} - // Text message formatting: // - replaces lowercase letters with uppercase // - merges consecutive spaces into single space @@ -164,40 +152,33 @@ void int_to_dd(char* str, int value, int width, bool full_sign) *str = 0; // Add zero terminator } -// convert integer index to ASCII character according to one of 6 tables: -// table 0: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?" -// table 1: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" -// table 2: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" -// table 3: "0123456789" -// table 4: " ABCDEFGHIJKLMNOPQRSTUVWXYZ" -// table 5: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/" -char charn(int c, int table_idx) +char charn(int c, ft8_char_table_e table) { - if (table_idx != 2 && table_idx != 3) + if ((table != FT8_CHAR_TABLE_ALPHANUM) && (table != FT8_CHAR_TABLE_NUMERIC)) { if (c == 0) return ' '; c -= 1; } - if (table_idx != 4) + if (table != FT8_CHAR_TABLE_LETTERS_SPACE) { if (c < 10) return '0' + c; c -= 10; } - if (table_idx != 3) + if (table != FT8_CHAR_TABLE_NUMERIC) { if (c < 26) return 'A' + c; c -= 26; } - if (table_idx == 0) + if (table == FT8_CHAR_TABLE_FULL) { if (c < 5) return "+-./?"[c]; } - else if (table_idx == 5) + else if (table == FT8_CHAR_TABLE_ALPHANUM_SPACE_SLASH) { if (c == 0) return '/'; @@ -207,29 +188,29 @@ char charn(int c, int table_idx) } // Convert character to its index (charn in reverse) according to a table -int nchar(char c, int table_idx) +int nchar(char c, ft8_char_table_e table) { int n = 0; - if (table_idx != 2 && table_idx != 3) + if ((table != FT8_CHAR_TABLE_ALPHANUM) && (table != FT8_CHAR_TABLE_NUMERIC)) { if (c == ' ') return n + 0; n += 1; } - if (table_idx != 4) + if (table != FT8_CHAR_TABLE_LETTERS_SPACE) { if (c >= '0' && c <= '9') return n + (c - '0'); n += 10; } - if (table_idx != 3) + if (table != FT8_CHAR_TABLE_NUMERIC) { if (c >= 'A' && c <= 'Z') return n + (c - 'A'); n += 26; } - if (table_idx == 0) + if (table == FT8_CHAR_TABLE_FULL) { if (c == '+') return n + 0; @@ -242,7 +223,7 @@ int nchar(char c, int table_idx) if (c == '?') return n + 4; } - else if (table_idx == 5) + else if (table == FT8_CHAR_TABLE_ALPHANUM_SPACE_SLASH) { if (c == '/') return n + 0; diff --git a/ft8/text.h b/ft8/text.h index f1af398..0f5dc35 100644 --- a/ft8/text.h +++ b/ft8/text.h @@ -9,35 +9,46 @@ 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); - 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); +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); +// Text message formatting: +// - replaces lowercase letters with uppercase +// - merges consecutive spaces into single space +void fmtmsg(char* msg_out, const char* msg_in); - // Text message formatting: - // - replaces lowercase letters with uppercase - // - merges consecutive spaces into single space - void fmtmsg(char* msg_out, const char* msg_in); +// Parse a 2 digit integer from string +int dd_to_int(const char* str, int length); - // 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); - // Convert a 2 digit integer to string - void int_to_dd(char* str, int value, int width, bool full_sign); +typedef enum +{ + FT8_CHAR_TABLE_FULL, // table[42] " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?" + FT8_CHAR_TABLE_ALPHANUM_SPACE_SLASH, // table[38] " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/" + FT8_CHAR_TABLE_ALPHANUM_SPACE, // table[37] " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + FT8_CHAR_TABLE_LETTERS_SPACE, // table[27] " ABCDEFGHIJKLMNOPQRSTUVWXYZ" + FT8_CHAR_TABLE_ALPHANUM, // table[36] "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + FT8_CHAR_TABLE_NUMERIC, // table[10] "0123456789" +} ft8_char_table_e; - char charn(int c, int table_idx); - int nchar(char c, int table_idx); +/// Convert integer index to ASCII character according to one of character tables +char charn(int c, ft8_char_table_e table); + +/// Look up the index of an ASCII character in one of character tables +int nchar(char c, ft8_char_table_e table); #ifdef __cplusplus } diff --git a/ft8/unpack.c b/ft8/unpack.c index 4ef6ee8..25e4c1c 100644 --- a/ft8/unpack.c +++ b/ft8/unpack.c @@ -15,7 +15,7 @@ // n28 is a 28-bit integer, e.g. n28a or n28b, containing all the // call sign bits from a packed message. -int unpack_callsign(uint32_t n28, uint8_t ip, uint8_t i3, char* result) +static int unpack_callsign(uint32_t n28, uint8_t ip, uint8_t i3, char* result, const unpack_hash_interface_t* hash_if) { // Check for special tokens DE, QRZ, CQ, CQ_nnn, CQ_aaaa if (n28 < NTOKENS) @@ -46,7 +46,7 @@ int unpack_callsign(uint32_t n28, uint8_t ip, uint8_t i3, char* result) aaaa[4] = '\0'; for (int i = 3; /* */; --i) { - aaaa[i] = charn(n % 27, 4); + aaaa[i] = charn(n % 27, FT8_CHAR_TABLE_LETTERS_SPACE); if (i == 0) break; n /= 27; @@ -64,12 +64,14 @@ int unpack_callsign(uint32_t n28, uint8_t ip, uint8_t i3, char* result) if (n28 < MAX22) { // This is a 22-bit hash of a result - // TODO: implement - strcpy(result, "<...>"); - // result[0] = '<'; - // int_to_dd(result + 1, n28, 7, false); - // result[8] = '>'; - // result[9] = '\0'; + if (hash_if != NULL) + { + hash_if->hash22(n28, result); + } + else + { + strcpy(result, "<...>"); + } return 0; } @@ -78,17 +80,17 @@ int unpack_callsign(uint32_t n28, uint8_t ip, uint8_t i3, char* result) char callsign[7]; callsign[6] = '\0'; - callsign[5] = charn(n % 27, 4); + callsign[5] = charn(n % 27, FT8_CHAR_TABLE_LETTERS_SPACE); n /= 27; - callsign[4] = charn(n % 27, 4); + callsign[4] = charn(n % 27, FT8_CHAR_TABLE_LETTERS_SPACE); n /= 27; - callsign[3] = charn(n % 27, 4); + callsign[3] = charn(n % 27, FT8_CHAR_TABLE_LETTERS_SPACE); n /= 27; - callsign[2] = charn(n % 10, 3); + callsign[2] = charn(n % 10, FT8_CHAR_TABLE_NUMERIC); n /= 10; - callsign[1] = charn(n % 36, 2); + callsign[1] = charn(n % 36, FT8_CHAR_TABLE_ALPHANUM); n /= 36; - callsign[0] = charn(n % 37, 1); + callsign[0] = charn(n % 37, FT8_CHAR_TABLE_ALPHANUM_SPACE); // Skip trailing and leading whitespace in case of a short callsign strcpy(result, trim(callsign)); @@ -111,7 +113,7 @@ int unpack_callsign(uint32_t n28, uint8_t ip, uint8_t i3, char* result) return 0; // Success } -int unpack_type1(const uint8_t* a77, uint8_t i3, char* call_to, char* call_de, char* extra) +static int unpack_type1(const uint8_t* a77, uint8_t i3, char* call_to, char* call_de, char* extra, const unpack_hash_interface_t* hash_if) { uint32_t n28a, n28b; uint16_t igrid4; @@ -133,23 +135,25 @@ int unpack_type1(const uint8_t* a77, uint8_t i3, char* call_to, char* call_de, c igrid4 |= (a77[9] >> 6); // Unpack both callsigns - if (unpack_callsign(n28a >> 1, n28a & 0x01, i3, call_to) < 0) + if (unpack_callsign(n28a >> 1, n28a & 0x01, i3, call_to, hash_if) < 0) { return -1; } - if (unpack_callsign(n28b >> 1, n28b & 0x01, i3, call_de) < 0) + if (unpack_callsign(n28b >> 1, n28b & 0x01, i3, call_de, hash_if) < 0) { return -2; } // Fix "CQ_" to "CQ " -> already done in unpack_callsign() // TODO: add to recent calls - // if (call_to[0] != '<' && strlen(call_to) >= 4) { - // save_hash_call(call_to) - // } - // if (call_de[0] != '<' && strlen(call_de) >= 4) { - // save_hash_call(call_de) - // } + if ((call_to[0] != '<') && (strlen(call_to) >= 4) && (hash_if != NULL)) + { + hash_if->save_hash(call_to); + } + if ((call_de[0] != '<') && (strlen(call_de) >= 4) && (hash_if != NULL)) + { + hash_if->save_hash(call_de); + } char* dst = extra; @@ -208,7 +212,7 @@ int unpack_type1(const uint8_t* a77, uint8_t i3, char* call_to, char* call_de, c return 0; // Success } -int unpack_text(const uint8_t* a71, char* text) +static int unpack_text(const uint8_t* a71, char* text) { // TODO: test uint8_t b71[9]; @@ -233,14 +237,14 @@ int unpack_text(const uint8_t* a71, char* text) b71[i] = rem / 42; rem = rem % 42; } - c14[idx] = charn(rem, 0); + c14[idx] = charn(rem, FT8_CHAR_TABLE_FULL); } strcpy(text, trim(c14)); return 0; // Success } -int unpack_telemetry(const uint8_t* a71, char* telemetry) +static int unpack_telemetry(const uint8_t* a71, char* telemetry) { uint8_t b71[9]; @@ -267,27 +271,27 @@ int unpack_telemetry(const uint8_t* a71, char* telemetry) return 0; } -//none standard for wsjt-x 2.0 -//by KD8CEC -int unpack_nonstandard(const uint8_t* a77, char* call_to, char* call_de, char* extra) +// none standard for wsjt-x 2.0 +// by KD8CEC +static int unpack_nonstandard(const uint8_t* a77, char* call_to, char* call_de, char* extra, const unpack_hash_interface_t* hash_if) { uint32_t n12, iflip, nrpt, icq; uint64_t n58; - n12 = (a77[0] << 4); //11 ~4 : 8 - n12 |= (a77[1] >> 4); //3~0 : 12 + n12 = (a77[0] << 4); // 11 ~4 : 8 + n12 |= (a77[1] >> 4); // 3~0 : 12 - n58 = ((uint64_t)(a77[1] & 0x0F) << 54); //57 ~ 54 : 4 - n58 |= ((uint64_t)a77[2] << 46); //53 ~ 46 : 12 - n58 |= ((uint64_t)a77[3] << 38); //45 ~ 38 : 12 - n58 |= ((uint64_t)a77[4] << 30); //37 ~ 30 : 12 - n58 |= ((uint64_t)a77[5] << 22); //29 ~ 22 : 12 - n58 |= ((uint64_t)a77[6] << 14); //21 ~ 14 : 12 - n58 |= ((uint64_t)a77[7] << 6); //13 ~ 6 : 12 - n58 |= ((uint64_t)a77[8] >> 2); //5 ~ 0 : 765432 10 + n58 = ((uint64_t)(a77[1] & 0x0F) << 54); // 57 ~ 54 : 4 + n58 |= ((uint64_t)a77[2] << 46); // 53 ~ 46 : 12 + n58 |= ((uint64_t)a77[3] << 38); // 45 ~ 38 : 12 + n58 |= ((uint64_t)a77[4] << 30); // 37 ~ 30 : 12 + n58 |= ((uint64_t)a77[5] << 22); // 29 ~ 22 : 12 + n58 |= ((uint64_t)a77[6] << 14); // 21 ~ 14 : 12 + n58 |= ((uint64_t)a77[7] << 6); // 13 ~ 6 : 12 + n58 |= ((uint64_t)a77[8] >> 2); // 5 ~ 0 : 765432 10 - iflip = (a77[8] >> 1) & 0x01; //76543210 + iflip = (a77[8] >> 1) & 0x01; // 76543210 nrpt = ((a77[8] & 0x01) << 1); - nrpt |= (a77[9] >> 7); //76543210 + nrpt |= (a77[9] >> 7); // 76543210 icq = ((a77[9] >> 6) & 0x01); char c11[12]; @@ -295,27 +299,32 @@ int unpack_nonstandard(const uint8_t* a77, char* call_to, char* call_de, char* e for (int i = 10; /* no condition */; --i) { - c11[i] = charn(n58 % 38, 5); + c11[i] = charn(n58 % 38, FT8_CHAR_TABLE_ALPHANUM_SPACE_SLASH); if (i == 0) break; n58 /= 38; } char call_3[15]; - // should replace with hash12(n12, call_3); - strcpy(call_3, "<...>"); - // call_3[0] = '<'; - // int_to_dd(call_3 + 1, n12, 4, false); - // call_3[5] = '>'; - // call_3[6] = '\0'; + if (hash_if != NULL) + { + hash_if->hash12(n12, call_3); + } + else + { + strcpy(call_3, "<...>"); + } - char* call_1 = (iflip) ? c11 : call_3; - char* call_2 = (iflip) ? call_3 : c11; - //save_hash_call(c11_trimmed); + char* call_1 = trim((iflip) ? c11 : call_3); + char* call_2 = trim((iflip) ? call_3 : c11); + if (hash_if != NULL) + { + hash_if->save_hash(c11); + } if (icq == 0) { - strcpy(call_to, trim(call_1)); + strcpy(call_to, call_1); if (nrpt == 1) strcpy(extra, "RRR"); else if (nrpt == 2) @@ -332,12 +341,12 @@ int unpack_nonstandard(const uint8_t* a77, char* call_to, char* call_de, char* e strcpy(call_to, "CQ"); extra[0] = '\0'; } - strcpy(call_de, trim(call_2)); + strcpy(call_de, call_2); return 0; } -int unpack77_fields(const uint8_t* a77, char* call_to, char* call_de, char* extra) +int unpack77_fields(const uint8_t* a77, char* call_to, char* call_de, char* extra, const unpack_hash_interface_t* hash_if) { call_to[0] = call_de[0] = extra[0] = '\0'; @@ -373,7 +382,7 @@ int unpack77_fields(const uint8_t* a77, char* call_to, char* call_de, char* extr else if (i3 == 1 || i3 == 2) { // Type 1 (standard message) or Type 2 ("/P" form for EU VHF contest) - return unpack_type1(a77, i3, call_to, call_de, extra); + return unpack_type1(a77, i3, call_to, call_de, extra, hash_if); } // else if (i3 == 3) { // // Type 3: ARRL RTTY Contest @@ -383,7 +392,7 @@ int unpack77_fields(const uint8_t* a77, char* call_to, char* call_de, char* extr // // Type 4: Nonstandard calls, e.g. PJ4/KA1ABC RR73 // // One hashed call or "CQ"; one compound or nonstandard call with up // // to 11 characters; and (if not "CQ") an optional RRR, RR73, or 73. - return unpack_nonstandard(a77, call_to, call_de, extra); + return unpack_nonstandard(a77, call_to, call_de, extra, hash_if); } // else if (i3 == 5) { // // Type 5: TU; W9XYZ K1ABC R-09 FN 1 28 28 1 7 9 74 WWROF contest @@ -393,13 +402,13 @@ int unpack77_fields(const uint8_t* a77, char* call_to, char* call_de, char* extr return -1; } -int unpack77(const uint8_t* a77, char* message) +int unpack77(const uint8_t* a77, char* message, const unpack_hash_interface_t* hash_if) { char call_to[14]; char call_de[14]; char extra[19]; - int rc = unpack77_fields(a77, call_to, call_de, extra); + int rc = unpack77_fields(a77, call_to, call_de, extra, hash_if); if (rc < 0) return rc; diff --git a/ft8/unpack.h b/ft8/unpack.h index c3f544c..4e9c85d 100644 --- a/ft8/unpack.h +++ b/ft8/unpack.h @@ -8,13 +8,29 @@ 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); +typedef struct +{ + /// Called when a callsign is looked up by its 22 bit hash code + void (*hash22)(uint32_t n22, char* callsign); + /// Called when a callsign is looked up by its 12 bit hash code + void (*hash12)(uint32_t n12, char* callsign); + /// Called when a callsign should hashed and stored (both by its 22 and 12 bit hash code) + void (*save_hash)(const char* callsign); +} unpack_hash_interface_t; - // message should have at least 35 bytes allocated (34 characters + zero terminator) - int unpack77(const uint8_t* a77, char* message); +/// Unpack a 77 bit message payload into three fields (typically call_to, call_de and grid/report/other) +/// @param[in] a77 message payload in binary form (77 bits, MSB first) +/// @param[out] field1 at least 14 bytes (typically call_to) +/// @param[out] field2 at least 14 bytes (typically call_de) +/// @param[out] field3 at least 7 bytes (typically grid/report/other) +/// @param[in] hash_if hashing interface (can be NULL) +int unpack77_fields(const uint8_t* a77, char* field1, char* field2, char* field3, const unpack_hash_interface_t* hash_if); + +/// Unpack a 77 bit message payload into text message +/// @param[in] a77 message payload in binary form (77 bits, MSB first) +/// @param[out] message should have at least 35 bytes allocated (34 characters + zero terminator) +/// @param[in] hash_if hashing interface (can be NULL) +int unpack77(const uint8_t* a77, char* message, const unpack_hash_interface_t* hash_if); #ifdef __cplusplus }