kopia lustrzana https://github.com/kgoba/ft8_lib
Added prefixes to ft8/ft4/ftx functions and clang-formatted code
rodzic
550f3dc29c
commit
26decf92f7
|
@ -0,0 +1,31 @@
|
|||
BasedOnStyle: WebKit
|
||||
# Cpp11BracedListStyle: false
|
||||
# ColumnLimit: 120
|
||||
IndentCaseLabels: false
|
||||
IndentWidth: 4
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
PointerAlignment: Left
|
||||
SortIncludes: false
|
||||
AlignConsecutiveMacros: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 0
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeBinaryOperators: All
|
||||
BraceWrapping:
|
||||
AfterControlStatement: true
|
||||
AfterClass: true
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: true
|
||||
BeforeElse: true
|
||||
BeforeCatch: true
|
4
Makefile
4
Makefile
|
@ -1,6 +1,6 @@
|
|||
CFLAGS = -O3
|
||||
CFLAGS = -O3 -ggdb3 -fsanitize=address
|
||||
CPPFLAGS = -std=c11 -I.
|
||||
LDFLAGS = -lm
|
||||
LDFLAGS = -lm -fsanitize=address
|
||||
|
||||
TARGETS = gen_ft8 decode_ft8 test
|
||||
|
||||
|
|
|
@ -183,12 +183,13 @@ int main(int argc, char **argv)
|
|||
.num_bins = num_bins,
|
||||
.time_osr = kTime_osr,
|
||||
.freq_osr = kFreq_osr,
|
||||
.mag = mag_power};
|
||||
.mag = mag_power
|
||||
};
|
||||
extract_power(signal, &power, block_size);
|
||||
|
||||
// Find top candidates by Costas sync score and localize them in time and frequency
|
||||
candidate_t candidate_list[kMax_candidates];
|
||||
int num_candidates = find_sync(&power, kMax_candidates, candidate_list, kMin_score);
|
||||
int num_candidates = ft8_find_sync(&power, kMax_candidates, candidate_list, kMin_score);
|
||||
|
||||
// Hash table for decoded messages (to check for duplicates)
|
||||
int num_decoded = 0;
|
||||
|
@ -213,7 +214,7 @@ int main(int argc, char **argv)
|
|||
|
||||
message_t message;
|
||||
decode_status_t status;
|
||||
if (!decode(&power, cand, &message, kLDPC_iterations, &status))
|
||||
if (!ft8_decode(&power, cand, &message, kLDPC_iterations, &status))
|
||||
{
|
||||
if (status.ldpc_errors > 0)
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// 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 ftx_compute_crc(const uint8_t message[], int num_bits)
|
||||
{
|
||||
uint16_t remainder = 0;
|
||||
int idx_byte = 0;
|
||||
|
@ -36,13 +36,13 @@ uint16_t ft8_crc(const uint8_t message[], int num_bits)
|
|||
return remainder & ((TOPBIT << 1) - 1u);
|
||||
}
|
||||
|
||||
uint16_t extract_crc(const uint8_t a91[])
|
||||
uint16_t ftx_extract_crc(const uint8_t a91[])
|
||||
{
|
||||
uint16_t chksum = ((a91[9] & 0x07) << 11) | (a91[10] << 3) | (a91[11] >> 5);
|
||||
return chksum;
|
||||
}
|
||||
|
||||
void add_crc(const uint8_t payload[], uint8_t a91[])
|
||||
void ftx_add_crc(const uint8_t payload[], uint8_t a91[])
|
||||
{
|
||||
// Copy 77 bits of payload data
|
||||
for (int i = 0; i < 10; i++)
|
||||
|
@ -54,7 +54,7 @@ void add_crc(const uint8_t payload[], uint8_t a91[])
|
|||
|
||||
// Calculate CRC of 82 bits (77 + 5 zeros)
|
||||
// 'The CRC is calculated on the source-encoded message, zero-extended from 77 to 82 bits'
|
||||
uint16_t checksum = ft8_crc(a91, 96 - 14);
|
||||
uint16_t checksum = ftx_compute_crc(a91, 96 - 14);
|
||||
|
||||
// Store the CRC at the end of 77 bit message
|
||||
a91[9] |= (uint8_t)(checksum >> 11);
|
||||
|
|
12
ft8/crc.h
12
ft8/crc.h
|
@ -4,19 +4,19 @@
|
|||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Compute 14-bit CRC for a sequence of given number of 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 ft8_crc(const uint8_t message[], int num_bits);
|
||||
uint16_t ftx_compute_crc(const uint8_t message[], int num_bits);
|
||||
|
||||
/// Extract the FT8 CRC of a packed message (during decoding)
|
||||
/// 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 extract_crc(const uint8_t a91[]);
|
||||
uint16_t ftx_extract_crc(const uint8_t a91[]);
|
||||
|
||||
/// Add the FT8 CRC to a packed message (during encoding)
|
||||
/// 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 add_crc(const uint8_t payload[], uint8_t a91[]);
|
||||
void ftx_add_crc(const uint8_t payload[], uint8_t a91[]);
|
||||
|
||||
#endif // _INCLUDE_CRC_H_
|
48
ft8/decode.c
48
ft8/decode.c
|
@ -12,7 +12,14 @@
|
|||
/// @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 void ft8_extract_likelihood(const waterfall_t* power, const 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[]
|
||||
/// @param[in] plain Array of bits (0 and nonzero values) with num_bits entires
|
||||
/// @param[in] num_bits Number of bits (entries) passed in bit_array
|
||||
/// @param[out] packed Byte-packed bits representing the data in bit_array
|
||||
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);
|
||||
|
@ -26,7 +33,7 @@ static int get_index(const waterfall_t *power, int block, int time_sub, int freq
|
|||
return ((((block * power->time_osr) + time_sub) * power->freq_osr + freq_sub) * power->num_bins) + bin;
|
||||
}
|
||||
|
||||
int find_sync(const waterfall_t *power, int num_candidates, candidate_t heap[], int min_score)
|
||||
int ft8_find_sync(const waterfall_t* power, int num_candidates, candidate_t heap[], int min_score)
|
||||
{
|
||||
int heap_size = 0;
|
||||
int sym_stride = power->time_osr * power->freq_osr * power->num_bins;
|
||||
|
@ -143,7 +150,7 @@ int find_sync(const waterfall_t *power, int num_candidates, candidate_t heap[],
|
|||
return heap_size;
|
||||
}
|
||||
|
||||
void extract_likelihood(const waterfall_t *power, const candidate_t *cand, float *log174)
|
||||
void ft8_extract_likelihood(const waterfall_t* power, const candidate_t* cand, float* log174)
|
||||
{
|
||||
int sym_stride = power->time_osr * power->freq_osr * power->num_bins;
|
||||
int offset = get_index(power, cand->time_offset, cand->time_sub, cand->freq_sub, cand->freq_offset);
|
||||
|
@ -193,10 +200,10 @@ void extract_likelihood(const waterfall_t *power, const candidate_t *cand, float
|
|||
}
|
||||
}
|
||||
|
||||
bool decode(const waterfall_t *power, const candidate_t *cand, message_t *message, int max_iterations, decode_status_t *status)
|
||||
bool ft8_decode(const waterfall_t* power, const candidate_t* cand, message_t* message, int max_iterations, decode_status_t* status)
|
||||
{
|
||||
float log174[FT8_LDPC_N]; // message bits encoded as likelihood
|
||||
extract_likelihood(power, cand, log174);
|
||||
ft8_extract_likelihood(power, cand, log174);
|
||||
|
||||
uint8_t plain174[FT8_LDPC_N]; // message bits (0/1)
|
||||
bp_decode(log174, max_iterations, plain174, &status->ldpc_errors);
|
||||
|
@ -212,11 +219,11 @@ bool decode(const waterfall_t *power, const candidate_t *cand, message_t *messag
|
|||
pack_bits(plain174, FT8_LDPC_K, a91);
|
||||
|
||||
// Extract CRC and check it
|
||||
status->crc_extracted = extract_crc(a91);
|
||||
status->crc_extracted = ftx_extract_crc(a91);
|
||||
// [1]: 'The CRC is calculated on the source-encoded message, zero-extended from 77 to 82 bits.'
|
||||
a91[9] &= 0xF8;
|
||||
a91[10] &= 0x00;
|
||||
status->crc_calculated = ft8_crc(a91, 96 - 14);
|
||||
status->crc_calculated = ftx_compute_crc(a91, 96 - 14);
|
||||
|
||||
if (status->crc_extracted != status->crc_calculated)
|
||||
{
|
||||
|
@ -367,3 +374,30 @@ static void decode_multi_symbols(const uint8_t *power, int num_bins, int n_syms,
|
|||
log174[bit_idx + i] = max_one - max_zero;
|
||||
}
|
||||
}
|
||||
|
||||
// Packs a string of bits each represented as a zero/non-zero byte in plain[],
|
||||
// as a string of packed bits starting from the MSB of the first byte of packed[]
|
||||
static void pack_bits(const uint8_t bit_array[], int num_bits, uint8_t packed[])
|
||||
{
|
||||
int num_bytes = (num_bits + 7) / 8;
|
||||
for (int i = 0; i < num_bytes; ++i)
|
||||
{
|
||||
packed[i] = 0;
|
||||
}
|
||||
|
||||
uint8_t mask = 0x80;
|
||||
int byte_idx = 0;
|
||||
for (int i = 0; i < num_bits; ++i)
|
||||
{
|
||||
if (bit_array[i])
|
||||
{
|
||||
packed[byte_idx] |= mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
if (!mask)
|
||||
{
|
||||
mask = 0x80;
|
||||
++byte_idx;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/// Input structure to find_sync() function. This structure describes stored waterfall data over the whole message slot.
|
||||
/// 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.
|
||||
|
@ -19,7 +19,7 @@ typedef struct
|
|||
uint8_t* mag; ///< FFT magnitudes stored as uint8_t[blocks][time_osr][freq_osr][num_bins]
|
||||
} waterfall_t;
|
||||
|
||||
/// Output structure of find_sync() and input structure of extract_likelihood().
|
||||
/// 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
|
||||
{
|
||||
|
@ -55,7 +55,7 @@ typedef struct
|
|||
/// @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 waterfall_t *power, int num_candidates, candidate_t heap[], int min_score);
|
||||
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
|
||||
|
@ -64,6 +64,6 @@ int find_sync(const waterfall_t *power, int num_candidates, candidate_t heap[],
|
|||
/// @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);
|
||||
bool ft8_decode(const waterfall_t* power, const candidate_t* cand, message_t* message, int max_iterations, decode_status_t* status);
|
||||
|
||||
#endif // _INCLUDE_DECODE_H_
|
||||
|
|
14
ft8/encode.c
14
ft8/encode.c
|
@ -5,7 +5,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
// Returns 1 if an odd number of bits are set in x, zero otherwise
|
||||
uint8_t parity8(uint8_t x)
|
||||
static uint8_t parity8(uint8_t x)
|
||||
{
|
||||
x ^= x >> 4; // a b c d ae bf cg dh
|
||||
x ^= x >> 2; // a b ac bd cae dbf aecg bfdh
|
||||
|
@ -13,13 +13,13 @@ uint8_t parity8(uint8_t x)
|
|||
return x % 2; // modulo 2
|
||||
}
|
||||
|
||||
// Encode a 91-bit message and return a 174-bit codeword.
|
||||
// Encode via LDPC a 91-bit message and return a 174-bit codeword.
|
||||
// The generator matrix has dimensions (87,87).
|
||||
// The code is a (174,91) regular LDPC code with column weight 3.
|
||||
// Arguments:
|
||||
// [IN] message - array of 91 bits stored as 12 bytes (MSB first)
|
||||
// [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first)
|
||||
void encode174(const uint8_t *message, uint8_t *codeword)
|
||||
static void encode174(const uint8_t* message, uint8_t* codeword)
|
||||
{
|
||||
// This implementation accesses the generator bits straight from the packed binary representation in kFT8_LDPC_generator
|
||||
|
||||
|
@ -62,13 +62,13 @@ void encode174(const uint8_t *message, uint8_t *codeword)
|
|||
}
|
||||
}
|
||||
|
||||
void genft8(const uint8_t *payload, uint8_t *tones)
|
||||
void ft8_encode(const uint8_t* payload, uint8_t* tones)
|
||||
{
|
||||
uint8_t a91[12]; // Store 77 bits of payload + 14 bits CRC
|
||||
|
||||
// Compute and add CRC at the end of the message
|
||||
// a91 contains 77 bits of payload + 14 bits of CRC
|
||||
add_crc(payload, a91);
|
||||
ftx_add_crc(payload, a91);
|
||||
|
||||
uint8_t codeword[22];
|
||||
encode174(a91, codeword);
|
||||
|
@ -124,13 +124,13 @@ void genft8(const uint8_t *payload, uint8_t *tones)
|
|||
}
|
||||
}
|
||||
|
||||
void genft4(const uint8_t *payload, uint8_t *tones)
|
||||
void ft4_encode(const uint8_t* payload, uint8_t* tones)
|
||||
{
|
||||
uint8_t a91[12]; // Store 77 bits of payload + 14 bits CRC
|
||||
|
||||
// Compute and add CRC at the end of the message
|
||||
// a91 contains 77 bits of payload + 14 bits of CRC
|
||||
add_crc(payload, a91);
|
||||
ftx_add_crc(payload, a91);
|
||||
|
||||
uint8_t codeword[22];
|
||||
encode174(a91, codeword); // 91 bits -> 174 bits
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
/// 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 genft8(const uint8_t *payload, uint8_t *tones);
|
||||
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 genft4(const uint8_t *payload, uint8_t *tones);
|
||||
void ft4_encode(const uint8_t* payload, uint8_t* tones);
|
||||
|
||||
#endif // _INCLUDE_ENCODE_H_
|
||||
|
|
27
ft8/ldpc.c
27
ft8/ldpc.c
|
@ -21,33 +21,6 @@ static int ldpc_check(uint8_t codeword[]);
|
|||
static float fast_tanh(float x);
|
||||
static float fast_atanh(float x);
|
||||
|
||||
// Packs a string of bits each represented as a zero/non-zero byte in plain[],
|
||||
// as a string of packed bits starting from the MSB of the first byte of packed[]
|
||||
void pack_bits(const uint8_t plain[], int num_bits, uint8_t packed[])
|
||||
{
|
||||
int num_bytes = (num_bits + 7) / 8;
|
||||
for (int i = 0; i < num_bytes; ++i)
|
||||
{
|
||||
packed[i] = 0;
|
||||
}
|
||||
|
||||
uint8_t mask = 0x80;
|
||||
int byte_idx = 0;
|
||||
for (int i = 0; i < num_bits; ++i)
|
||||
{
|
||||
if (plain[i])
|
||||
{
|
||||
packed[byte_idx] |= mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
if (!mask)
|
||||
{
|
||||
mask = 0x80;
|
||||
++byte_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// codeword is 174 log-likelihoods.
|
||||
// plain is a return value, 174 ints, to be 0 or 1.
|
||||
// max_iters is how hard to try.
|
||||
|
|
|
@ -11,8 +11,4 @@ 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);
|
||||
|
||||
// Packs a string of bits each represented as a zero/non-zero byte in plain[],
|
||||
// as a string of packed bits starting from the MSB of the first byte of packed[]
|
||||
void pack_bits(const uint8_t plain[], int num_bits, uint8_t packed[]);
|
||||
|
||||
#endif // _INCLUDE_LDPC_H_
|
||||
|
|
14
ft8/pack.c
14
ft8/pack.c
|
@ -75,9 +75,7 @@ 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)
|
||||
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)
|
||||
{
|
||||
// This is a standard callsign
|
||||
int32_t n28 = i0;
|
||||
|
@ -138,9 +136,7 @@ uint16_t packgrid(const char *grid4)
|
|||
return MAXGRID4 + 4;
|
||||
|
||||
// Check for standard 4 letter grid
|
||||
if (in_range(grid4[0], 'A', 'R') &&
|
||||
in_range(grid4[1], 'A', 'R') &&
|
||||
is_digit(grid4[2]) && is_digit(grid4[3]))
|
||||
if (in_range(grid4[0], 'A', 'R') && in_range(grid4[1], 'A', 'R') && is_digit(grid4[2]) && is_digit(grid4[3]))
|
||||
{
|
||||
uint16_t igrid4 = (grid4[0] - 'A');
|
||||
igrid4 = igrid4 * 18 + (grid4[1] - 'A');
|
||||
|
@ -321,7 +317,8 @@ bool test1()
|
|||
"LL3JG",
|
||||
"LL3AJG",
|
||||
"CQ ",
|
||||
0};
|
||||
0
|
||||
};
|
||||
|
||||
for (int i = 0; inputs[i]; ++i)
|
||||
{
|
||||
|
@ -341,7 +338,8 @@ bool test2()
|
|||
"L0UAA LL3JG +02",
|
||||
"L0UAA LL3JG RRR",
|
||||
"L0UAA LL3JG 73",
|
||||
0};
|
||||
0
|
||||
};
|
||||
|
||||
for (int i = 0; inputs[i]; ++i)
|
||||
{
|
||||
|
|
|
@ -170,11 +170,11 @@ int main(int argc, char **argv)
|
|||
uint8_t tones[num_tones]; // Array of 79 tones (symbols)
|
||||
if (is_ft4)
|
||||
{
|
||||
genft4(packed, tones);
|
||||
ft4_encode(packed, tones);
|
||||
}
|
||||
else
|
||||
{
|
||||
genft8(packed, tones);
|
||||
ft8_encode(packed, tones);
|
||||
}
|
||||
|
||||
printf("FSK tones: ");
|
||||
|
|
2
test.c
2
test.c
|
@ -96,7 +96,7 @@ void test2() {
|
|||
|
||||
void test3() {
|
||||
uint8_t test_in2[10] = { 0x11, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x04, 0x01, 0x00 };
|
||||
uint16_t crc1 = ft8_crc(test_in2, 76); // Calculate CRC of 76 bits only
|
||||
uint16_t crc1 = ftx_compute_crc(test_in2, 76); // Calculate CRC of 76 bits only
|
||||
LOG(LOG_INFO, "CRC: %04x\n", crc1); // should be 0x0708
|
||||
}
|
||||
*/
|
||||
|
|
Ładowanie…
Reference in New Issue