diff --git a/Makefile b/Makefile index c658344..73ec27b 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,25 @@ BUILD_DIR = .build -FT8_SRC = $(wildcard ft8/*.c) -FT8_OBJ = $(patsubst %.c,$(BUILD_DIR)/%.o,$(FT8_SRC)) +FT8_SRC = $(wildcard ft8/*.c) +FT8_OBJ = $(patsubst %.c,$(BUILD_DIR)/%.o,$(FT8_SRC)) COMMON_SRC = $(wildcard common/*.c) COMMON_OBJ = $(patsubst %.c,$(BUILD_DIR)/%.o,$(COMMON_SRC)) -FFT_SRC = $(wildcard fft/*.c) -FFT_OBJ = $(patsubst %.c,$(BUILD_DIR)/%.o,$(FFT_SRC)) +FFT_SRC = $(wildcard fft/*.c) +FFT_OBJ = $(patsubst %.c,$(BUILD_DIR)/%.o,$(FFT_SRC)) -TARGETS = gen_ft8 decode_ft8 test_ft8 +TARGETS = gen_ft8 decode_ft8 test_ft8 -CFLAGS = -O3 -ggdb3 -fsanitize=address -CPPFLAGS = -std=c11 -I. -I/opt/local/include -LDFLAGS = -lm -fsanitize=address -lportaudio -L/opt/local/lib +CFLAGS = -fsanitize=address -O3 -ggdb3 +CPPFLAGS = -std=c11 -I. +LDFLAGS = -fsanitize=address -lm -CPPFLAGS += -DUSE_PORTAUDIO -I/opt/local/include -LDFLAGS += -lportaudio -L/opt/local/lib +# Optionally, use Portaudio for live audio input +ifdef PORTAUDIO_PREFIX +CPPFLAGS += -DUSE_PORTAUDIO -I$(PORTAUDIO_PREFIX)/include +LDFLAGS += -lportaudio -L$(PORTAUDIO_PREFIX)/lib +endif .PHONY: all clean run_tests install diff --git a/common/audio.c b/common/audio.c index 9f34acb..5d97fd2 100644 --- a/common/audio.c +++ b/common/audio.c @@ -167,4 +167,25 @@ int audio_read(float* buffer, int num_samples) return 0; } +#else + +int audio_init(void) +{ + return -1; +} + +void audio_list(void) +{ +} + +int audio_open(const char* name) +{ + return -1; +} + +int audio_read(float* buffer, int num_samples) +{ + return -1; +} + #endif \ No newline at end of file diff --git a/common/monitor.c b/common/monitor.c index 0904c2e..6bd228a 100644 --- a/common/monitor.c +++ b/common/monitor.c @@ -1,4 +1,5 @@ #include "monitor.h" +#include #define LOG_LEVEL LOG_INFO #include @@ -33,7 +34,7 @@ static float hann_i(int i, int N) // return a0 - a1 * x1 + a2 * x2; // } -static void waterfall_init(waterfall_t* me, int max_blocks, int num_bins, int time_osr, int freq_osr) +static void waterfall_init(ftx_waterfall_t* me, int max_blocks, int num_bins, int time_osr, int freq_osr) { size_t mag_size = max_blocks * time_osr * freq_osr * num_bins * sizeof(me->mag[0]); me->max_blocks = max_blocks; @@ -46,7 +47,7 @@ static void waterfall_init(waterfall_t* me, int max_blocks, int num_bins, int ti LOG(LOG_DEBUG, "Waterfall size = %zu\n", mag_size); } -static void waterfall_free(waterfall_t* me) +static void waterfall_free(ftx_waterfall_t* me) { free(me->mag); } diff --git a/common/monitor.h b/common/monitor.h index 7440362..dd70d8d 100644 --- a/common/monitor.h +++ b/common/monitor.h @@ -25,16 +25,16 @@ typedef struct typedef struct { float symbol_period; ///< FT4/FT8 symbol period in seconds - 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) + int min_bin; ///< First FFT bin in the frequency range (begin) + int max_bin; ///< First FFT bin outside the frequency range (end) + 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) + ftx_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 diff --git a/demo/decode_ft8.c b/demo/decode_ft8.c index 85c3364..1f45967 100644 --- a/demo/decode_ft8.c +++ b/demo/decode_ft8.c @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -33,7 +32,7 @@ void usage(const char* error_msg) { fprintf(stderr, "ERROR: %s\n", error_msg); } - fprintf(stderr, "Usage: decode_ft8 [-list|-ft4] INPUT\n\n"); + fprintf(stderr, "Usage: decode_ft8 [-list|([-ft4] [INPUT|-dev DEVICE])]\n\n"); fprintf(stderr, "Decode a 15-second (or slighly shorter) WAV file.\n"); } @@ -41,8 +40,8 @@ void usage(const char* error_msg) static struct { - char callsign[12]; - uint32_t hash; + char callsign[12]; ///> Up to 11 symbols of callsign + trailing zeros (always filled) + uint32_t hash; ///> 8 MSBs contain the age of callsign; 22 LSBs contain hash value } callsign_hashtable[CALLSIGN_HASHTABLE_SIZE]; static int callsign_hashtable_size; @@ -103,10 +102,10 @@ void hashtable_add(const char* callsign, uint32_t hash) callsign_hashtable[idx_hash].hash = hash; } -bool hashtable_lookup(ftx_callsign_hash_type_e hash_type, uint32_t hash, char* callsign) +bool hashtable_lookup(ftx_callsign_hash_type_t hash_type, uint32_t hash, char* callsign) { uint8_t hash_shift = (hash_type == FTX_CALLSIGN_HASH_10_BITS) ? 12 : (hash_type == FTX_CALLSIGN_HASH_12_BITS ? 10 : 0); - uint16_t hash10 = (hash >> (12 - hash_shift)) & 0x3FF; + uint16_t hash10 = (hash >> (12 - hash_shift)) & 0x3FFu; int idx_hash = (hash10 * 23) % CALLSIGN_HASHTABLE_SIZE; while (callsign_hashtable[idx_hash].callsign[0] != '\0') { @@ -127,12 +126,12 @@ ftx_callsign_hash_interface_t hash_if = { .save_hash = hashtable_add }; -void decode(const monitor_t* mon) +void decode(const monitor_t* mon, struct tm* tm_slot_start) { - const waterfall_t* wf = &mon->wf; + const ftx_waterfall_t* wf = &mon->wf; // Find top candidates by Costas sync score and localize them in time and frequency - candidate_t candidate_list[kMax_candidates]; - int num_candidates = ftx_find_sync(wf, kMax_candidates, candidate_list, kMin_score); + ftx_candidate_t candidate_list[kMax_candidates]; + int num_candidates = ftx_find_candidates(wf, kMax_candidates, candidate_list, kMin_score); // Hash table for decoded messages (to check for duplicates) int num_decoded = 0; @@ -148,7 +147,7 @@ void decode(const monitor_t* mon) // Go over candidates and attempt to decode messages for (int idx = 0; idx < num_candidates; ++idx) { - const candidate_t* cand = &candidate_list[idx]; + const ftx_candidate_t* cand = &candidate_list[idx]; float freq_hz = (mon->min_bin + cand->freq_offset + (float)cand->freq_sub / wf->freq_osr) / mon->symbol_period; float time_sec = (cand->time_offset + (float)cand->time_sub / wf->time_osr) * mon->symbol_period; @@ -167,11 +166,9 @@ void decode(const monitor_t* mon) #endif ftx_message_t message; - decode_status_t status; - if (!ftx_decode(wf, cand, kLDPC_iterations, &message, &status)) + ftx_decode_status_t status; + if (!ftx_decode_candidate(wf, cand, kLDPC_iterations, &message, &status)) { - // 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, "---"); if (status.ldpc_errors > 0) { LOG(LOG_DEBUG, "LDPC decode: %d errors\n", status.ldpc_errors); @@ -215,25 +212,17 @@ void decode(const monitor_t* mon) ++num_decoded; char text[FTX_MAX_MESSAGE_LENGTH]; - // int unpack_status = unpack77(message.payload, text, NULL); - int unpack_status = ftx_message_decode(&message, &hash_if, text); - if (unpack_status != 0) + ftx_message_rc_t unpack_status = ftx_message_decode(&message, &hash_if, text); + if (unpack_status != FTX_MESSAGE_RC_OK) { - strcpy(text, "Error while unpacking!"); + snprintf(text, sizeof(text), "Error [%d] while unpacking!", (int)unpack_status); } - // uint8_t i3 = ftx_message_get_i3(&message); - // if (i3 == 0) - // { - // uint8_t n3 = ftx_message_get_n3(&message); - // printf("000000 %02d %+4.2f %4.0f [%d.%d] ~ %s\n", cand->score, time_sec, freq_hz, i3, n3, text); - // } - // else - // printf("000000 %02d %+4.2f %4.0f [%d ] ~ %s\n", cand->score, time_sec, freq_hz, i3, text); - // Fake WSJT-X-like output for now float snr = cand->score * 0.5f; // TODO: compute better approximation of SNR - printf("000000 %+05.1f %+4.2f %4.0f ~ %s\n", snr, time_sec, freq_hz, text); + printf("%02d%02d%02d %+05.1f %+4.2f %4.0f ~ %s\n", + tm_slot_start->tm_hour, tm_slot_start->tm_min, tm_slot_start->tm_sec, + snr, time_sec, freq_hz, text); } } LOG(LOG_INFO, "Decoded %d messages, callsign hashtable size %d\n", num_decoded, callsign_hashtable_size); @@ -306,9 +295,9 @@ int main(int argc, char** argv) return -1; } - float slot_time = ((protocol == FTX_PROTOCOL_FT8) ? FT8_SLOT_TIME : FT4_SLOT_TIME); + float slot_period = ((protocol == FTX_PROTOCOL_FT8) ? FT8_SLOT_TIME : FT4_SLOT_TIME); int sample_rate = 12000; - int num_samples = slot_time * sample_rate; + int num_samples = slot_period * sample_rate; float signal[num_samples]; bool is_live = false; @@ -326,7 +315,7 @@ int main(int argc, char** argv) { audio_init(); audio_open(dev_name); - num_samples = (slot_time - 0.4f) * sample_rate; + num_samples = (slot_period - 0.4f) * sample_rate; is_live = true; } @@ -348,6 +337,7 @@ int main(int argc, char** argv) do { + struct tm tm_slot_start = { 0 }; if (is_live) { // Wait for the start of time slot @@ -355,12 +345,18 @@ int main(int argc, char** argv) { struct timespec spec; clock_gettime(CLOCK_REALTIME, &spec); - float time_within_slot = fmod((double)spec.tv_sec + (spec.tv_nsec * 1e-9) - time_shift, slot_time); - if (time_within_slot > slot_time / 3) + double time = (double)spec.tv_sec + (spec.tv_nsec / 1e9); + double time_within_slot = fmod(time - time_shift, slot_period); + if (time_within_slot > slot_period / 4) + { audio_read(signal, mon.block_size); + } else { - LOG(LOG_INFO, "Time within slot: %.3f s\n", time_within_slot); + time_t time_slot_start = (time_t)(time - time_within_slot); + gmtime_r(&time_slot_start, &tm_slot_start); + LOG(LOG_INFO, "Time within slot %02d%02d%02d: %.3f s\n", tm_slot_start.tm_hour, + tm_slot_start.tm_min, tm_slot_start.tm_sec, time_within_slot); break; } } @@ -383,7 +379,7 @@ int main(int argc, char** argv) LOG(LOG_INFO, "Max magnitude: %.1f dB\n", mon.max_mag); // Decode accumulated data (containing slightly less than a full time slot) - decode(&mon); + decode(&mon, &tm_slot_start); // Reset internal variables for the next time slot monitor_reset(&mon); diff --git a/demo/gen_ft8.c b/demo/gen_ft8.c index bb253d2..0f56068 100644 --- a/demo/gen_ft8.c +++ b/demo/gen_ft8.c @@ -6,12 +6,12 @@ #include "common/common.h" #include "common/wave.h" -#include "ft8/debug.h" -#include "ft8/pack.h" +#include "ft8/message.h" #include "ft8/encode.h" #include "ft8/constants.h" #define LOG_LEVEL LOG_INFO +#include "ft8/debug.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) @@ -130,19 +130,19 @@ int main(int argc, char** argv) bool is_ft4 = (argc > 4) && (0 == strcmp(argv[4], "-ft4")); // First, pack the text data into binary message - uint8_t packed[FTX_LDPC_K_BYTES]; - int rc = pack77(message, packed); - if (rc < 0) + ftx_message_t msg; + ftx_message_rc_t rc = ftx_message_encode(&msg, NULL, message); + if (rc != FTX_MESSAGE_RC_OK) { printf("Cannot parse message!\n"); - printf("RC = %d\n", rc); + printf("RC = %d\n", (int)rc); return -2; } printf("Packed data: "); for (int j = 0; j < 10; ++j) { - printf("%02x ", packed[j]); + printf("%02x ", msg.payload[j]); } printf("\n"); @@ -155,11 +155,11 @@ int main(int argc, char** argv) uint8_t tones[num_tones]; // Array of 79 tones (symbols) if (is_ft4) { - ft4_encode(packed, tones); + ft4_encode(msg.payload, tones); } else { - ft8_encode(packed, tones); + ft8_encode(msg.payload, tones); } printf("FSK tones: "); diff --git a/ft8/decode.c b/ft8/decode.c index 48d466e..f7158e6 100644 --- a/ft8/decode.c +++ b/ft8/decode.c @@ -2,7 +2,6 @@ #include "constants.h" #include "crc.h" #include "ldpc.h" -#include "unpack.h" #include #include @@ -15,8 +14,8 @@ /// @param[in] cand Candidate to extract the message from /// @param[in] code_map Symbol encoding map /// @param[out] log174 Output of decoded log likelihoods for each of the 174 message bits -static void ft4_extract_likelihood(const waterfall_t* wf, const candidate_t* cand, float* log174); -static void ft8_extract_likelihood(const waterfall_t* wf, const candidate_t* cand, float* log174); +static void ft4_extract_likelihood(const ftx_waterfall_t* wf, const ftx_candidate_t* cand, float* log174); +static void ft8_extract_likelihood(const ftx_waterfall_t* wf, const ftx_candidate_t* cand, float* log174); /// Packs a string of bits each represented as a zero/non-zero byte in bit_array[], /// as a string of packed bits starting from the MSB of the first byte of packed[] @@ -27,15 +26,15 @@ static void pack_bits(const uint8_t bit_array[], int num_bits, uint8_t packed[]) static float max2(float a, float b); static float max4(float a, float b, float c, float d); -static void heapify_down(candidate_t heap[], int heap_size); -static void heapify_up(candidate_t heap[], int heap_size); +static void heapify_down(ftx_candidate_t heap[], int heap_size); +static void heapify_up(ftx_candidate_t heap[], int heap_size); static void ftx_normalize_logl(float* log174); static void ft4_extract_symbol(const WF_ELEM_T* wf, float* logl); static void ft8_extract_symbol(const WF_ELEM_T* wf, float* logl); static void ft8_decode_multi_symbols(const WF_ELEM_T* wf, int num_bins, int n_syms, int bit_idx, float* log174); -static const WF_ELEM_T* get_cand_mag(const waterfall_t* wf, const candidate_t* candidate) +static const WF_ELEM_T* get_cand_mag(const ftx_waterfall_t* wf, const ftx_candidate_t* candidate) { int offset = candidate->time_offset; offset = (offset * wf->time_osr) + candidate->time_sub; @@ -44,7 +43,7 @@ static const WF_ELEM_T* get_cand_mag(const waterfall_t* wf, const candidate_t* c return wf->mag + offset; } -static int ft8_sync_score(const waterfall_t* wf, const candidate_t* candidate) +static int ft8_sync_score(const ftx_waterfall_t* wf, const ftx_candidate_t* candidate) { int score = 0; int num_average = 0; @@ -110,7 +109,7 @@ static int ft8_sync_score(const waterfall_t* wf, const candidate_t* candidate) return score; } -static int ft4_sync_score(const waterfall_t* wf, const candidate_t* candidate) +static int ft4_sync_score(const ftx_waterfall_t* wf, const ftx_candidate_t* candidate) { int score = 0; int num_average = 0; @@ -173,13 +172,13 @@ static int ft4_sync_score(const waterfall_t* wf, const candidate_t* candidate) return score; } -int ftx_find_sync(const waterfall_t* wf, int num_candidates, candidate_t heap[], int min_score) +int ftx_find_candidates(const ftx_waterfall_t* wf, int num_candidates, ftx_candidate_t heap[], int min_score) { - int (*sync_fun)(const waterfall_t*, const candidate_t*) = (wf->protocol == FTX_PROTOCOL_FT4) ? ft4_sync_score : ft8_sync_score; + int (*sync_fun)(const ftx_waterfall_t*, const ftx_candidate_t*) = (wf->protocol == FTX_PROTOCOL_FT4) ? ft4_sync_score : ft8_sync_score; int num_tones = (wf->protocol == FTX_PROTOCOL_FT4) ? 4 : 8; int heap_size = 0; - candidate_t candidate; + ftx_candidate_t candidate; // Here we allow time offsets that exceed signal boundaries, as long as we still have all data bits. // I.e. we can afford to skip the first 7 or the last 7 Costas symbols, as long as we track how many @@ -226,7 +225,7 @@ int ftx_find_sync(const waterfall_t* wf, int num_candidates, candidate_t heap[], // exchange it with the last element in the heap, and decrease the heap size. // Then restore the heap property in the new, smaller heap. // At the end the elements will be sorted in descending order. - candidate_t tmp = heap[len_unsorted - 1]; + ftx_candidate_t tmp = heap[len_unsorted - 1]; heap[len_unsorted - 1] = heap[0]; heap[0] = tmp; len_unsorted--; @@ -236,7 +235,7 @@ int ftx_find_sync(const waterfall_t* wf, int num_candidates, candidate_t heap[], return heap_size; } -static void ft4_extract_likelihood(const waterfall_t* wf, const candidate_t* cand, float* log174) +static void ft4_extract_likelihood(const ftx_waterfall_t* wf, const ftx_candidate_t* cand, float* log174) { const WF_ELEM_T* mag = get_cand_mag(wf, cand); // Pointer to 4 magnitude bins of the first symbol @@ -262,7 +261,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) +static void ft8_extract_likelihood(const ftx_waterfall_t* wf, const ftx_candidate_t* cand, float* log174) { const WF_ELEM_T* mag = get_cand_mag(wf, cand); // Pointer to 8 magnitude bins of the first symbol @@ -310,7 +309,7 @@ static void ftx_normalize_logl(float* log174) } } -bool ftx_decode(const waterfall_t* wf, const candidate_t* cand, int max_iterations, ftx_message_t* message, decode_status_t* status) +bool ftx_decode_candidate(const ftx_waterfall_t* wf, const ftx_candidate_t* cand, int max_iterations, ftx_message_t* message, ftx_decode_status_t* status) { float log174[FTX_LDPC_N]; // message bits encoded as likelihood if (wf->protocol == FTX_PROTOCOL_FT4) @@ -383,7 +382,7 @@ static float max4(float a, float b, float c, float d) return max2(max2(a, b), max2(c, d)); } -static void heapify_down(candidate_t heap[], int heap_size) +static void heapify_down(ftx_candidate_t heap[], int heap_size) { // heapify from the root down int current = 0; // root node @@ -409,14 +408,14 @@ static void heapify_down(candidate_t heap[], int heap_size) } // Exchange the current node with the smallest child and move down to it - candidate_t tmp = heap[smallest]; + ftx_candidate_t tmp = heap[smallest]; heap[smallest] = heap[current]; heap[current] = tmp; current = smallest; } } -static void heapify_up(candidate_t heap[], int heap_size) +static void heapify_up(ftx_candidate_t heap[], int heap_size) { // heapify from the last node up int current = heap_size - 1; @@ -429,7 +428,7 @@ static void heapify_up(candidate_t heap[], int heap_size) } // Exchange the current node with its parent and move up - candidate_t tmp = heap[parent]; + ftx_candidate_t tmp = heap[parent]; heap[parent] = heap[current]; heap[current] = tmp; current = parent; diff --git a/ft8/decode.h b/ft8/decode.h index 064b588..43cd376 100644 --- a/ft8/decode.h +++ b/ft8/decode.h @@ -46,7 +46,7 @@ typedef struct WF_ELEM_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; +} ftx_waterfall_t; /// Output structure of ftx_find_sync() and input structure of ftx_decode(). /// Holds the position of potential start of a message in time and frequency. @@ -57,7 +57,7 @@ typedef struct 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; +} ftx_candidate_t; /// Structure that contains the status of various steps during decoding of a message typedef struct @@ -68,26 +68,26 @@ typedef struct 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; +} ftx_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,out] heap Array of ftx_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 ftx_find_sync(const waterfall_t* power, int num_candidates, candidate_t heap[], int min_score); +int ftx_find_candidates(const ftx_waterfall_t* power, int num_candidates, ftx_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[in] max_iterations Maximum allowed LDPC iterations (lower number means faster decode, but less precise) /// @param[out] message ftx_message_t structure that will receive the decoded message -/// @param[out] status decode_status_t structure that will be filled with the status of various decoding steps +/// @param[out] status ftx_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 ftx_decode(const waterfall_t* power, const candidate_t* cand, int max_iterations, ftx_message_t* message, decode_status_t* status); +bool ftx_decode_candidate(const ftx_waterfall_t* power, const ftx_candidate_t* cand, int max_iterations, ftx_message_t* message, ftx_decode_status_t* status); #ifdef __cplusplus } diff --git a/ft8/message.c b/ft8/message.c index 9d46a60..21f5a49 100644 --- a/ft8/message.c +++ b/ft8/message.c @@ -23,7 +23,7 @@ static void add_brackets(char* result, const char* original, int length); /// @param[out] n10_out Pointer to store 10-bit hash value (can be NULL) /// @return True on success static bool save_callsign(const ftx_callsign_hash_interface_t* hash_if, const char* callsign, uint32_t* n22_out, uint16_t* n12_out, uint16_t* n10_out); -static bool lookup_callsign(const ftx_callsign_hash_interface_t* hash_if, ftx_callsign_hash_type_e hash_type, uint32_t hash, char* callsign); +static bool lookup_callsign(const ftx_callsign_hash_interface_t* hash_if, ftx_callsign_hash_type_t hash_type, uint32_t hash, char* callsign); static int32_t pack_basecall(const char* callsign, int length); @@ -591,7 +591,7 @@ static bool save_callsign(const ftx_callsign_hash_interface_t* hash_if, const ch return true; } -static bool lookup_callsign(const ftx_callsign_hash_interface_t* hash_if, ftx_callsign_hash_type_e hash_type, uint32_t hash, char* callsign) +static bool lookup_callsign(const ftx_callsign_hash_interface_t* hash_if, ftx_callsign_hash_type_t hash_type, uint32_t hash, char* callsign) { char c11[12]; diff --git a/ft8/message.h b/ft8/message.h index 2f44fe0..2399295 100644 --- a/ft8/message.h +++ b/ft8/message.h @@ -58,12 +58,12 @@ typedef enum FTX_CALLSIGN_HASH_22_BITS, FTX_CALLSIGN_HASH_12_BITS, FTX_CALLSIGN_HASH_10_BITS -} ftx_callsign_hash_type_e; +} ftx_callsign_hash_type_t; typedef struct { /// Called when a callsign is looked up by its 22/12/10 bit hash code - bool (*lookup_hash)(ftx_callsign_hash_type_e hash_type, uint32_t hash, char* callsign); + bool (*lookup_hash)(ftx_callsign_hash_type_t hash_type, uint32_t hash, char* callsign); /// Called when a callsign should hashed and stored (by its 22, 12 and 10 bit hash codes) void (*save_hash)(const char* callsign, uint32_t n22); } ftx_callsign_hash_interface_t; @@ -78,9 +78,12 @@ typedef enum FTX_MESSAGE_RC_ERROR_TYPE } ftx_message_rc_t; -// Basecall - 1-2 letter/digit prefix (at least one letter), 1 digit area code, 1-3 letter suffix, total 3-6 chars (except for 7 char 3DA0- and 3X- calls) -// Ext. basecall - basecall followed by /R or /P -// Nonstd. call - all the rest, limited to 3-11 characters either alphanumeric or stroke (/) +// Callsign types and sizes: +// * Std. call (basecall) - 1-2 letter/digit prefix (at least one letter), 1 digit area code, 1-3 letter suffix, +// total 3-6 chars (exception: 7 character calls with prefixes 3DA0- and 3XA..3XZ-) +// * Ext. std. call - basecall followed by /R or /P +// * Nonstd. call - all the rest, limited to 3-11 characters either alphanumeric or stroke (/) +// In case a call is looked up from its hash value, the call is enclosed in angular brackets (). void ftx_message_init(ftx_message_t* msg); diff --git a/ft8/pack.c b/ft8/pack.c deleted file mode 100644 index 9784f2b..0000000 --- a/ft8/pack.c +++ /dev/null @@ -1,365 +0,0 @@ -#include "pack.h" -#include "text.h" - -#include -#include -#include -#include - -#define NTOKENS ((uint32_t)2063592L) -#define MAX22 ((uint32_t)4194304L) -#define MAXGRID4 ((uint16_t)32400) - -// Pack a special token, a 22-bit hash code, or a valid base call -// into a 28-bit integer. -static int32_t pack28(const char* callsign) -{ - // Check for special tokens first - if (starts_with(callsign, "DE ")) - return 0; - if (starts_with(callsign, "QRZ ")) - return 1; - if (starts_with(callsign, "CQ ")) - return 2; - - if (starts_with(callsign, "CQ_")) - { - int nnum = 0, nlet = 0; - - // TODO: - } - - // TODO: Check for <...> callsign - - char c6[6] = { ' ', ' ', ' ', ' ', ' ', ' ' }; - - int length = 0; // strlen(callsign); // We will need it later - while (callsign[length] != ' ' && callsign[length] != 0) - { - length++; - } - - // Copy callsign to 6 character buffer - if (starts_with(callsign, "3DA0") && length <= 7) - { - // Work-around for Swaziland prefix: 3DA0XYZ -> 3D0XYZ - memcpy(c6, "3D0", 3); - memcpy(c6 + 3, callsign + 4, length - 4); - } - else if (starts_with(callsign, "3X") && is_letter(callsign[2]) && length <= 7) - { - // Work-around for Guinea prefixes: 3XA0XYZ -> QA0XYZ - memcpy(c6, "Q", 1); - memcpy(c6 + 1, callsign + 2, length - 2); - } - else - { - // Check the position of callsign digit and make a right-aligned copy into c6 - if (is_digit(callsign[2]) && length <= 6) - { - // AB0XYZ - memcpy(c6, callsign, length); - } - else if (is_digit(callsign[1]) && length <= 5) - { - // A0XYZ -> " A0XYZ" - memcpy(c6 + 1, callsign, length); - } - } - - // Check for standard callsign - 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; - n28 = n28 * 36 + i1; - n28 = n28 * 10 + i2; - n28 = n28 * 27 + i3; - n28 = n28 * 27 + i4; - n28 = n28 * 27 + i5; - return NTOKENS + MAX22 + n28; - } - - // char text[13]; - // if (length > 13) return -1; - - // TODO: - // Treat this as a nonstandard callsign: compute its 22-bit hash - return -1; -} - -// Check if a string could be a valid standard callsign or a valid -// compound callsign. -// Return base call "bc" and a logical "cok" indicator. -static bool chkcall(const char* call, char* bc) -{ - int length = strlen(call); // n1=len_trim(w) - if (length > 11) - return false; - if (0 != strchr(call, '.')) - return false; - if (0 != strchr(call, '+')) - return false; - if (0 != strchr(call, '-')) - return false; - if (0 != strchr(call, '?')) - return false; - if (length > 6 && 0 != strchr(call, '/')) - return false; - - // TODO: implement suffix parsing (or rework?) - - return true; -} - -static uint16_t packgrid(const char* grid4) -{ - if (grid4 == 0) - { - // Two callsigns only, no report/grid - return MAXGRID4 + 1; - } - - // Take care of special cases - if (equals(grid4, "RRR")) - return MAXGRID4 + 2; - if (equals(grid4, "RR73")) - return MAXGRID4 + 3; - if (equals(grid4, "73")) - 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])) - { - uint16_t igrid4 = (grid4[0] - 'A'); - igrid4 = igrid4 * 18 + (grid4[1] - 'A'); - igrid4 = igrid4 * 10 + (grid4[2] - '0'); - igrid4 = igrid4 * 10 + (grid4[3] - '0'); - return igrid4; - } - - // Parse report: +dd / -dd / R+dd / R-dd - // TODO: check the range of dd - if (grid4[0] == 'R') - { - int dd = dd_to_int(grid4 + 1, 3); - uint16_t irpt = 35 + dd; - return (MAXGRID4 + irpt) | 0x8000; // ir = 1 - } - else - { - int dd = dd_to_int(grid4, 3); - uint16_t irpt = 35 + dd; - return (MAXGRID4 + irpt); // ir = 0 - } - - return MAXGRID4 + 1; -} - -// Pack Type 1 (Standard 77-bit message) and Type 2 (ditto, with a "/P" call) -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* call2 = s1 + 1; // 2nd call - - int32_t n28a = pack28(call1); - int32_t n28b = pack28(call2); - - if (n28a < 0 || n28b < 0) - return -1; - - uint16_t igrid4; - - // Locate the second delimiter - const char* s2 = strchr(s1 + 1, ' '); - if (s2 != 0) - { - igrid4 = packgrid(s2 + 1); - } - else - { - // Two callsigns, no grid/report - igrid4 = packgrid(0); - } - - uint8_t i3 = 1; // No suffix or /R - - // TODO: check for suffixes - - // Shift in ipa and ipb bits into n28a and n28b - n28a <<= 1; // ipa = 0 - n28b <<= 1; // ipb = 0 - - // Pack into (28 + 1) + (28 + 1) + (1 + 15) + 3 bits - b77[0] = (n28a >> 21); - b77[1] = (n28a >> 13); - b77[2] = (n28a >> 5); - b77[3] = (uint8_t)(n28a << 3) | (uint8_t)(n28b >> 26); - b77[4] = (n28b >> 18); - b77[5] = (n28b >> 10); - b77[6] = (n28b >> 2); - b77[7] = (uint8_t)(n28b << 6) | (uint8_t)(igrid4 >> 10); - b77[8] = (igrid4 >> 2); - b77[9] = (uint8_t)(igrid4 << 6) | (uint8_t)(i3 << 3); - - return 0; -} - -static void packtext77(const char* text, uint8_t* b77) -{ - int length = strlen(text); - - // Skip leading and trailing spaces - while (*text == ' ' && *text != 0) - { - ++text; - --length; - } - while (length > 0 && text[length - 1] == ' ') - { - --length; - } - - // Clear the first 72 bits representing a long number - for (int i = 0; i < 9; ++i) - { - b77[i] = 0; - } - - // Now express the text as base-42 number stored - // in the first 72 bits of b77 - for (int j = 0; j < 13; ++j) - { - // Multiply the long integer in b77 by 42 - uint16_t x = 0; - for (int i = 8; i >= 0; --i) - { - x += b77[i] * (uint16_t)42; - b77[i] = (x & 0xFF); - x >>= 8; - } - - // Get the index of the current char - if (j < length) - { - int q = nchar(text[j], FT8_CHAR_TABLE_FULL); - x = (q > 0) ? q : 0; - } - else - { - x = 0; - } - // Here we double each added number in order to have the result multiplied - // by two as well, so that it's a 71 bit number left-aligned in 72 bits (9 bytes) - x <<= 1; - - // Now add the number to our long number - for (int i = 8; i >= 0; --i) - { - if (x == 0) - break; - x += b77[i]; - b77[i] = (x & 0xFF); - x >>= 8; - } - } - - // Set n3=0 (bits 71..73) and i3=0 (bits 74..76) - b77[8] &= 0xFE; - b77[9] &= 0x00; -} - -int pack77(const char* msg, uint8_t* c77) -{ - // Check Type 1 (Standard 77-bit message) or Type 2, with optional "/P" - if (0 == pack77_1(msg, c77)) - { - return 0; - } - - // TODO: - // Check 0.5 (telemetry) - - // Check Type 4 (One nonstandard call and one hashed call) - - // Default to free text - // i3=0 n3=0 - packtext77(msg, c77); - return 0; -} - -#ifdef UNIT_TEST - -#include - -bool test1() -{ - const char* inputs[] = { - "", - " ", - "ABC", - "A9", - "L9A", - "L7BC", - "L0ABC", - "LL3JG", - "LL3AJG", - "CQ ", - 0 - }; - - for (int i = 0; inputs[i]; ++i) - { - int32_t result = ft8_v2::pack28(inputs[i]); - printf("pack28(\"%s\") = %d\n", inputs[i], result); - } - - return true; -} - -bool test2() -{ - const char* inputs[] = { - "CQ LL3JG", - "CQ LL3JG KO26", - "L0UAA LL3JG KO26", - "L0UAA LL3JG +02", - "L0UAA LL3JG RRR", - "L0UAA LL3JG 73", - 0 - }; - - for (int i = 0; inputs[i]; ++i) - { - uint8_t result[10]; - int rc = ft8_v2::pack77_1(inputs[i], result); - printf("pack77_1(\"%s\") = %d\t[", inputs[i], rc); - for (int j = 0; j < 10; ++j) - { - printf("%02x ", result[j]); - } - printf("]\n"); - } - - return true; -} - -int main() -{ - test1(); - test2(); - return 0; -} - -#endif \ No newline at end of file diff --git a/ft8/pack.h b/ft8/pack.h deleted file mode 100644 index 0ded722..0000000 --- a/ft8/pack.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _INCLUDE_PACK_H_ -#define _INCLUDE_PACK_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/// 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 -} -#endif - -#endif // _INCLUDE_PACK_H_ diff --git a/ft8/unpack.c b/ft8/unpack.c deleted file mode 100644 index 572a10b..0000000 --- a/ft8/unpack.c +++ /dev/null @@ -1,435 +0,0 @@ -#ifdef __linux__ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#endif - -#include "unpack.h" -#include "text.h" - -#include - -#define MAX22 ((uint32_t)4194304L) -#define NTOKENS ((uint32_t)2063592L) -#define MAXGRID4 ((uint16_t)32400L) - -// n28 is a 28-bit integer, e.g. n28a or n28b, containing all the -// call sign bits from a packed message. -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) - { - if (n28 <= 2) - { - if (n28 == 0) - strcpy(result, "DE"); - if (n28 == 1) - strcpy(result, "QRZ"); - if (n28 == 2) - strcpy(result, "CQ"); - return 0; // Success - } - if (n28 <= 1002) - { - // CQ_nnn with 3 digits - strcpy(result, "CQ "); - int_to_dd(result + 3, n28 - 3, 3, false); - return 0; // Success - } - if (n28 <= 532443L) - { - // CQ_aaaa with 4 alphanumeric symbols - uint32_t n = n28 - 1003; - char aaaa[5]; - - aaaa[4] = '\0'; - for (int i = 3; /* */; --i) - { - aaaa[i] = charn(n % 27, FT8_CHAR_TABLE_LETTERS_SPACE); - if (i == 0) - break; - n /= 27; - } - - strcpy(result, "CQ "); - strcat(result, trim_front(aaaa)); - return 0; // Success - } - // ? TODO: unspecified in the WSJT-X code - return -1; - } - - n28 = n28 - NTOKENS; - if (n28 < MAX22) - { - // This is a 22-bit hash of a result - if (hash_if != NULL) - { - hash_if->hash22(n28, result); - } - else - { - strcpy(result, "<...>"); - } - return 0; - } - - // Standard callsign - uint32_t n = n28 - MAX22; - - char callsign[7]; - callsign[6] = '\0'; - callsign[5] = charn(n % 27, FT8_CHAR_TABLE_LETTERS_SPACE); - n /= 27; - callsign[4] = charn(n % 27, FT8_CHAR_TABLE_LETTERS_SPACE); - n /= 27; - callsign[3] = charn(n % 27, FT8_CHAR_TABLE_LETTERS_SPACE); - n /= 27; - callsign[2] = charn(n % 10, FT8_CHAR_TABLE_NUMERIC); - n /= 10; - callsign[1] = charn(n % 36, FT8_CHAR_TABLE_ALPHANUM); - n /= 36; - 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)); - if (strlen(result) == 0) - return -1; - - // Check if we should append /R or /P suffix - if (ip) - { - if (i3 == 1) - { - strcat(result, "/R"); - } - else if (i3 == 2) - { - strcat(result, "/P"); - } - } - - return 0; // Success -} - -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; - uint8_t ir; - - // Extract packed fields - n28a = (a77[0] << 21); - n28a |= (a77[1] << 13); - n28a |= (a77[2] << 5); - n28a |= (a77[3] >> 3); - n28b = ((a77[3] & 0x07) << 26); - n28b |= (a77[4] << 18); - n28b |= (a77[5] << 10); - n28b |= (a77[6] << 2); - n28b |= (a77[7] >> 6); - ir = ((a77[7] & 0x20) >> 5); - igrid4 = ((a77[7] & 0x1F) << 10); - igrid4 |= (a77[8] << 2); - igrid4 |= (a77[9] >> 6); - - // Unpack both callsigns - 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, 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) && (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; - - if (igrid4 <= MAXGRID4) - { - // Extract 4 symbol grid locator - if (ir > 0) - { - // In case of ir=1 add an "R" before grid - dst = stpcpy(dst, "R "); - } - - uint16_t n = igrid4; - dst[4] = '\0'; - dst[3] = '0' + (n % 10); - n /= 10; - dst[2] = '0' + (n % 10); - n /= 10; - dst[1] = 'A' + (n % 18); - n /= 18; - dst[0] = 'A' + (n % 18); - // if (ir > 0 && strncmp(call_to, "CQ", 2) == 0) return -1; - } - else - { - // Extract report - int irpt = igrid4 - MAXGRID4; - - // Check special cases first (irpt > 0 always) - switch (irpt) - { - case 1: - extra[0] = '\0'; - break; - case 2: - strcpy(dst, "RRR"); - break; - case 3: - strcpy(dst, "RR73"); - break; - case 4: - strcpy(dst, "73"); - break; - default: - // Extract signal report as a two digit number with a + or - sign - if (ir > 0) - { - *dst++ = 'R'; // Add "R" before report - } - int_to_dd(dst, irpt - 35, 2, true); - break; - } - // if (irpt >= 2 && strncmp(call_to, "CQ", 2) == 0) return -1; - } - - return 0; // Success -} - -static int unpack_text(const uint8_t* a71, char* text) -{ - uint8_t b71[9]; - - // Shift 71 bits right by 1 bit, so that it's right-aligned in the byte array - uint8_t carry = 0; - for (int i = 0; i < 9; ++i) - { - b71[i] = carry | (a71[i] >> 1); - carry = (a71[i] & 1) ? 0x80 : 0; - } - - char c14[14]; - c14[13] = 0; - for (int idx = 12; idx >= 0; --idx) - { - // Divide the long integer in b71 by 42 - uint16_t rem = 0; - for (int i = 0; i < 9; ++i) - { - rem = (rem << 8) | b71[i]; - b71[i] = rem / 42; - rem = rem % 42; - } - c14[idx] = charn(rem, FT8_CHAR_TABLE_FULL); - } - - strcpy(text, trim(c14)); - return 0; // Success -} - -static int unpack_telemetry(const uint8_t* a71, char* telemetry) -{ - uint8_t b71[9]; - - // Shift bits in a71 right by 1 bit - uint8_t carry = 0; - for (int i = 0; i < 9; ++i) - { - b71[i] = (carry << 7) | (a71[i] >> 1); - carry = (a71[i] & 0x01); - } - - // Convert b71 to hexadecimal string - for (int i = 0; i < 9; ++i) - { - uint8_t nibble1 = (b71[i] >> 4); - uint8_t nibble2 = (b71[i] & 0x0F); - char c1 = (nibble1 > 9) ? (nibble1 - 10 + 'A') : nibble1 + '0'; - char c2 = (nibble2 > 9) ? (nibble2 - 10 + 'A') : nibble2 + '0'; - telemetry[i * 2] = c1; - telemetry[i * 2 + 1] = c2; - } - - telemetry[18] = '\0'; - return 0; -} - -// 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 - - 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 - nrpt = ((a77[8] & 0x01) << 1); - nrpt |= (a77[9] >> 7); // 76543210 - icq = ((a77[9] >> 6) & 0x01); - - char c11[12]; - c11[11] = '\0'; - - for (int i = 10; /* no condition */; --i) - { - c11[i] = charn(n58 % 38, FT8_CHAR_TABLE_ALPHANUM_SPACE_SLASH); - if (i == 0) - break; - n58 /= 38; - } - - char call_3[15]; - if (hash_if != NULL) - { - hash_if->hash12(n12, call_3); - } - else - { - strcpy(call_3, "<...>"); - } - - 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, call_1); - if (nrpt == 1) - strcpy(extra, "RRR"); - else if (nrpt == 2) - strcpy(extra, "RR73"); - else if (nrpt == 3) - strcpy(extra, "73"); - else - { - extra[0] = '\0'; - } - } - else - { - strcpy(call_to, "CQ"); - extra[0] = '\0'; - } - strcpy(call_de, call_2); - - return 0; -} - -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'; - - // Extract i3 (bits 74..76) - uint8_t i3 = (a77[9] >> 3) & 0x07; - - if (i3 == 0) - { - // Extract n3 (bits 71..73) - uint8_t n3 = ((a77[8] << 2) & 0x04) | ((a77[9] >> 6) & 0x03); - - if (n3 == 0) - { - // 0.0 Free text - return unpack_text(a77, extra); - } - // else if (i3 == 0 && n3 == 1) { - // // 0.1 K1ABC RR73; W9XYZ -11 28 28 10 5 71 DXpedition Mode - // } - // else if (i3 == 0 && n3 == 2) { - // // 0.2 PA3XYZ/P R 590003 IO91NP 28 1 1 3 12 25 70 EU VHF contest - // } - // else if (i3 == 0 && (n3 == 3 || n3 == 4)) { - // // 0.3 WA9XYZ KA1ABC R 16A EMA 28 28 1 4 3 7 71 ARRL Field Day - // // 0.4 WA9XYZ KA1ABC R 32A EMA 28 28 1 4 3 7 71 ARRL Field Day - // } - else if (n3 == 5) - { - // 0.5 0123456789abcdef01 71 71 Telemetry (18 hex) - return unpack_telemetry(a77, extra); - } - } - 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, hash_if); - } - // else if (i3 == 3) { - // // Type 3: ARRL RTTY Contest - // } - else if (i3 == 4) - { - // 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, hash_if); - } - // else if (i3 == 5) { - // // Type 5: TU; W9XYZ K1ABC R-09 FN 1 28 28 1 7 9 74 WWROF contest - // } - - // unknown type, should never get here - return -1; -} - -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, hash_if); - if (rc < 0) - return rc; - - // int msg_sz = strlen(call_to) + strlen(call_de) + strlen(extra) + 2; - char* dst = message; - - dst[0] = '\0'; - - if (call_to[0] != '\0') - { - dst = stpcpy(dst, call_to); - *dst++ = ' '; - } - - if (call_de[0] != '\0') - { - dst = stpcpy(dst, call_de); - *dst++ = ' '; - } - - dst = stpcpy(dst, extra); - *dst = '\0'; - - return 0; -} diff --git a/ft8/unpack.h b/ft8/unpack.h deleted file mode 100644 index c6246b3..0000000 --- a/ft8/unpack.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _INCLUDE_UNPACK_H_ -#define _INCLUDE_UNPACK_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -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; - -/// 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 -} -#endif - -#endif // _INCLUDE_UNPACK_H_ diff --git a/test/test.c b/test/test.c index 8ef60bd..4feffef 100644 --- a/test/test.c +++ b/test/test.c @@ -4,10 +4,7 @@ #include #include -#include "ft8/debug.h" - #include "ft8/text.h" -#include "ft8/pack.h" #include "ft8/encode.h" #include "ft8/constants.h" @@ -16,6 +13,7 @@ #include "ft8/message.h" #define LOG_LEVEL LOG_INFO +#include "ft8/debug.h" // void convert_8bit_to_6bit(uint8_t* dst, const uint8_t* src, int nBits) // { @@ -151,7 +149,7 @@ void hashtable_add(const char* callsign, uint32_t hash) callsign_hashtable[idx_hash].hash = hash; } -bool hashtable_lookup(ftx_callsign_hash_type_e hash_type, uint32_t hash, char* callsign) +bool hashtable_lookup(ftx_callsign_hash_type_t hash_type, uint32_t hash, char* callsign) { uint32_t hash_mask = (hash_type == FTX_CALLSIGN_HASH_10_BITS) ? 0x3FFu : (hash_type == FTX_CALLSIGN_HASH_12_BITS ? 0xFFFu : 0x3FFFFFu); int idx_hash = (hash * 23) % CALLSIGN_HASHTABLE_SIZE;