kopia lustrzana https://github.com/kgoba/ft8_lib
Preparation for v2 decoding
rodzic
0edb70c94b
commit
451798660c
|
@ -79,10 +79,7 @@ void heapify_up(Candidate * heap, int heap_size) {
|
|||
|
||||
// Find 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).
|
||||
void find_sync(const uint8_t * power, int num_blocks, int num_bins, int num_candidates, Candidate * heap) {
|
||||
// Costas 7x7 tone pattern
|
||||
const uint8_t ICOS7[] = { 2,5,6,0,4,1,3 };
|
||||
|
||||
void find_sync(const uint8_t *power, int num_blocks, int num_bins, const uint8_t *sync_map, int num_candidates, Candidate *heap) {
|
||||
int heap_size = 0;
|
||||
|
||||
for (int alt = 0; alt < 4; ++alt) {
|
||||
|
@ -94,7 +91,7 @@ void find_sync(const uint8_t * power, int num_blocks, int num_bins, int num_cand
|
|||
for (int m = 0; m <= 72; m += 36) {
|
||||
for (int k = 0; k < 7; ++k) {
|
||||
int offset = ((time_offset + k + m) * 4 + alt) * num_bins + freq_offset;
|
||||
score += 8 * (int)power[offset + ICOS7[k]] -
|
||||
score += 8 * (int)power[offset + sync_map[k]] -
|
||||
power[offset + 0] - power[offset + 1] -
|
||||
power[offset + 2] - power[offset + 3] -
|
||||
power[offset + 4] - power[offset + 5] -
|
||||
|
@ -201,7 +198,7 @@ uint8_t max4(uint8_t a, uint8_t b, uint8_t cand, uint8_t d) {
|
|||
|
||||
// Compute log likelihood log(p(1) / p(0)) of 174 message bits
|
||||
// for later use in soft-decision LDPC decoding
|
||||
void extract_likelihood(const uint8_t * power, int num_bins, const Candidate & cand, float * log174) {
|
||||
void extract_likelihood(const uint8_t *power, int num_bins, const Candidate & cand, const uint8_t *code_map, float *log174) {
|
||||
int offset = (cand.time_offset * 4 + cand.time_sub * 2 + cand.freq_sub) * num_bins + cand.freq_offset;
|
||||
|
||||
int k = 0;
|
||||
|
@ -211,12 +208,17 @@ void extract_likelihood(const uint8_t * power, int num_bins, const Candidate & c
|
|||
|
||||
// Pointer to 8 bins of the current symbol
|
||||
const uint8_t * ps = power + (offset + i * 4 * num_bins);
|
||||
uint8_t s2[8];
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
s2[i] = ps[code_map[i]];
|
||||
}
|
||||
|
||||
// Extract bit significance (and convert them to float)
|
||||
// 8 FSK tones = 3 bits
|
||||
log174[k + 0] = (int)max4(ps[4], ps[5], ps[6], ps[7]) - (int)max4(ps[0], ps[1], ps[2], ps[3]);
|
||||
log174[k + 1] = (int)max4(ps[2], ps[3], ps[6], ps[7]) - (int)max4(ps[0], ps[1], ps[4], ps[5]);
|
||||
log174[k + 2] = (int)max4(ps[1], ps[3], ps[5], ps[7]) - (int)max4(ps[0], ps[2], ps[4], ps[6]);
|
||||
log174[k + 0] = (int)max4(s2[4], s2[5], s2[6], s2[7]) - (int)max4(s2[0], s2[1], s2[2], s2[3]);
|
||||
log174[k + 1] = (int)max4(s2[2], s2[3], s2[6], s2[7]) - (int)max4(s2[0], s2[1], s2[4], s2[5]);
|
||||
log174[k + 2] = (int)max4(s2[1], s2[3], s2[5], s2[7]) - (int)max4(s2[0], s2[2], s2[4], s2[6]);
|
||||
// printf("%d %d %d %d %d %d %d %d : %.0f %.0f %.0f\n",
|
||||
// ps[0], ps[1], ps[2], ps[3], ps[4], ps[5], ps[6], ps[7],
|
||||
// log174[k + 0], log174[k + 1], log174[k + 2]);
|
||||
|
@ -243,6 +245,7 @@ void extract_likelihood(const uint8_t * power, int num_bins, const Candidate & c
|
|||
//printf("\n");
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
// Expect one command-line argument
|
||||
if (argc < 2) {
|
||||
|
@ -261,6 +264,13 @@ int main(int argc, char ** argv) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Costas 7x7 tone pattern
|
||||
const uint8_t kCostas_map_v1[] = { 2,5,6,0,4,1,3 };
|
||||
const uint8_t kCostas_map_v2[] = { 3,1,4,0,6,5,2 };
|
||||
// Gray maps (used only in v2)
|
||||
const uint8_t kGray_map_v1[8] = { 0,1,2,3,4,5,6,7 }; // identity map
|
||||
const uint8_t kGray_map_v2[8] = { 0,1,3,2,5,6,4,7 };
|
||||
|
||||
const float fsk_dev = 6.25f;
|
||||
|
||||
const int num_bins = (int)(sample_rate / (2 * fsk_dev));
|
||||
|
@ -275,13 +285,13 @@ int main(int argc, char ** argv) {
|
|||
int num_candidates = 250;
|
||||
Candidate heap[num_candidates];
|
||||
|
||||
find_sync(power, num_blocks, num_bins, num_candidates, heap);
|
||||
find_sync(power, num_blocks, num_bins, kCostas_map_v1, num_candidates, heap);
|
||||
|
||||
for (int idx = 0; idx < num_candidates; ++idx) {
|
||||
Candidate &cand = heap[idx];
|
||||
|
||||
float log174[3 * ND];
|
||||
extract_likelihood(power, num_bins, cand, log174);
|
||||
extract_likelihood(power, num_bins, cand, kGray_map_v1, log174);
|
||||
|
||||
const int num_iters = 20;
|
||||
int plain[3 * ND];
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
#include "encode.h"
|
||||
|
||||
constexpr int N = 174, K = 87, M = N-K; // Define the LDPC sizes
|
||||
// Define the LDPC sizes
|
||||
constexpr int N = 174; // Number of bits in the encoded message
|
||||
constexpr int K = 87; // Number of payload bits
|
||||
constexpr int M = N - K; // Number of checksum bits
|
||||
|
||||
constexpr uint16_t POLYNOMIAL = 0xC06; // CRC-12 polynomial without the leading (MSB) 1
|
||||
|
||||
constexpr int K_BYTES = (K + 7) / 8; // Number of whole bytes needed to store K bits
|
||||
|
||||
// Parity generator matrix for (174,87) LDPC code, stored in bitpacked format (MSB first)
|
||||
const uint8_t G[87][11] = {
|
||||
const uint8_t kGenerator[M][K_BYTES] = {
|
||||
{ 0x23, 0xbb, 0xa8, 0x30, 0xe2, 0x3b, 0x6b, 0x6f, 0x50, 0x98, 0x2e },
|
||||
{ 0x1f, 0x8e, 0x55, 0xda, 0x21, 0x8c, 0x5d, 0xf3, 0x30, 0x90, 0x52 },
|
||||
{ 0xca, 0x7b, 0x32, 0x17, 0xcd, 0x92, 0xbd, 0x59, 0xa5, 0xae, 0x20 },
|
||||
|
@ -96,7 +101,7 @@ const uint8_t G[87][11] = {
|
|||
};
|
||||
|
||||
// Column order (permutation) in which the bits in codeword are stored
|
||||
const uint8_t colorder[174] = {
|
||||
const uint8_t kColumn_order[174] = {
|
||||
0, 1, 2, 3, 30, 4, 5, 6, 7, 8, 9, 10, 11, 32, 12, 40, 13, 14, 15, 16,
|
||||
17, 18, 37, 45, 29, 19, 20, 21, 41, 22, 42, 31, 33, 34, 44, 35, 47, 51, 50, 43,
|
||||
36, 52, 63, 46, 25, 55, 27, 24, 23, 53, 39, 49, 59, 38, 48, 61, 60, 57, 28, 62,
|
||||
|
@ -109,7 +114,7 @@ const uint8_t colorder[174] = {
|
|||
};
|
||||
|
||||
// Costas 7x7 tone pattern
|
||||
const uint8_t ICOS7[] = { 2,5,6,0,4,1,3 };
|
||||
const uint8_t kCostas_map[] = { 2,5,6,0,4,1,3 };
|
||||
|
||||
|
||||
// Returns 1 if an odd number of bits are set in x, zero otherwise
|
||||
|
@ -126,13 +131,13 @@ uint8_t parity8(uint8_t x) {
|
|||
// The code is a (174,87) regular ldpc code with column weight 3.
|
||||
// The code was generated using the PEG algorithm.
|
||||
// After creating the codeword, the columns are re-ordered according to
|
||||
// "colorder" to make the codeword compatible with the parity-check matrix
|
||||
// "kColumn_order" to make the codeword compatible with the parity-check matrix
|
||||
// Arguments:
|
||||
// [IN] message - array of 87 bits stored as 11 bytes (MSB first)
|
||||
// [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first)
|
||||
void encode174(const uint8_t *message, uint8_t *codeword) {
|
||||
// Here we don't generate the generator bit matrix as in WSJT-X implementation
|
||||
// Instead we access the generator bits straight from the binary representation in G
|
||||
// Instead we access the generator bits straight from the binary representation in kGenerator
|
||||
|
||||
// Also we don't use the itmp temporary buffer, instead filling codeword bit by bit
|
||||
// in the reordered order as we compute the result.
|
||||
|
@ -140,7 +145,7 @@ void encode174(const uint8_t *message, uint8_t *codeword) {
|
|||
// For reference:
|
||||
// itmp(1:M)=pchecks
|
||||
// itmp(M+1:N)=message(1:K)
|
||||
// codeword(colorder+1)=itmp(1:N)
|
||||
// codeword(kColumn_order+1)=itmp(1:N)
|
||||
|
||||
int colidx = 0; // track the current column in codeword
|
||||
|
||||
|
@ -152,16 +157,16 @@ void encode174(const uint8_t *message, uint8_t *codeword) {
|
|||
// Compute the first part of itmp (1:M) and store the result in codeword
|
||||
for (int i = 0; i < M; ++i) { // do i=1,M
|
||||
// Fast implementation of bitwise multiplication and parity checking
|
||||
// Normally nsum would contain the result of dot product between message and G[i],
|
||||
// Normally nsum would contain the result of dot product between message and kGenerator[i],
|
||||
// but we only compute the sum modulo 2.
|
||||
uint8_t nsum = 0;
|
||||
for (int j = 0; j < 11; ++j) {
|
||||
uint8_t bits = message[j] & G[i][j]; // bitwise AND (bitwise multiplication)
|
||||
for (int j = 0; j < K_BYTES; ++j) {
|
||||
uint8_t bits = message[j] & kGenerator[i][j]; // bitwise AND (bitwise multiplication)
|
||||
nsum ^= parity8(bits); // bitwise XOR (addition modulo 2)
|
||||
}
|
||||
// Check if we need to set a bit in codeword
|
||||
if (nsum % 2) { // pchecks(i)=mod(nsum,2)
|
||||
uint8_t col = colorder[colidx]; // Index of the bit to set
|
||||
uint8_t col = kColumn_order[colidx]; // Index of the bit to set
|
||||
codeword[col/8] |= (1 << (7 - col%8));
|
||||
}
|
||||
++colidx;
|
||||
|
@ -172,7 +177,7 @@ void encode174(const uint8_t *message, uint8_t *codeword) {
|
|||
for (int j = 0; j < K; ++j) {
|
||||
// Copy the j-th bit from message to codeword
|
||||
if (message[j/8] & mask) {
|
||||
uint8_t col = colorder[colidx]; // Index of the bit to set
|
||||
uint8_t col = kColumn_order[colidx]; // Index of the bit to set
|
||||
codeword[col/8] |= (1 << (7 - col%8));
|
||||
}
|
||||
++colidx;
|
||||
|
@ -242,9 +247,9 @@ void genft8(const uint8_t *payload, uint8_t i3, uint8_t *itone) {
|
|||
|
||||
// Message structure: S7 D29 S7 D29 S7
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
itone[i] = ICOS7[i];
|
||||
itone[36 + i] = ICOS7[i];
|
||||
itone[72 + i] = ICOS7[i];
|
||||
itone[i] = kCostas_map[i];
|
||||
itone[36 + i] = kCostas_map[i];
|
||||
itone[72 + i] = kCostas_map[i];
|
||||
}
|
||||
|
||||
int k = 7; // Skip over the first set of Costas symbols
|
||||
|
|
|
@ -4,14 +4,17 @@
|
|||
|
||||
namespace ft8_v2 {
|
||||
|
||||
constexpr int N = 174, K = 91, M = N-K; // Define the LDPC sizes
|
||||
// Define the LDPC sizes
|
||||
constexpr int N = 174; // Number of bits in the encoded message
|
||||
constexpr int K = 91; // Number of payload bits
|
||||
constexpr int M = N - K; // Number of checksum bits
|
||||
|
||||
constexpr uint16_t POLYNOMIAL = 0x2757; // CRC-14 polynomial without the leading (MSB) 1
|
||||
|
||||
constexpr int K_BYTES = (K + 7) / 8;
|
||||
constexpr int K_BYTES = (K + 7) / 8; // Number of whole bytes needed to store K bits
|
||||
|
||||
// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first)
|
||||
const uint8_t G[M][K_BYTES] = {
|
||||
const uint8_t kGenerator[M][K_BYTES] = {
|
||||
{ 0x83, 0x29, 0xce, 0x11, 0xbf, 0x31, 0xea, 0xf5, 0x09, 0xf2, 0x7f, 0xc0 },
|
||||
{ 0x76, 0x1c, 0x26, 0x4e, 0x25, 0xc2, 0x59, 0x33, 0x54, 0x93, 0x13, 0x20 },
|
||||
{ 0xdc, 0x26, 0x59, 0x02, 0xfb, 0x27, 0x7c, 0x64, 0x10, 0xa1, 0xbd, 0xc0 },
|
||||
|
@ -98,7 +101,7 @@ const uint8_t G[M][K_BYTES] = {
|
|||
};
|
||||
|
||||
// Column order (permutation) in which the bits in codeword are stored
|
||||
const uint8_t colorder[N] = {
|
||||
const uint8_t kColumn_order[N] = {
|
||||
0, 1, 2, 3, 28, 4, 5, 6, 7, 8, 9, 10, 11, 34, 12, 32, 13, 14, 15, 16,
|
||||
17, 18, 36, 29, 43, 19, 20, 42, 21, 40, 30, 37, 22, 47, 61, 45, 44, 23, 41, 39,
|
||||
49, 24, 46, 50, 48, 26, 31, 33, 51, 38, 52, 59, 55, 66, 57, 27, 60, 35, 54, 58,
|
||||
|
@ -111,10 +114,10 @@ const uint8_t colorder[N] = {
|
|||
};
|
||||
|
||||
// Costas 7x7 tone pattern
|
||||
const uint8_t ICOS7[] = { 3,1,4,0,6,5,2 };
|
||||
const uint8_t kCostas_map[7] = { 3,1,4,0,6,5,2 };
|
||||
|
||||
// Gray code map
|
||||
const uint8_t GRAY[8] = { 0,1,3,2,5,6,4,7 };
|
||||
const uint8_t kGray_map[8] = { 0,1,3,2,5,6,4,7 };
|
||||
|
||||
// Returns 1 if an odd number of bits are set in x, zero otherwise
|
||||
uint8_t parity8(uint8_t x) {
|
||||
|
@ -134,7 +137,7 @@ uint8_t parity8(uint8_t x) {
|
|||
// [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first)
|
||||
void encode174(const uint8_t *message, uint8_t *codeword) {
|
||||
// Here we don't generate the generator bit matrix as in WSJT-X implementation
|
||||
// Instead we access the generator bits straight from the binary representation in G
|
||||
// Instead we access the generator bits straight from the binary representation in kGenerator
|
||||
|
||||
// For reference:
|
||||
// codeword(1:K)=message
|
||||
|
@ -157,11 +160,11 @@ void encode174(const uint8_t *message, uint8_t *codeword) {
|
|||
// Compute the first part of itmp (1:M) and store the result in codeword
|
||||
for (int i = 0; i < M; ++i) { // do i=1,M
|
||||
// Fast implementation of bitwise multiplication and parity checking
|
||||
// Normally nsum would contain the result of dot product between message and G[i],
|
||||
// Normally nsum would contain the result of dot product between message and kGenerator[i],
|
||||
// but we only compute the sum modulo 2.
|
||||
uint8_t nsum = 0;
|
||||
for (int j = 0; j < K_BYTES; ++j) {
|
||||
uint8_t bits = message[j] & G[i][j]; // bitwise AND (bitwise multiplication)
|
||||
uint8_t bits = message[j] & kGenerator[i][j]; // bitwise AND (bitwise multiplication)
|
||||
nsum ^= parity8(bits); // bitwise XOR (addition modulo 2)
|
||||
}
|
||||
// Check if we need to set a bit in codeword
|
||||
|
@ -239,8 +242,6 @@ void genft8(const uint8_t *payload, uint8_t *itone) {
|
|||
|
||||
// Calculate CRC of 12 bytes = 96 bits, see WSJT-X code
|
||||
uint16_t checksum = ft8_crc(a91, 96 - 14);
|
||||
// 3dcf = 0011 1101 1100 1111
|
||||
// 111 1011 1001 111
|
||||
|
||||
// Store the CRC at the end of 77 bit message
|
||||
a91[9] |= (uint8_t)(checksum >> 11);
|
||||
|
@ -253,9 +254,9 @@ void genft8(const uint8_t *payload, uint8_t *itone) {
|
|||
|
||||
// Message structure: S7 D29 S7 D29 S7
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
itone[i] = ICOS7[i];
|
||||
itone[36 + i] = ICOS7[i];
|
||||
itone[72 + i] = ICOS7[i];
|
||||
itone[i] = kCostas_map[i];
|
||||
itone[36 + i] = kCostas_map[i];
|
||||
itone[72 + i] = kCostas_map[i];
|
||||
}
|
||||
|
||||
int k = 7; // Skip over the first set of Costas symbols
|
||||
|
@ -277,9 +278,9 @@ void genft8(const uint8_t *payload, uint8_t *itone) {
|
|||
if (codeword[i_byte] & mask) bits3 |= 1;
|
||||
if (0 == (mask >>= 1)) { mask = 0x80; i_byte++; }
|
||||
|
||||
itone[k] = GRAY[bits3];
|
||||
itone[k] = kGray_map[bits3];
|
||||
++k;
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
}; // ft8_v2
|
||||
|
|
Ładowanie…
Reference in New Issue