Simplified decoder interface and decode_ft8.c

pull/12/merge
Karlis Goba 2021-08-12 13:43:25 +03:00
rodzic 1690b55a62
commit ac9510b993
9 zmienionych plików z 252 dodań i 156 usunięć

Wyświetl plik

@ -11,13 +11,13 @@ all: $(TARGETS)
run_tests: test
@./test
gen_ft8: gen_ft8.o ft8/constants.o ft8/text.o ft8/pack.o ft8/encode.o common/wave.o
gen_ft8: gen_ft8.o ft8/constants.o ft8/text.o ft8/pack.o ft8/encode.o ft8/crc.o common/wave.o
$(CXX) $(LDFLAGS) -o $@ $^
test: test.o ft8/pack.o ft8/encode.o ft8/text.o ft8/constants.o fft/kiss_fftr.o fft/kiss_fft.o
test: test.o ft8/pack.o ft8/encode.o ft8/crc.o ft8/text.o ft8/constants.o fft/kiss_fftr.o fft/kiss_fft.o
$(CXX) $(LDFLAGS) -o $@ $^
decode_ft8: decode_ft8.o fft/kiss_fftr.o fft/kiss_fft.o ft8/decode.o ft8/encode.o ft8/ldpc.o ft8/unpack.o ft8/text.o ft8/constants.o common/wave.o
decode_ft8: decode_ft8.o fft/kiss_fftr.o fft/kiss_fft.o ft8/decode.o ft8/encode.o ft8/crc.o ft8/ldpc.o ft8/unpack.o ft8/text.o ft8/constants.o common/wave.o
$(CXX) $(LDFLAGS) -o $@ $^
clean:

Wyświetl plik

@ -9,6 +9,7 @@
#include "ft8/decode.h"
#include "ft8/constants.h"
#include "ft8/encode.h"
#include "ft8/crc.h"
#include "common/wave.h"
#include "common/debug.h"
@ -21,7 +22,6 @@ const int kMax_candidates = 120;
const int kLDPC_iterations = 25;
const int kMax_decoded_messages = 50;
const int kMax_message_length = 25;
const int kFreq_osr = 2;
const int kTime_osr = 2;
@ -68,7 +68,7 @@ static float max2(float a, float b)
}
// Compute FFT magnitudes (log power) for each timeslot in the signal
void extract_power(const float signal[], MagArray *power)
void extract_power(const float signal[], waterfall_t *power)
{
const int block_size = 2 * power->num_bins; // Average over 2 bins per FSK tone
const int subblock_size = block_size / power->time_osr;
@ -79,6 +79,9 @@ void extract_power(const float signal[], MagArray *power)
for (int i = 0; i < nfft; ++i)
{
window[i] = hann_i(i, nfft);
// window[i] = (i < block_size) ? hamming_i(i, block_size) : 0;
// window[i] = blackman_i(i, nfft);
// window[i] = hamming_i(i, nfft);
}
size_t fft_work_size;
@ -94,7 +97,7 @@ void extract_power(const float signal[], MagArray *power)
int offset = 0;
float max_mag = -100.0f;
for (int i = 0; i < power->num_blocks; ++i)
for (int idx_block = 0; idx_block < power->num_blocks; ++idx_block)
{
// Loop over two possible time offsets (0 and block_size/2)
for (int time_sub = 0; time_sub < power->time_osr; ++time_sub)
@ -104,26 +107,26 @@ void extract_power(const float signal[], MagArray *power)
float mag_db[nfft / 2 + 1];
// Extract windowed signal block
for (int j = 0; j < nfft; ++j)
for (int pos = 0; pos < nfft; ++pos)
{
timedata[j] = window[j] * signal[(i * block_size) + (j + time_sub * subblock_size)];
timedata[pos] = window[pos] * signal[(idx_block * block_size) + (pos + time_sub * subblock_size)];
}
kiss_fftr(fft_cfg, timedata, freqdata);
// Compute log magnitude in decibels
for (int j = 0; j < nfft / 2 + 1; ++j)
for (int idx_bin = 0; idx_bin < nfft / 2 + 1; ++idx_bin)
{
float mag2 = (freqdata[j].i * freqdata[j].i) + (freqdata[j].r * freqdata[j].r);
mag_db[j] = 10.0f * log10f(1E-10f + mag2 * fft_norm * fft_norm);
float mag2 = (freqdata[idx_bin].i * freqdata[idx_bin].i) + (freqdata[idx_bin].r * freqdata[idx_bin].r);
mag_db[idx_bin] = 10.0f * log10f(1E-10f + mag2 * fft_norm * fft_norm);
}
// Loop over two possible frequency bin offsets (for averaging)
for (int freq_sub = 0; freq_sub < power->freq_osr; ++freq_sub)
{
for (int j = 0; j < power->num_bins; ++j)
for (int pos = 0; pos < power->num_bins; ++pos)
{
float db = mag_db[j * power->freq_osr + freq_sub];
float db = mag_db[pos * power->freq_osr + freq_sub];
// Scale decibels to unsigned 8-bit range and clamp the value
int scaled = (int)(2 * (db + 120));
@ -207,7 +210,7 @@ int main(int argc, char **argv)
// Compute FFT over the whole signal and store it
uint8_t mag_power[num_blocks * kFreq_osr * kTime_osr * num_bins];
MagArray power = {
waterfall_t power = {
.num_blocks = num_blocks,
.num_bins = num_bins,
.time_osr = kTime_osr,
@ -216,103 +219,85 @@ int main(int argc, char **argv)
extract_power(signal, &power);
// Find top candidates by Costas sync score and localize them in time and frequency
Candidate candidate_list[kMax_candidates];
candidate_t candidate_list[kMax_candidates];
int num_candidates = find_sync(&power, kMax_candidates, candidate_list, kMin_score);
// TODO: sort the candidates by strongest sync first?
// Go over candidates and attempt to decode messages
char decoded[kMax_decoded_messages][kMax_message_length];
// Hash table for decoded messages (to check for duplicates)
int num_decoded = 0;
message_t decoded[kMax_decoded_messages];
message_t *decoded_hashtable[kMax_decoded_messages];
// Initialize hash table pointers
for (int i = 0; i < kMax_decoded_messages; ++i)
{
decoded_hashtable[i] = NULL;
}
// Go over candidates and attempt to decode messages
for (int idx = 0; idx < num_candidates; ++idx)
{
const Candidate *cand = &candidate_list[idx];
const candidate_t *cand = &candidate_list[idx];
if (cand->score < kMin_score)
continue;
float freq_hz = (cand->freq_offset + (float)cand->freq_sub / kFreq_osr) * kFSK_dev;
float time_sec = (cand->time_offset + (float)cand->time_sub / kTime_osr) / kFSK_dev;
float log174[FT8_N];
extract_likelihood(&power, cand, log174);
// Try partial decodes with truncated end of message
// (to check if successful decoding can be done prior to receiving the whole message)
for (int bits_received = 100; bits_received < 174; ++bits_received)
message_t message;
decode_status_t status;
if (!decode(&power, cand, &message, kLDPC_iterations, &status))
{
// bp_decode() produces better decodes, uses way less memory
uint8_t plain[FT8_N];
float log174_masked[FT8_N];
int n_errors = 0;
// mask trailing bits with 0 likelihood (p(1)=p(0)=0.5)
for (int m = 0; m < 174; ++m)
if (status.ldpc_errors > 0)
{
log174_masked[m] = (m < bits_received) ? log174[m] : 0;
LOG(LOG_DEBUG, "LDPC decode: %d errors\n", status.ldpc_errors);
}
bp_decode(log174_masked, kLDPC_iterations, plain, &n_errors);
// ldpc_decode(log174_masked, kLDPC_iterations, plain, &n_errors);
if (n_errors > 0)
else if (status.crc_calculated != status.crc_extracted)
{
LOG(LOG_DEBUG, "ldpc_decode() = %d (%.0f Hz)\n", n_errors, freq_hz);
continue;
LOG(LOG_DEBUG, "CRC mismatch!\n");
}
int sum_plain = 0;
for (int i = 0; i < FT8_N; ++i)
else if (status.unpack_status != 0)
{
sum_plain += plain[i];
LOG(LOG_DEBUG, "Error while unpacking!\n");
}
if (sum_plain == 0)
continue;
}
LOG(LOG_DEBUG, "Checking hash table for %4.1fs / %4.1fHz [%d]...\n", time_sec, freq_hz, cand->score);
int hash_idx = message.hash % kMax_decoded_messages;
bool found_empty_slot = false;
bool found_duplicate = false;
do
{
if (decoded_hashtable[hash_idx] == NULL)
{
// All zeroes message
continue;
LOG(LOG_DEBUG, "Found an empty slot\n");
found_empty_slot = true;
}
// Extract payload + CRC (first FT8_K bits)
uint8_t a91[FT8_K_BYTES];
pack_bits(plain, FT8_K, a91);
// Extract CRC and check it
uint16_t chksum = ((a91[9] & 0x07) << 11) | (a91[10] << 3) | (a91[11] >> 5);
a91[9] &= 0xF8;
a91[10] = 0;
a91[11] = 0;
uint16_t chksum2 = ft8_crc(a91, 96 - 14);
if (chksum != chksum2)
else if ((decoded_hashtable[hash_idx]->hash == message.hash) && (0 == strcmp(decoded_hashtable[hash_idx]->text, message.text)))
{
LOG(LOG_DEBUG, "Checksum: message = %04x, CRC = %04x\n", chksum, chksum2);
continue;
LOG(LOG_DEBUG, "Found a duplicate [%s]\n", message.text);
found_duplicate = true;
}
char message[kMax_message_length];
if (unpack77(a91, message) < 0)
else
{
continue;
LOG(LOG_DEBUG, "Hash table clash!\n");
// Move on to check the next entry in hash table
hash_idx = (hash_idx + 1) % kMax_decoded_messages;
}
} while (!found_empty_slot && !found_duplicate);
// Check for duplicate messages (TODO: use hashing)
bool found = false;
for (int i = 0; i < num_decoded; ++i)
{
if (0 == strcmp(decoded[i], message))
{
found = true;
break;
}
}
if (found_empty_slot)
{
// Fill the empty hashtable slot
memcpy(&decoded[hash_idx], &message, sizeof(message));
decoded_hashtable[hash_idx] = &decoded[hash_idx];
++num_decoded;
if (!found && num_decoded < kMax_decoded_messages)
{
strcpy(decoded[num_decoded], message);
++num_decoded;
// Fake WSJT-X-like output for now
int snr = 0; // TODO: compute SNR
printf("000000 %3d [%2d] %4.1f %4d ~ %s\n", cand->score, bits_received, time_sec, (int)(freq_hz + 0.5f), message);
continue;
}
// Fake WSJT-X-like output for now
int snr = 0; // TODO: compute SNR
printf("000000 %3d %4.1f %4d ~ %s\n", cand->score, time_sec, (int)(freq_hz + 0.5f), message.text);
}
}
LOG(LOG_INFO, "Decoded %d messages\n", num_decoded);

54
ft8/crc.c 100644
Wyświetl plik

@ -0,0 +1,54 @@
#include "crc.h"
#include "constants.h"
#define TOPBIT (1u << (FT8_CRC_WIDTH - 1))
// Compute 14-bit CRC for a sequence of given number of bits
// Adapted from https://barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code
// [IN] message - byte sequence (MSB first)
// [IN] num_bits - number of bits in the sequence
uint16_t ft8_crc(const uint8_t message[], int num_bits)
{
uint16_t remainder = 0;
int idx_byte = 0;
// Perform modulo-2 division, a bit at a time.
for (int idx_bit = 0; idx_bit < num_bits; ++idx_bit)
{
if (idx_bit % 8 == 0)
{
// Bring the next byte into the remainder.
remainder ^= (message[idx_byte] << (FT8_CRC_WIDTH - 8));
++idx_byte;
}
// Try to divide the current data bit.
if (remainder & TOPBIT)
{
remainder = (remainder << 1) ^ FT8_CRC_POLYNOMIAL;
}
else
{
remainder = (remainder << 1);
}
}
return remainder & ((TOPBIT << 1) - 1u);
}
uint16_t extract_crc(uint8_t a91[])
{
uint16_t chksum = ((a91[9] & 0x07) << 11) | (a91[10] << 3) | (a91[11] >> 5);
return chksum;
}
void add_crc(uint8_t a91[])
{
// Calculate CRC of 12 bytes = 96 bits, see WSJT-X code
uint16_t checksum = ft8_crc(a91, 96 - 14);
// Store the CRC at the end of 77 bit message
a91[9] |= (uint8_t)(checksum >> 11);
a91[10] = (uint8_t)(checksum >> 3);
a91[11] = (uint8_t)(checksum << 5);
}

18
ft8/crc.h 100644
Wyświetl plik

@ -0,0 +1,18 @@
#ifndef _INCLUDE_CRC_H_
#define _INCLUDE_CRC_H_
#include <stdint.h>
#include <stdbool.h>
// Compute 14-bit CRC for a sequence of given number of bits
// [IN] message - byte sequence (MSB first)
// [IN] num_bits - number of bits in the sequence
uint16_t ft8_crc(const uint8_t message[], int num_bits);
/// Check the FT8 CRC of a packed message (during decoding)
uint16_t extract_crc(uint8_t a91[]);
/// Add the FT8 CRC to a packed message (during encoding)
void add_crc(uint8_t a91[]);
#endif // _INCLUDE_CRC_H_

Wyświetl plik

@ -1,22 +1,32 @@
#include "decode.h"
#include "constants.h"
#include "crc.h"
#include "ldpc.h"
#include "unpack.h"
#include <stdbool.h>
#include <math.h>
/// Compute log likelihood log(p(1) / p(0)) of 174 message bits for later use in soft-decision LDPC decoding
/// @param[in] power Waterfall data collected during message slot
/// @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 extract_likelihood(const waterfall_t *power, const candidate_t *cand, float *log174);
static float max2(float a, float b);
static float max4(float a, float b, float c, float d);
static void heapify_down(Candidate heap[], int heap_size);
static void heapify_up(Candidate heap[], int heap_size);
static void heapify_down(candidate_t heap[], int heap_size);
static void heapify_up(candidate_t heap[], int heap_size);
static void decode_symbol(const uint8_t *power, int bit_idx, float *log174);
static void decode_multi_symbols(const uint8_t *power, int num_bins, int n_syms, int bit_idx, float *log174);
static int get_index(const MagArray *power, int block, int time_sub, int freq_sub, int bin)
static int get_index(const waterfall_t *power, int block, int time_sub, int freq_sub, int bin)
{
return ((((block * power->time_osr) + time_sub) * power->freq_osr + freq_sub) * power->num_bins) + bin;
}
int find_sync(const MagArray *power, int num_candidates, Candidate heap[], int min_score)
int find_sync(const waterfall_t *power, int num_candidates, candidate_t heap[], int min_score)
{
int heap_size = 0;
int num_alt = power->time_osr * power->freq_osr;
@ -28,9 +38,9 @@ int find_sync(const MagArray *power, int num_candidates, Candidate heap[], int m
{
for (int freq_sub = 0; freq_sub < power->freq_osr; ++freq_sub)
{
for (int time_offset = -7; time_offset < power->num_blocks - FT8_NN + 7; ++time_offset)
for (int time_offset = -8; time_offset < power->num_blocks - FT8_NN + 8; ++time_offset)
{
for (int freq_offset = 0; freq_offset < power->num_bins - 8; ++freq_offset)
for (int freq_offset = 0; freq_offset + 8 < power->num_bins; ++freq_offset)
{
int score = 0;
@ -116,7 +126,7 @@ int find_sync(const MagArray *power, int num_candidates, Candidate heap[], int m
return heap_size;
}
void extract_likelihood(const MagArray *power, const Candidate *cand, float *log174)
void extract_likelihood(const waterfall_t *power, const candidate_t *cand, float *log174)
{
int num_alt = power->time_osr * power->freq_osr;
int offset = get_index(power, cand->time_offset, cand->time_sub, cand->freq_sub, cand->freq_offset);
@ -140,23 +150,67 @@ void extract_likelihood(const MagArray *power, const Candidate *cand, float *log
// Compute the variance of log174
float sum = 0;
float sum2 = 0;
float inv_n = 1.0f / FT8_N;
for (int i = 0; i < FT8_N; ++i)
{
sum += log174[i];
sum2 += log174[i] * log174[i];
}
float variance = (sum2 - sum * sum * inv_n) * inv_n;
float inv_n = 1.0f / FT8_N;
float variance = (sum2 - (sum * sum * inv_n)) * inv_n;
// Normalize log174 such that sigma = 2.83 (Why? It's in WSJT-X, ft8b.f90)
// Seems to be 2.83 = sqrt(8). Experimentally sqrt(16) works better.
float norm_factor = sqrtf(16.0f / variance);
// Seems to be 2.83 = sqrt(8). Experimentally sqrt(32) works better.
float norm_factor = sqrtf(32.0f / variance);
for (int i = 0; i < FT8_N; ++i)
{
log174[i] *= norm_factor;
}
}
bool decode(const waterfall_t *power, const candidate_t *cand, message_t *message, int max_iterations, decode_status_t *status)
{
float log174[FT8_N]; // message bits encoded as likelihood
extract_likelihood(power, cand, log174);
uint8_t plain174[FT8_N]; // message bits (0/1)
bp_decode(log174, max_iterations, plain174, &status->ldpc_errors);
// ldpc_decode(log174, kLDPC_iterations, plain174, &n_errors);
if (status->ldpc_errors > 0)
{
return false;
}
// Extract payload + CRC (first FT8_K bits) packed into a byte array
uint8_t a91[FT8_K_BYTES];
pack_bits(plain174, FT8_K, a91);
// Extract CRC and check it
status->crc_extracted = extract_crc(a91);
// TODO: not sure why the zeroing of message is needed and also why CRC over 96-14 bits?
a91[9] &= 0xF8;
a91[10] = 0;
a91[11] = 0;
status->crc_calculated = ft8_crc(a91, 96 - 14);
if (status->crc_extracted != status->crc_calculated)
{
return false;
}
status->unpack_status = unpack77(a91, message->text);
if (status->unpack_status < 0)
{
return false;
}
// Reuse binary message CRC as hash value for the message
message->hash = status->crc_extracted;
return true;
}
static float max2(float a, float b)
{
return (a >= b) ? a : b;
@ -167,7 +221,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 heap[], int heap_size)
static void heapify_down(candidate_t heap[], int heap_size)
{
// heapify from the root down
int current = 0;
@ -190,14 +244,14 @@ static void heapify_down(Candidate heap[], int heap_size)
break;
}
Candidate tmp = heap[largest];
candidate_t tmp = heap[largest];
heap[largest] = heap[current];
heap[current] = tmp;
current = largest;
}
}
static void heapify_up(Candidate heap[], int heap_size)
static void heapify_up(candidate_t heap[], int heap_size)
{
// heapify from the last node up
int current = heap_size - 1;
@ -209,7 +263,7 @@ static void heapify_up(Candidate heap[], int heap_size)
break;
}
Candidate tmp = heap[parent];
candidate_t tmp = heap[parent];
heap[parent] = heap[current];
heap[current] = tmp;
current = parent;

Wyświetl plik

@ -2,6 +2,7 @@
#define _INCLUDE_DECODE_H_
#include <stdint.h>
#include <stdbool.h>
/// Input structure to 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.
@ -16,7 +17,7 @@ typedef struct
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]
} MagArray;
} waterfall_t;
/// Output structure of find_sync() and input structure of extract_likelihood().
/// Holds the position of potential start of a message in time and frequency.
@ -27,23 +28,42 @@ 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;
} 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 contains the status of various steps during decoding of a message
typedef struct
{
int ldpc_errors;
uint16_t crc_extracted;
uint16_t crc_calculated;
int unpack_status;
} 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 type entries (with num_candidates allocated entries)
/// @param[in] min_score Minimal score allowed for trimming unlikely candidates (can be zero for no effect)
/// @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 find_sync(const MagArray *power, int num_candidates, Candidate heap[], int min_score);
int find_sync(const waterfall_t *power, int num_candidates, candidate_t heap[], int min_score);
/// Compute log likelihood log(p(1) / p(0)) of 174 message bits for later use in soft-decision LDPC decoding
/// 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 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
void extract_likelihood(const MagArray *power, const Candidate *cand, float *log174);
/// @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 decode(const waterfall_t *power, const candidate_t *cand, message_t *message, int max_iterations, decode_status_t *status);
#endif // _INCLUDE_DECODE_H_

Wyświetl plik

@ -1,10 +1,9 @@
#include "encode.h"
#include "constants.h"
#include "crc.h"
#include <stdio.h>
#define TOPBIT (1u << (FT8_CRC_WIDTH - 1))
// Returns 1 if an odd number of bits are set in x, zero otherwise
uint8_t parity8(uint8_t x)
{
@ -75,39 +74,6 @@ void encode174(const uint8_t *message, uint8_t *codeword)
}
}
// Compute 14-bit CRC for a sequence of given number of bits
// Adapted from https://barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code
// [IN] message - byte sequence (MSB first)
// [IN] num_bits - number of bits in the sequence
uint16_t ft8_crc(uint8_t *message, int num_bits)
{
uint16_t remainder = 0;
int idx_byte = 0;
// Perform modulo-2 division, a bit at a time.
for (int idx_bit = 0; idx_bit < num_bits; ++idx_bit)
{
if (idx_bit % 8 == 0)
{
// Bring the next byte into the remainder.
remainder ^= (message[idx_byte] << (FT8_CRC_WIDTH - 8));
++idx_byte;
}
// Try to divide the current data bit.
if (remainder & TOPBIT)
{
remainder = (remainder << 1) ^ FT8_CRC_POLYNOMIAL;
}
else
{
remainder = (remainder << 1);
}
}
return remainder & ((TOPBIT << 1) - 1u);
}
// Generate FT8 tone sequence from payload data
// [IN] payload - 10 byte array consisting of 77 bit payload (MSB first)
// [OUT] itone - array of NN (79) bytes to store the generated tones (encoded as 0..7)
@ -124,15 +90,10 @@ void genft8(const uint8_t *payload, uint8_t *itone)
a91[10] = 0;
a91[11] = 0;
// Calculate CRC of 12 bytes = 96 bits, see WSJT-X code
uint16_t checksum = ft8_crc(a91, 96 - 14);
// Compute and add CRC at the end of the message
// a91 contains 77 bits of payload + 14 bits of CRC
add_crc(a91);
// Store the CRC at the end of 77 bit message
a91[9] |= (uint8_t)(checksum >> 11);
a91[10] = (uint8_t)(checksum >> 3);
a91[11] = (uint8_t)(checksum << 5);
// a87 contains 77 bits of payload + 14 bits of CRC
uint8_t codeword[22];
encode174(a91, codeword);

Wyświetl plik

@ -19,9 +19,4 @@ void genft8(const uint8_t *payload, uint8_t *itone);
// * codeword - array of 174 bits stored as 22 bytes (MSB first)
void encode174(const uint8_t *message, uint8_t *codeword);
// Compute 14-bit CRC for a sequence of given number of bits
// [IN] message - byte sequence (MSB first)
// [IN] num_bits - number of bits in the sequence
uint16_t ft8_crc(uint8_t *message, int num_bits);
#endif // _INCLUDE_ENCODE_H_

Wyświetl plik

@ -15,6 +15,7 @@
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>
static int ldpc_check(uint8_t codeword[]);
static float fast_tanh(float x);
@ -177,12 +178,20 @@ void bp_decode(float codeword[], int max_iters, uint8_t plain[], int *ok)
for (int iter = 0; iter < max_iters; ++iter)
{
float zn[FT8_N];
int plain_sum = 0;
// Update bit log likelihood ratios (tov=0 in iter 0)
for (int i = 0; i < FT8_N; ++i)
{
zn[i] = codeword[i] + tov[i][0] + tov[i][1] + tov[i][2];
plain[i] = (zn[i] > 0) ? 1 : 0;
plain_sum += plain[i];
}
if (plain_sum == 0)
{
// message converged to all-zeros, which is prohibited
break;
}
// Check to see if we have a codeword (check before we do any iter)