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
|
||||
|
||||
|
|
|
@ -7,25 +7,25 @@
|
|||
#include <stdint.h>
|
||||
|
||||
// Save signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers.
|
||||
void save_wav(const float *signal, int num_samples, int sample_rate, const char *path)
|
||||
void save_wav(const float* signal, int num_samples, int sample_rate, const char* path)
|
||||
{
|
||||
char subChunk1ID[4] = {'f', 'm', 't', ' '};
|
||||
char subChunk1ID[4] = { 'f', 'm', 't', ' ' };
|
||||
uint32_t subChunk1Size = 16; // 16 for PCM
|
||||
uint16_t audioFormat = 1; // PCM = 1
|
||||
uint16_t audioFormat = 1; // PCM = 1
|
||||
uint16_t numChannels = 1;
|
||||
uint16_t bitsPerSample = 16;
|
||||
uint32_t sampleRate = sample_rate;
|
||||
uint16_t blockAlign = numChannels * bitsPerSample / 8;
|
||||
uint32_t byteRate = sampleRate * blockAlign;
|
||||
|
||||
char subChunk2ID[4] = {'d', 'a', 't', 'a'};
|
||||
char subChunk2ID[4] = { 'd', 'a', 't', 'a' };
|
||||
uint32_t subChunk2Size = num_samples * blockAlign;
|
||||
|
||||
char chunkID[4] = {'R', 'I', 'F', 'F'};
|
||||
char chunkID[4] = { 'R', 'I', 'F', 'F' };
|
||||
uint32_t chunkSize = 4 + (8 + subChunk1Size) + (8 + subChunk2Size);
|
||||
char format[4] = {'W', 'A', 'V', 'E'};
|
||||
char format[4] = { 'W', 'A', 'V', 'E' };
|
||||
|
||||
int16_t *raw_data = (int16_t *)malloc(num_samples * blockAlign);
|
||||
int16_t* raw_data = (int16_t*)malloc(num_samples * blockAlign);
|
||||
for (int i = 0; i < num_samples; i++)
|
||||
{
|
||||
float x = signal[i];
|
||||
|
@ -36,7 +36,7 @@ void save_wav(const float *signal, int num_samples, int sample_rate, const char
|
|||
raw_data[i] = (int)(0.5 + (x * 32767.0));
|
||||
}
|
||||
|
||||
FILE *f = fopen(path, "wb");
|
||||
FILE* f = fopen(path, "wb");
|
||||
|
||||
// NOTE: works only on little-endian architecture
|
||||
fwrite(chunkID, sizeof(chunkID), 1, f);
|
||||
|
@ -63,48 +63,48 @@ void save_wav(const float *signal, int num_samples, int sample_rate, const char
|
|||
}
|
||||
|
||||
// Load signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers.
|
||||
int load_wav(float *signal, int *num_samples, int *sample_rate, const char *path)
|
||||
int load_wav(float* signal, int* num_samples, int* sample_rate, const char* path)
|
||||
{
|
||||
char subChunk1ID[4]; // = {'f', 'm', 't', ' '};
|
||||
char subChunk1ID[4]; // = {'f', 'm', 't', ' '};
|
||||
uint32_t subChunk1Size; // = 16; // 16 for PCM
|
||||
uint16_t audioFormat; // = 1; // PCM = 1
|
||||
uint16_t numChannels; // = 1;
|
||||
uint16_t audioFormat; // = 1; // PCM = 1
|
||||
uint16_t numChannels; // = 1;
|
||||
uint16_t bitsPerSample; // = 16;
|
||||
uint32_t sampleRate;
|
||||
uint16_t blockAlign; // = numChannels * bitsPerSample / 8;
|
||||
uint32_t byteRate; // = sampleRate * blockAlign;
|
||||
uint32_t byteRate; // = sampleRate * blockAlign;
|
||||
|
||||
char subChunk2ID[4]; // = {'d', 'a', 't', 'a'};
|
||||
char subChunk2ID[4]; // = {'d', 'a', 't', 'a'};
|
||||
uint32_t subChunk2Size; // = num_samples * blockAlign;
|
||||
|
||||
char chunkID[4]; // = {'R', 'I', 'F', 'F'};
|
||||
char chunkID[4]; // = {'R', 'I', 'F', 'F'};
|
||||
uint32_t chunkSize; // = 4 + (8 + subChunk1Size) + (8 + subChunk2Size);
|
||||
char format[4]; // = {'W', 'A', 'V', 'E'};
|
||||
char format[4]; // = {'W', 'A', 'V', 'E'};
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
FILE* f = fopen(path, "rb");
|
||||
|
||||
// NOTE: works only on little-endian architecture
|
||||
fread((void *)chunkID, sizeof(chunkID), 1, f);
|
||||
fread((void *)&chunkSize, sizeof(chunkSize), 1, f);
|
||||
fread((void *)format, sizeof(format), 1, f);
|
||||
fread((void*)chunkID, sizeof(chunkID), 1, f);
|
||||
fread((void*)&chunkSize, sizeof(chunkSize), 1, f);
|
||||
fread((void*)format, sizeof(format), 1, f);
|
||||
|
||||
fread((void *)subChunk1ID, sizeof(subChunk1ID), 1, f);
|
||||
fread((void *)&subChunk1Size, sizeof(subChunk1Size), 1, f);
|
||||
fread((void*)subChunk1ID, sizeof(subChunk1ID), 1, f);
|
||||
fread((void*)&subChunk1Size, sizeof(subChunk1Size), 1, f);
|
||||
if (subChunk1Size != 16)
|
||||
return -1;
|
||||
|
||||
fread((void *)&audioFormat, sizeof(audioFormat), 1, f);
|
||||
fread((void *)&numChannels, sizeof(numChannels), 1, f);
|
||||
fread((void *)&sampleRate, sizeof(sampleRate), 1, f);
|
||||
fread((void *)&byteRate, sizeof(byteRate), 1, f);
|
||||
fread((void *)&blockAlign, sizeof(blockAlign), 1, f);
|
||||
fread((void *)&bitsPerSample, sizeof(bitsPerSample), 1, f);
|
||||
fread((void*)&audioFormat, sizeof(audioFormat), 1, f);
|
||||
fread((void*)&numChannels, sizeof(numChannels), 1, f);
|
||||
fread((void*)&sampleRate, sizeof(sampleRate), 1, f);
|
||||
fread((void*)&byteRate, sizeof(byteRate), 1, f);
|
||||
fread((void*)&blockAlign, sizeof(blockAlign), 1, f);
|
||||
fread((void*)&bitsPerSample, sizeof(bitsPerSample), 1, f);
|
||||
|
||||
if (audioFormat != 1 || numChannels != 1 || bitsPerSample != 16)
|
||||
return -1;
|
||||
|
||||
fread((void *)subChunk2ID, sizeof(subChunk2ID), 1, f);
|
||||
fread((void *)&subChunk2Size, sizeof(subChunk2Size), 1, f);
|
||||
fread((void*)subChunk2ID, sizeof(subChunk2ID), 1, f);
|
||||
fread((void*)&subChunk2Size, sizeof(subChunk2Size), 1, f);
|
||||
|
||||
if (subChunk2Size / blockAlign > *num_samples)
|
||||
return -2;
|
||||
|
@ -112,9 +112,9 @@ int load_wav(float *signal, int *num_samples, int *sample_rate, const char *path
|
|||
*num_samples = subChunk2Size / blockAlign;
|
||||
*sample_rate = sampleRate;
|
||||
|
||||
int16_t *raw_data = (int16_t *)malloc(*num_samples * blockAlign);
|
||||
int16_t* raw_data = (int16_t*)malloc(*num_samples * blockAlign);
|
||||
|
||||
fread((void *)raw_data, blockAlign, *num_samples, f);
|
||||
fread((void*)raw_data, blockAlign, *num_samples, f);
|
||||
for (int i = 0; i < *num_samples; i++)
|
||||
{
|
||||
signal[i] = raw_data[i] / 32768.0f;
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#define _INCLUDE_WAVE_H_
|
||||
|
||||
// Save signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers.
|
||||
void save_wav(const float *signal, int num_samples, int sample_rate, const char *path);
|
||||
void save_wav(const float* signal, int num_samples, int sample_rate, const char* path);
|
||||
|
||||
// Load signal in floating point format (-1 .. +1) as a WAVE file using 16-bit signed integers.
|
||||
int load_wav(float *signal, int *num_samples, int *sample_rate, const char *path);
|
||||
int load_wav(float* signal, int* num_samples, int* sample_rate, const char* path);
|
||||
|
||||
#endif // _INCLUDE_WAVE_H_
|
||||
|
|
21
decode_ft8.c
21
decode_ft8.c
|
@ -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[], waterfall_t *power, int block_size)
|
||||
void extract_power(const float signal[], waterfall_t* power, int block_size)
|
||||
{
|
||||
const int subblock_size = block_size / power->time_osr;
|
||||
const int nfft = block_size * power->freq_osr;
|
||||
|
@ -93,7 +93,7 @@ void extract_power(const float signal[], waterfall_t *power, int block_size)
|
|||
LOG(LOG_INFO, "N_FFT = %d\n", nfft);
|
||||
LOG(LOG_INFO, "FFT work area = %lu\n", fft_work_size);
|
||||
|
||||
void *fft_work = malloc(fft_work_size);
|
||||
void* fft_work = malloc(fft_work_size);
|
||||
kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(nfft, 0, fft_work, &fft_work_size);
|
||||
|
||||
int offset = 0;
|
||||
|
@ -146,7 +146,7 @@ void extract_power(const float signal[], waterfall_t *power, int block_size)
|
|||
free(fft_work);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Expect one command-line argument
|
||||
if (argc < 2)
|
||||
|
@ -155,7 +155,7 @@ int main(int argc, char **argv)
|
|||
return -1;
|
||||
}
|
||||
|
||||
const char *wav_path = argv[1];
|
||||
const char* wav_path = argv[1];
|
||||
|
||||
int sample_rate = 12000;
|
||||
int num_samples = 15 * sample_rate;
|
||||
|
@ -169,7 +169,7 @@ int main(int argc, char **argv)
|
|||
|
||||
// Compute DSP parameters that depend on the sample rate
|
||||
const int num_bins = (int)(sample_rate / (2 * kFSK_dev)); // number bins of FSK tone width that the spectrum can be divided into
|
||||
const int block_size = (int)(sample_rate / kFSK_dev); // samples corresponding to one FSK symbol
|
||||
const int block_size = (int)(sample_rate / kFSK_dev); // samples corresponding to one FSK symbol
|
||||
const int subblock_size = block_size / kTime_osr;
|
||||
const int nfft = block_size * kFreq_osr;
|
||||
const int num_blocks = (num_samples - nfft + subblock_size) / block_size;
|
||||
|
@ -183,17 +183,18 @@ 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;
|
||||
message_t decoded[kMax_decoded_messages];
|
||||
message_t *decoded_hashtable[kMax_decoded_messages];
|
||||
message_t* decoded_hashtable[kMax_decoded_messages];
|
||||
|
||||
// Initialize hash table pointers
|
||||
for (int i = 0; i < kMax_decoded_messages; ++i)
|
||||
|
@ -204,7 +205,7 @@ int main(int argc, char **argv)
|
|||
// Go over candidates and attempt to decode messages
|
||||
for (int idx = 0; idx < num_candidates; ++idx)
|
||||
{
|
||||
const candidate_t *cand = &candidate_list[idx];
|
||||
const candidate_t* cand = &candidate_list[idx];
|
||||
if (cand->score < kMin_score)
|
||||
continue;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -9,21 +9,21 @@
|
|||
#define FT8_NN (79) ///< Total channel symbols (FT8_NS + FT8_ND)
|
||||
|
||||
// Define FT4 symbol counts
|
||||
#define FT4_ND (87) ///< Data symbols
|
||||
#define FT4_NS (16) ///< Sync symbols (3 @ Costas 7x7)
|
||||
#define FT4_NR (2) ///< Ramp symbols (beginning + end)
|
||||
#define FT4_ND (87) ///< Data symbols
|
||||
#define FT4_NS (16) ///< Sync symbols (3 @ Costas 7x7)
|
||||
#define FT4_NR (2) ///< Ramp symbols (beginning + end)
|
||||
#define FT4_NN (105) ///< Total channel symbols (FT4_NS + FT4_ND + FT4_NR)
|
||||
|
||||
// Define LDPC parameters
|
||||
#define FT8_LDPC_N (174) ///< Number of bits in the encoded message (payload with LDPC checksum bits)
|
||||
#define FT8_LDPC_K (91) ///< Number of payload bits (including CRC)
|
||||
#define FT8_LDPC_M (83) ///< Number of LDPC checksum bits (FT8_LDPC_N - FT8_LDPC_K)
|
||||
#define FT8_LDPC_N (174) ///< Number of bits in the encoded message (payload with LDPC checksum bits)
|
||||
#define FT8_LDPC_K (91) ///< Number of payload bits (including CRC)
|
||||
#define FT8_LDPC_M (83) ///< Number of LDPC checksum bits (FT8_LDPC_N - FT8_LDPC_K)
|
||||
#define FT8_LDPC_N_BYTES ((FT8_LDPC_N + 7) / 8) ///< Number of whole bytes needed to store 174 bits (full message)
|
||||
#define FT8_LDPC_K_BYTES ((FT8_LDPC_K + 7) / 8) ///< Number of whole bytes needed to store 91 bits (payload + CRC only)
|
||||
|
||||
// Define CRC parameters
|
||||
#define FT8_CRC_POLYNOMIAL ((uint16_t)0x2757u) ///< CRC-14 polynomial without the leading (MSB) 1
|
||||
#define FT8_CRC_WIDTH (14)
|
||||
#define FT8_CRC_WIDTH (14)
|
||||
|
||||
/// Costas 7x7 tone pattern for synchronization
|
||||
extern const uint8_t kFT8_Costas_pattern[7];
|
||||
|
|
|
@ -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_
|
62
ft8/decode.c
62
ft8/decode.c
|
@ -12,21 +12,28 @@
|
|||
/// @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);
|
||||
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 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 waterfall_t *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 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;
|
||||
|
@ -58,7 +65,7 @@ int find_sync(const waterfall_t *power, int num_candidates, candidate_t heap[],
|
|||
break;
|
||||
|
||||
int offset = get_index(power, block, time_sub, freq_sub, freq_offset);
|
||||
const uint8_t *p8 = power->mag + offset;
|
||||
const uint8_t* p8 = power->mag + offset;
|
||||
|
||||
// Weighted difference between the expected and all other symbols
|
||||
// Does not work as well as the alternative score below
|
||||
|
@ -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);
|
||||
|
@ -168,7 +175,7 @@ void extract_likelihood(const waterfall_t *power, const candidate_t *cand, float
|
|||
else
|
||||
{
|
||||
// Pointer to 8 bins of the current symbol
|
||||
const uint8_t *ps = power->mag + offset + (sym_idx * sym_stride);
|
||||
const uint8_t* ps = power->mag + offset + (sym_idx * sym_stride);
|
||||
|
||||
decode_symbol(ps, bit_idx, log174);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -296,7 +303,7 @@ static void heapify_up(candidate_t heap[], int heap_size)
|
|||
}
|
||||
|
||||
// Compute unnormalized log likelihood log(p(1) / p(0)) of 3 message bits (1 FSK symbol)
|
||||
static void decode_symbol(const uint8_t *power, int bit_idx, float *log174)
|
||||
static void decode_symbol(const uint8_t* power, int bit_idx, float* log174)
|
||||
{
|
||||
// Cleaned up code for the simple case of n_syms==1
|
||||
float s2[8];
|
||||
|
@ -312,7 +319,7 @@ static void decode_symbol(const uint8_t *power, int bit_idx, float *log174)
|
|||
}
|
||||
|
||||
// Compute unnormalized log likelihood log(p(1) / p(0)) of bits corresponding to several FSK symbols at once
|
||||
static void decode_multi_symbols(const uint8_t *power, int num_bins, int n_syms, 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)
|
||||
{
|
||||
const int n_bits = 3 * n_syms;
|
||||
const int n_tones = (1 << n_bits);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
22
ft8/decode.h
22
ft8/decode.h
|
@ -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.
|
||||
|
@ -13,21 +13,21 @@
|
|||
typedef struct
|
||||
{
|
||||
int num_blocks; ///< number of total blocks (symbols) in terms of 160 ms time periods
|
||||
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 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]
|
||||
} 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
|
||||
{
|
||||
int16_t score; ///< Candidate score (non-negative number; higher score means higher likelihood)
|
||||
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
|
||||
uint8_t time_sub; ///< Index of the time subdivision used
|
||||
uint8_t freq_sub; ///< Index of the frequency subdivision used
|
||||
} candidate_t;
|
||||
|
||||
/// Structure that holds the decoded message
|
||||
|
@ -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_
|
||||
|
|
28
ft8/encode.c
28
ft8/encode.c
|
@ -5,21 +5,21 @@
|
|||
#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
|
||||
x ^= x >> 1; // a ab bac acbd bdcae caedbf aecgbfdh
|
||||
x ^= x >> 4; // a b c d ae bf cg dh
|
||||
x ^= x >> 2; // a b ac bd cae dbf aecg bfdh
|
||||
x ^= x >> 1; // a ab bac acbd bdcae caedbf aecgbfdh
|
||||
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
|
||||
|
||||
|
@ -31,7 +31,7 @@ void encode174(const uint8_t *message, uint8_t *codeword)
|
|||
|
||||
// Compute the byte index and bit mask for the first checksum bit
|
||||
uint8_t col_mask = (0x80u >> (FT8_LDPC_K % 8u)); // bitmask of current byte
|
||||
uint8_t col_idx = FT8_LDPC_K_BYTES - 1; // index into byte array
|
||||
uint8_t col_idx = FT8_LDPC_K_BYTES - 1; // index into byte array
|
||||
|
||||
// Compute the LDPC checksum bits and store them in codeword
|
||||
for (int i = 0; i < FT8_LDPC_M; ++i)
|
||||
|
@ -43,7 +43,7 @@ void encode174(const uint8_t *message, uint8_t *codeword)
|
|||
for (int j = 0; j < FT8_LDPC_K_BYTES; ++j)
|
||||
{
|
||||
uint8_t bits = message[j] & kFT8_LDPC_generator[i][j]; // bitwise AND (bitwise multiplication)
|
||||
nsum ^= parity8(bits); // bitwise XOR (addition modulo 2)
|
||||
nsum ^= parity8(bits); // bitwise XOR (addition modulo 2)
|
||||
}
|
||||
|
||||
// Set the current checksum bit in codeword if nsum is odd
|
||||
|
@ -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);
|
||||
|
@ -77,7 +77,7 @@ void genft8(const uint8_t *payload, uint8_t *tones)
|
|||
// Total symbols: 79 (FT8_NN)
|
||||
|
||||
uint8_t mask = 0x80u; // Mask to extract 1 bit from codeword
|
||||
int i_byte = 0; // Index of the current byte of the codeword
|
||||
int i_byte = 0; // Index of the current byte of the codeword
|
||||
for (int i_tone = 0; i_tone < FT8_NN; ++i_tone)
|
||||
{
|
||||
if ((i_tone >= 0) && (i_tone < 7))
|
||||
|
@ -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
|
||||
|
@ -139,7 +139,7 @@ void genft4(const uint8_t *payload, uint8_t *tones)
|
|||
// Total symbols: 105 (FT4_NN)
|
||||
|
||||
uint8_t mask = 0x80u; // Mask to extract 1 bit from codeword
|
||||
int i_byte = 0; // Index of the current byte of the codeword
|
||||
int i_byte = 0; // Index of the current byte of the codeword
|
||||
for (int i_tone = 0; i_tone < FT4_NN; ++i_tone)
|
||||
{
|
||||
if ((i_tone == 0) || (i_tone == 104))
|
||||
|
|
|
@ -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_
|
||||
|
|
31
ft8/ldpc.c
31
ft8/ldpc.c
|
@ -21,38 +21,11 @@ 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.
|
||||
// ok == 87 means success.
|
||||
void ldpc_decode(float codeword[], int max_iters, uint8_t plain[], int *ok)
|
||||
void ldpc_decode(float codeword[], int max_iters, uint8_t plain[], int* ok)
|
||||
{
|
||||
float m[FT8_LDPC_M][FT8_LDPC_N]; // ~60 kB
|
||||
float e[FT8_LDPC_M][FT8_LDPC_N]; // ~60 kB
|
||||
|
@ -154,7 +127,7 @@ static int ldpc_check(uint8_t codeword[])
|
|||
return errors;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
float tov[FT8_LDPC_N][3];
|
||||
float toc[FT8_LDPC_M][7];
|
||||
|
|
|
@ -7,12 +7,8 @@
|
|||
// 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 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[]);
|
||||
void bp_decode(float codeword[], int max_iters, uint8_t plain[], int* ok);
|
||||
|
||||
#endif // _INCLUDE_LDPC_H_
|
||||
|
|
44
ft8/pack.c
44
ft8/pack.c
|
@ -6,8 +6,8 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define NTOKENS ((uint32_t)2063592L)
|
||||
#define MAX22 ((uint32_t)4194304L)
|
||||
#define NTOKENS ((uint32_t)2063592L)
|
||||
#define MAX22 ((uint32_t)4194304L)
|
||||
#define MAXGRID4 ((uint16_t)32400)
|
||||
|
||||
// TODO: This is wasteful, should figure out something more elegant
|
||||
|
@ -19,7 +19,7 @@ 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)
|
||||
int32_t pack28(const char* callsign)
|
||||
{
|
||||
// Check for special tokens first
|
||||
if (starts_with(callsign, "DE "))
|
||||
|
@ -38,7 +38,7 @@ int32_t pack28(const char *callsign)
|
|||
|
||||
// TODO: Check for <...> callsign
|
||||
|
||||
char c6[6] = {' ', ' ', ' ', ' ', ' ', ' '};
|
||||
char c6[6] = { ' ', ' ', ' ', ' ', ' ', ' ' };
|
||||
|
||||
int length = 0; // strlen(callsign); // We will need it later
|
||||
while (callsign[length] != ' ' && callsign[length] != 0)
|
||||
|
@ -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;
|
||||
|
@ -100,7 +98,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)
|
||||
bool chkcall(const char* call, char* bc)
|
||||
{
|
||||
int length = strlen(call); // n1=len_trim(w)
|
||||
if (length > 11)
|
||||
|
@ -121,7 +119,7 @@ bool chkcall(const char *call, char *bc)
|
|||
return true;
|
||||
}
|
||||
|
||||
uint16_t packgrid(const char *grid4)
|
||||
uint16_t packgrid(const char* grid4)
|
||||
{
|
||||
if (grid4 == 0)
|
||||
{
|
||||
|
@ -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');
|
||||
|
@ -168,15 +164,15 @@ 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)
|
||||
int pack77_1(const char* msg, uint8_t* b77)
|
||||
{
|
||||
// Locate the first delimiter
|
||||
const char *s1 = strchr(msg, ' ');
|
||||
const char* s1 = strchr(msg, ' ');
|
||||
if (s1 == 0)
|
||||
return -1;
|
||||
|
||||
const char *call1 = msg; // 1st call
|
||||
const char *call2 = s1 + 1; // 2nd call
|
||||
const char* call1 = msg; // 1st call
|
||||
const char* call2 = s1 + 1; // 2nd call
|
||||
|
||||
int32_t n28a = pack28(call1);
|
||||
int32_t n28b = pack28(call2);
|
||||
|
@ -187,7 +183,7 @@ int pack77_1(const char *msg, uint8_t *b77)
|
|||
uint16_t igrid4;
|
||||
|
||||
// Locate the second delimiter
|
||||
const char *s2 = strchr(s1 + 1, ' ');
|
||||
const char* s2 = strchr(s1 + 1, ' ');
|
||||
if (s2 != 0)
|
||||
{
|
||||
igrid4 = packgrid(s2 + 1);
|
||||
|
@ -221,7 +217,7 @@ int pack77_1(const char *msg, uint8_t *b77)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void packtext77(const char *text, uint8_t *b77)
|
||||
void packtext77(const char* text, uint8_t* b77)
|
||||
{
|
||||
int length = strlen(text);
|
||||
|
||||
|
@ -285,7 +281,7 @@ void packtext77(const char *text, uint8_t *b77)
|
|||
b77[9] &= 0x00;
|
||||
}
|
||||
|
||||
int pack77(const char *msg, uint8_t *c77)
|
||||
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))
|
||||
|
@ -310,7 +306,7 @@ int pack77(const char *msg, uint8_t *c77)
|
|||
|
||||
bool test1()
|
||||
{
|
||||
const char *inputs[] = {
|
||||
const char* inputs[] = {
|
||||
"",
|
||||
" ",
|
||||
"ABC",
|
||||
|
@ -321,7 +317,8 @@ bool test1()
|
|||
"LL3JG",
|
||||
"LL3AJG",
|
||||
"CQ ",
|
||||
0};
|
||||
0
|
||||
};
|
||||
|
||||
for (int i = 0; inputs[i]; ++i)
|
||||
{
|
||||
|
@ -334,14 +331,15 @@ bool test1()
|
|||
|
||||
bool test2()
|
||||
{
|
||||
const char *inputs[] = {
|
||||
const char* inputs[] = {
|
||||
"CQ LL3JG",
|
||||
"CQ LL3JG KO26",
|
||||
"L0UAA LL3JG KO26",
|
||||
"L0UAA LL3JG +02",
|
||||
"L0UAA LL3JG RRR",
|
||||
"L0UAA LL3JG 73",
|
||||
0};
|
||||
0
|
||||
};
|
||||
|
||||
for (int i = 0; inputs[i]; ++i)
|
||||
{
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
// 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);
|
||||
int pack77(const char* msg, uint8_t* c77);
|
||||
|
||||
#endif // _INCLUDE_PACK_H_
|
||||
|
|
20
ft8/text.c
20
ft8/text.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
const char *trim_front(const char *str)
|
||||
const char* trim_front(const char* str)
|
||||
{
|
||||
// Skip leading whitespace
|
||||
while (*str == ' ')
|
||||
|
@ -12,7 +12,7 @@ const char *trim_front(const char *str)
|
|||
return str;
|
||||
}
|
||||
|
||||
void trim_back(char *str)
|
||||
void trim_back(char* str)
|
||||
{
|
||||
// Skip trailing whitespace by replacing it with '\0' characters
|
||||
int idx = strlen(str) - 1;
|
||||
|
@ -24,9 +24,9 @@ void trim_back(char *str)
|
|||
|
||||
// 1) trims a string from the back by changing whitespaces to '\0'
|
||||
// 2) trims a string from the front by skipping whitespaces
|
||||
char *trim(char *str)
|
||||
char* trim(char* str)
|
||||
{
|
||||
str = (char *)trim_front(str);
|
||||
str = (char*)trim_front(str);
|
||||
trim_back(str);
|
||||
// return a pointer to the first non-whitespace character
|
||||
return str;
|
||||
|
@ -57,17 +57,17 @@ bool in_range(char c, char min, char max)
|
|||
return (c >= min) && (c <= max);
|
||||
}
|
||||
|
||||
bool starts_with(const char *string, const char *prefix)
|
||||
bool starts_with(const char* string, const char* prefix)
|
||||
{
|
||||
return 0 == memcmp(string, prefix, strlen(prefix));
|
||||
}
|
||||
|
||||
bool equals(const char *string1, const char *string2)
|
||||
bool equals(const char* string1, const char* string2)
|
||||
{
|
||||
return 0 == strcmp(string1, string2);
|
||||
}
|
||||
|
||||
int char_index(const char *string, char c)
|
||||
int char_index(const char* string, char c)
|
||||
{
|
||||
for (int i = 0; *string; ++i, ++string)
|
||||
{
|
||||
|
@ -82,7 +82,7 @@ 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)
|
||||
void fmtmsg(char* msg_out, const char* msg_in)
|
||||
{
|
||||
char c;
|
||||
char last_out = 0;
|
||||
|
@ -100,7 +100,7 @@ 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)
|
||||
int dd_to_int(const char* str, int length)
|
||||
{
|
||||
int result = 0;
|
||||
bool negative;
|
||||
|
@ -131,7 +131,7 @@ 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)
|
||||
void int_to_dd(char* str, int value, int width, bool full_sign)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
|
|
18
ft8/text.h
18
ft8/text.h
|
@ -6,30 +6,30 @@
|
|||
|
||||
// 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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
void int_to_dd(char* str, int value, int width, bool full_sign);
|
||||
|
||||
char charn(int c, int table_idx);
|
||||
int nchar(char c, int table_idx);
|
||||
|
|
44
ft8/unpack.c
44
ft8/unpack.c
|
@ -1,18 +1,18 @@
|
|||
#ifdef __linux__
|
||||
#define _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include "unpack.h"
|
||||
#include "text.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define MAX22 ((uint32_t)4194304L)
|
||||
#define NTOKENS ((uint32_t)2063592L)
|
||||
#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.
|
||||
int unpack_callsign(uint32_t n28, uint8_t ip, uint8_t i3, char *result)
|
||||
int unpack_callsign(uint32_t n28, uint8_t ip, uint8_t i3, char* result)
|
||||
{
|
||||
// Check for special tokens DE, QRZ, CQ, CQ_nnn, CQ_aaaa
|
||||
if (n28 < NTOKENS)
|
||||
|
@ -108,7 +108,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)
|
||||
int unpack_type1(const uint8_t* a77, uint8_t i3, char* call_to, char* call_de, char* extra)
|
||||
{
|
||||
uint32_t n28a, n28b;
|
||||
uint16_t igrid4;
|
||||
|
@ -148,7 +148,7 @@ int unpack_type1(const uint8_t *a77, uint8_t i3, char *call_to, char *call_de, c
|
|||
// save_hash_call(call_de)
|
||||
// }
|
||||
|
||||
char *dst = extra;
|
||||
char* dst = extra;
|
||||
|
||||
if (igrid4 <= MAXGRID4)
|
||||
{
|
||||
|
@ -205,7 +205,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)
|
||||
int unpack_text(const uint8_t* a71, char* text)
|
||||
{
|
||||
// TODO: test
|
||||
uint8_t b71[9];
|
||||
|
@ -237,7 +237,7 @@ int unpack_text(const uint8_t *a71, char *text)
|
|||
return 0; // Success
|
||||
}
|
||||
|
||||
int unpack_telemetry(const uint8_t *a71, char *telemetry)
|
||||
int unpack_telemetry(const uint8_t* a71, char* telemetry)
|
||||
{
|
||||
uint8_t b71[9];
|
||||
|
||||
|
@ -266,21 +266,21 @@ int unpack_telemetry(const uint8_t *a71, char *telemetry)
|
|||
|
||||
//none standard for wsjt-x 2.0
|
||||
//by KD8CEC
|
||||
int unpack_nonstandard(const uint8_t *a77, char *call_to, char *call_de, char *extra)
|
||||
int unpack_nonstandard(const uint8_t* a77, char* call_to, char* call_de, char* extra)
|
||||
{
|
||||
uint32_t n12, iflip, nrpt, icq;
|
||||
uint64_t n58;
|
||||
n12 = (a77[0] << 4); //11 ~4 : 8
|
||||
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[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);
|
||||
|
@ -306,8 +306,8 @@ int unpack_nonstandard(const uint8_t *a77, char *call_to, char *call_de, char *e
|
|||
// call_3[5] = '>';
|
||||
// call_3[6] = '\0';
|
||||
|
||||
char *call_1 = (iflip) ? c11 : call_3;
|
||||
char *call_2 = (iflip) ? call_3 : c11;
|
||||
char* call_1 = (iflip) ? c11 : call_3;
|
||||
char* call_2 = (iflip) ? call_3 : c11;
|
||||
//save_hash_call(c11_trimmed);
|
||||
|
||||
if (icq == 0)
|
||||
|
@ -334,7 +334,7 @@ int unpack_nonstandard(const uint8_t *a77, char *call_to, char *call_de, char *e
|
|||
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)
|
||||
{
|
||||
call_to[0] = call_de[0] = extra[0] = '\0';
|
||||
|
||||
|
@ -390,7 +390,7 @@ 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)
|
||||
{
|
||||
char call_to[14];
|
||||
char call_de[14];
|
||||
|
@ -401,7 +401,7 @@ int unpack77(const uint8_t *a77, char *message)
|
|||
return rc;
|
||||
|
||||
// int msg_sz = strlen(call_to) + strlen(call_de) + strlen(extra) + 2;
|
||||
char *dst = message;
|
||||
char* dst = message;
|
||||
|
||||
dst[0] = '\0';
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
// 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);
|
||||
int unpack77_fields(const uint8_t* a77, char* field1, char* field2, char* field3);
|
||||
|
||||
// message should have at least 35 bytes allocated (34 characters + zero terminator)
|
||||
int unpack77(const uint8_t *a77, char *message);
|
||||
int unpack77(const uint8_t* a77, char* message);
|
||||
|
||||
#endif // _INCLUDE_UNPACK_H_
|
||||
|
|
28
gen_ft8.c
28
gen_ft8.c
|
@ -13,13 +13,13 @@
|
|||
|
||||
#define LOG_LEVEL LOG_INFO
|
||||
|
||||
#define FT8_SLOT_TIME 15.0f // total length of output waveform in seconds
|
||||
#define FT8_SLOT_TIME 15.0f // total length of output waveform in seconds
|
||||
#define FT8_SYMBOL_RATE 6.25f // tone deviation (and symbol rate) in Hz
|
||||
#define FT8_SYMBOL_BT 2.0f // symbol smoothing filter bandwidth factor (BT)
|
||||
#define FT8_SYMBOL_BT 2.0f // symbol smoothing filter bandwidth factor (BT)
|
||||
|
||||
#define FT4_SLOT_TIME 7.5f // total length of output waveform in seconds
|
||||
#define FT4_SLOT_TIME 7.5f // total length of output waveform in seconds
|
||||
#define FT4_SYMBOL_RATE 20.833333f // tone deviation (and symbol rate) in Hz
|
||||
#define FT4_SYMBOL_BT 1.0f // symbol smoothing filter bandwidth factor (BT)
|
||||
#define FT4_SYMBOL_BT 1.0f // symbol smoothing filter bandwidth factor (BT)
|
||||
|
||||
#define GFSK_CONST_K 5.336446f // pi * sqrt(2 / log(2))
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
|||
/// @param[in] b Shape parameter (values defined for FT8/FT4)
|
||||
/// @param[out] pulse Output array of pulse samples
|
||||
///
|
||||
void gfsk_pulse(int n_spsym, float symbol_bt, float *pulse)
|
||||
void gfsk_pulse(int n_spsym, float symbol_bt, float* pulse)
|
||||
{
|
||||
for (int i = 0; i < 3 * n_spsym; ++i)
|
||||
{
|
||||
|
@ -51,10 +51,10 @@ void gfsk_pulse(int n_spsym, float symbol_bt, float *pulse)
|
|||
/// @param[in] signal_rate Sample rate of synthesized signal, Hertz
|
||||
/// @param[out] signal Output array of signal waveform samples (should have space for n_sym*n_spsym samples)
|
||||
///
|
||||
void synth_gfsk(const uint8_t *symbols, int n_sym, float f0, float symbol_bt, float symbol_rate, int signal_rate, float *signal)
|
||||
void synth_gfsk(const uint8_t* symbols, int n_sym, float f0, float symbol_bt, float symbol_rate, int signal_rate, float* signal)
|
||||
{
|
||||
int n_spsym = (int)(0.5f + signal_rate / symbol_rate); // Samples per symbol
|
||||
int n_wave = n_sym * n_spsym; // Number of output samples
|
||||
int n_wave = n_sym * n_spsym; // Number of output samples
|
||||
float hmod = 1.0f;
|
||||
|
||||
LOG(LOG_DEBUG, "n_spsym = %d\n", n_spsym);
|
||||
|
@ -116,7 +116,7 @@ void usage()
|
|||
printf("(Note that you might have to enclose your message in quote marks if it contains spaces)\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Expect two command-line arguments
|
||||
if (argc < 3)
|
||||
|
@ -125,8 +125,8 @@ int main(int argc, char **argv)
|
|||
return -1;
|
||||
}
|
||||
|
||||
const char *message = argv[1];
|
||||
const char *wav_path = argv[2];
|
||||
const char* message = argv[1];
|
||||
const char* wav_path = argv[2];
|
||||
float frequency = 1000.0;
|
||||
if (argc > 3)
|
||||
{
|
||||
|
@ -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: ");
|
||||
|
@ -187,8 +187,8 @@ int main(int argc, char **argv)
|
|||
// Third, convert the FSK tones into an audio signal
|
||||
int sample_rate = 12000;
|
||||
int num_samples = (int)(0.5f + num_tones / symbol_rate * sample_rate); // Number of samples in the data signal
|
||||
int num_silence = (slot_time * sample_rate - num_samples) / 2; // Silence padding at both ends to make 15 seconds
|
||||
int num_total_samples = num_silence + num_samples + num_silence; // Number of samples in the padded signal
|
||||
int num_silence = (slot_time * sample_rate - num_samples) / 2; // Silence padding at both ends to make 15 seconds
|
||||
int num_total_samples = num_silence + num_samples + num_silence; // Number of samples in the padded signal
|
||||
float signal[num_total_samples];
|
||||
for (int i = 0; i < num_silence; i++)
|
||||
{
|
||||
|
|
10
test.c
10
test.c
|
@ -15,7 +15,7 @@
|
|||
|
||||
#define LOG_LEVEL LOG_INFO
|
||||
|
||||
void convert_8bit_to_6bit(uint8_t *dst, const uint8_t *src, int nBits)
|
||||
void convert_8bit_to_6bit(uint8_t* dst, const uint8_t* src, int nBits)
|
||||
{
|
||||
// Zero-fill the destination array as we will only be setting bits later
|
||||
for (int j = 0; j < (nBits + 5) / 6; ++j)
|
||||
|
@ -96,17 +96,17 @@ 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
|
||||
}
|
||||
*/
|
||||
|
||||
void test_tones(float *log174)
|
||||
void test_tones(float* log174)
|
||||
{
|
||||
// Just a test case
|
||||
for (int i = 0; i < FT8_ND; ++i)
|
||||
{
|
||||
const uint8_t inv_map[8] = {0, 1, 3, 2, 6, 4, 5, 7};
|
||||
const uint8_t inv_map[8] = { 0, 1, 3, 2, 6, 4, 5, 7 };
|
||||
uint8_t tone = ("0000000011721762454112705354533170166234757420515470163426"[i]) - '0';
|
||||
uint8_t b3 = inv_map[tone];
|
||||
log174[3 * i] = (b3 & 4) ? +1.0 : -1.0;
|
||||
|
@ -126,7 +126,7 @@ void test4()
|
|||
printf("N_FFT = %d\n", nfft);
|
||||
printf("FFT work area = %lu\n", fft_work_size);
|
||||
|
||||
void *fft_work = malloc(fft_work_size);
|
||||
void* fft_work = malloc(fft_work_size);
|
||||
kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(nfft, 0, fft_work, &fft_work_size);
|
||||
|
||||
kiss_fft_scalar window[nfft];
|
||||
|
|
Ładowanie…
Reference in New Issue