Preparation for v2 decoding

This commit is contained in:
Karlis Goba 2018-11-26 16:47:45 +02:00
parent 0edb70c94b
commit 451798660c
3 changed files with 58 additions and 42 deletions

View file

@ -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) // 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). // 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) { void find_sync(const uint8_t *power, int num_blocks, int num_bins, const uint8_t *sync_map, int num_candidates, Candidate *heap) {
// Costas 7x7 tone pattern
const uint8_t ICOS7[] = { 2,5,6,0,4,1,3 };
int heap_size = 0; int heap_size = 0;
for (int alt = 0; alt < 4; ++alt) { 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 m = 0; m <= 72; m += 36) {
for (int k = 0; k < 7; ++k) { for (int k = 0; k < 7; ++k) {
int offset = ((time_offset + k + m) * 4 + alt) * num_bins + freq_offset; 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 + 0] - power[offset + 1] -
power[offset + 2] - power[offset + 3] - power[offset + 2] - power[offset + 3] -
power[offset + 4] - power[offset + 5] - 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 // Compute log likelihood log(p(1) / p(0)) of 174 message bits
// for later use in soft-decision LDPC decoding // 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 offset = (cand.time_offset * 4 + cand.time_sub * 2 + cand.freq_sub) * num_bins + cand.freq_offset;
int k = 0; 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 // Pointer to 8 bins of the current symbol
const uint8_t * ps = power + (offset + i * 4 * num_bins); 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) // Extract bit significance (and convert them to float)
// 8 FSK tones = 3 bits // 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 + 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(ps[2], ps[3], ps[6], ps[7]) - (int)max4(ps[0], ps[1], ps[4], ps[5]); 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(ps[1], ps[3], ps[5], ps[7]) - (int)max4(ps[0], ps[2], ps[4], ps[6]); 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", // 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], // 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]); // 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"); //printf("\n");
} }
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
// Expect one command-line argument // Expect one command-line argument
if (argc < 2) { if (argc < 2) {
@ -261,6 +264,13 @@ int main(int argc, char ** argv) {
return -1; 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 float fsk_dev = 6.25f;
const int num_bins = (int)(sample_rate / (2 * fsk_dev)); const int num_bins = (int)(sample_rate / (2 * fsk_dev));
@ -275,13 +285,13 @@ int main(int argc, char ** argv) {
int num_candidates = 250; int num_candidates = 250;
Candidate heap[num_candidates]; 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) { for (int idx = 0; idx < num_candidates; ++idx) {
Candidate &cand = heap[idx]; Candidate &cand = heap[idx];
float log174[3 * ND]; 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; const int num_iters = 20;
int plain[3 * ND]; int plain[3 * ND];

View file

@ -1,11 +1,16 @@
#include "encode.h" #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 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) // 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 }, { 0x23, 0xbb, 0xa8, 0x30, 0xe2, 0x3b, 0x6b, 0x6f, 0x50, 0x98, 0x2e },
{ 0x1f, 0x8e, 0x55, 0xda, 0x21, 0x8c, 0x5d, 0xf3, 0x30, 0x90, 0x52 }, { 0x1f, 0x8e, 0x55, 0xda, 0x21, 0x8c, 0x5d, 0xf3, 0x30, 0x90, 0x52 },
{ 0xca, 0x7b, 0x32, 0x17, 0xcd, 0x92, 0xbd, 0x59, 0xa5, 0xae, 0x20 }, { 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 // 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, 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, 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, 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 // 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 // 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 is a (174,87) regular ldpc code with column weight 3.
// The code was generated using the PEG algorithm. // The code was generated using the PEG algorithm.
// After creating the codeword, the columns are re-ordered according to // 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: // Arguments:
// [IN] message - array of 87 bits stored as 11 bytes (MSB first) // [IN] message - array of 87 bits stored as 11 bytes (MSB first)
// [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first) // [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first)
void encode174(const uint8_t *message, uint8_t *codeword) { void encode174(const uint8_t *message, uint8_t *codeword) {
// Here we don't generate the generator bit matrix as in WSJT-X implementation // 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 // Also we don't use the itmp temporary buffer, instead filling codeword bit by bit
// in the reordered order as we compute the result. // in the reordered order as we compute the result.
@ -140,7 +145,7 @@ void encode174(const uint8_t *message, uint8_t *codeword) {
// For reference: // For reference:
// itmp(1:M)=pchecks // itmp(1:M)=pchecks
// itmp(M+1:N)=message(1:K) // 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 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 // 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 for (int i = 0; i < M; ++i) { // do i=1,M
// Fast implementation of bitwise multiplication and parity checking // 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. // but we only compute the sum modulo 2.
uint8_t nsum = 0; uint8_t nsum = 0;
for (int j = 0; j < 11; ++j) { 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) nsum ^= parity8(bits); // bitwise XOR (addition modulo 2)
} }
// Check if we need to set a bit in codeword // Check if we need to set a bit in codeword
if (nsum % 2) { // pchecks(i)=mod(nsum,2) 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)); codeword[col/8] |= (1 << (7 - col%8));
} }
++colidx; ++colidx;
@ -172,7 +177,7 @@ void encode174(const uint8_t *message, uint8_t *codeword) {
for (int j = 0; j < K; ++j) { for (int j = 0; j < K; ++j) {
// Copy the j-th bit from message to codeword // Copy the j-th bit from message to codeword
if (message[j/8] & mask) { 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)); codeword[col/8] |= (1 << (7 - col%8));
} }
++colidx; ++colidx;
@ -242,9 +247,9 @@ void genft8(const uint8_t *payload, uint8_t i3, uint8_t *itone) {
// Message structure: S7 D29 S7 D29 S7 // Message structure: S7 D29 S7 D29 S7
for (int i = 0; i < 7; ++i) { for (int i = 0; i < 7; ++i) {
itone[i] = ICOS7[i]; itone[i] = kCostas_map[i];
itone[36 + i] = ICOS7[i]; itone[36 + i] = kCostas_map[i];
itone[72 + i] = ICOS7[i]; itone[72 + i] = kCostas_map[i];
} }
int k = 7; // Skip over the first set of Costas symbols int k = 7; // Skip over the first set of Costas symbols

View file

@ -4,14 +4,17 @@
namespace ft8_v2 { 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 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) // 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 }, { 0x83, 0x29, 0xce, 0x11, 0xbf, 0x31, 0xea, 0xf5, 0x09, 0xf2, 0x7f, 0xc0 },
{ 0x76, 0x1c, 0x26, 0x4e, 0x25, 0xc2, 0x59, 0x33, 0x54, 0x93, 0x13, 0x20 }, { 0x76, 0x1c, 0x26, 0x4e, 0x25, 0xc2, 0x59, 0x33, 0x54, 0x93, 0x13, 0x20 },
{ 0xdc, 0x26, 0x59, 0x02, 0xfb, 0x27, 0x7c, 0x64, 0x10, 0xa1, 0xbd, 0xc0 }, { 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 // 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, 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, 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, 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 // 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 // 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 // Returns 1 if an odd number of bits are set in x, zero otherwise
uint8_t parity8(uint8_t x) { 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) // [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first)
void encode174(const uint8_t *message, uint8_t *codeword) { void encode174(const uint8_t *message, uint8_t *codeword) {
// Here we don't generate the generator bit matrix as in WSJT-X implementation // 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: // For reference:
// codeword(1:K)=message // 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 // 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 for (int i = 0; i < M; ++i) { // do i=1,M
// Fast implementation of bitwise multiplication and parity checking // 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. // but we only compute the sum modulo 2.
uint8_t nsum = 0; uint8_t nsum = 0;
for (int j = 0; j < K_BYTES; ++j) { 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) nsum ^= parity8(bits); // bitwise XOR (addition modulo 2)
} }
// Check if we need to set a bit in codeword // 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 // Calculate CRC of 12 bytes = 96 bits, see WSJT-X code
uint16_t checksum = ft8_crc(a91, 96 - 14); 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 // Store the CRC at the end of 77 bit message
a91[9] |= (uint8_t)(checksum >> 11); 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 // Message structure: S7 D29 S7 D29 S7
for (int i = 0; i < 7; ++i) { for (int i = 0; i < 7; ++i) {
itone[i] = ICOS7[i]; itone[i] = kCostas_map[i];
itone[36 + i] = ICOS7[i]; itone[36 + i] = kCostas_map[i];
itone[72 + i] = ICOS7[i]; itone[72 + i] = kCostas_map[i];
} }
int k = 7; // Skip over the first set of Costas symbols 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 (codeword[i_byte] & mask) bits3 |= 1;
if (0 == (mask >>= 1)) { mask = 0x80; i_byte++; } if (0 == (mask >>= 1)) { mask = 0x80; i_byte++; }
itone[k] = GRAY[bits3]; itone[k] = kGray_map[bits3];
++k; ++k;
} }
} }
}; // namespace }; // ft8_v2