diff --git a/encode.h b/encode.h index 0d81773..3e6db5f 100644 --- a/encode.h +++ b/encode.h @@ -25,4 +25,7 @@ void genft8(const uint8_t *payload, uint8_t i3, uint8_t *itone); // * codeword - array of 174 bits stored as 22 bytes (MSB first) void encode174(const uint8_t *message, uint8_t *codeword); -uint16_t ft8_crc(uint8_t *message, int nBits); +// Compute 12-bit CRC for a sequence of given number of bits +// [IN] message - byte sequence (MSB first) +// [IN] num_bits - number of bits in the sequence +uint16_t ft8_crc(uint8_t *message, int num_bits); diff --git a/encode_91.cpp b/encode_91.cpp index 34a0b88..a23a226 100644 --- a/encode_91.cpp +++ b/encode_91.cpp @@ -1,5 +1,7 @@ #include "encode.h" +namespace ft8_v2 { + constexpr int N = 174, K = 91, M = N-K; // Define the LDPC sizes constexpr uint16_t POLYNOMIAL = 0x2757; // CRC-14 polynomial without the leading (MSB) 1 @@ -214,10 +216,9 @@ uint16_t ft8_crc(uint8_t *message, int num_bits) { // Generate FT8 tone sequence from payload data -// [IN] payload - 9 byte array consisting of 72 bit payload (MSB first) -// [IN] i3 - 3 bits containing message type (zero?) +// [IN] payload - 10 byte array consisting of 77 bit payload (MSB first) // [OUT] itone - array of NN (79) bytes to store the generated tones (encoded as 0..7) -void genft8(const uint8_t *payload, uint8_t i3, uint8_t *itone) { +void genft8(const uint8_t *payload, uint8_t *itone) { uint8_t a91[12]; // Store 77 bits of payload + 14 bits CRC // Copy 77 bits of payload data @@ -261,3 +262,5 @@ void genft8(const uint8_t *payload, uint8_t i3, uint8_t *itone) { ++k; } } + +}; // namespace diff --git a/encode_91.h b/encode_91.h new file mode 100644 index 0000000..a3e9794 --- /dev/null +++ b/encode_91.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace ft8_v2 { + + constexpr int ND = 58; // Data symbols + constexpr int NS = 21; // Sync symbols (3 @ Costas 7x7) + constexpr int NN = NS+ND; // Total channel symbols (79) + + + // Generate FT8 tone sequence from payload data + // [IN] payload - 9 byte array consisting of 72 bit payload + // [OUT] itone - array of NN (79) bytes to store the generated tones (encoded as 0..7) + void genft8(const uint8_t *payload, uint8_t *itone); + + + // Encode an 87-bit message and return a 174-bit codeword. + // The generator matrix has dimensions (87,87). + // 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 + // Arguments: + // * message - array of 87 bits stored as 11 bytes (MSB first) + // * codeword - array of 174 bits stored as 22 bytes (MSB first) + void encode174(const uint8_t *message, uint8_t *codeword); + + + // Compute 14-bit CRC for a sequence of given number of bits + // [IN] message - byte sequence (MSB first) + // [IN] num_bits - number of bits in the sequence + uint16_t ft8_crc(uint8_t *message, int num_bits); +}; diff --git a/pack_77.cpp b/pack_77.cpp index 0ddff29..3f751b2 100644 --- a/pack_77.cpp +++ b/pack_77.cpp @@ -3,6 +3,8 @@ #include "text.h" +namespace ft8_v2 { + // TODO: This is wasteful, should figure out something more elegant const char *A0 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"; const char *A1 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -33,11 +35,12 @@ int32_t pack28(const char *callsign) { if (starts_with(callsign, "CQ_")) { int nnum = 0, nlet = 0; + // TODO: // if(nnum.eq.3 .and. nlet.eq.0) then n28=3+nqsy // if(nlet.ge.1 .and. nlet.le.4 .and. nnum.eq.0) then n28=3+1000+m } - // Check for <...> callsign + // TODO: Check for <...> callsign // if(c13(1:1).eq.'<')then // call save_hash_call(c13,n10,n12,n22) !Save callsign in hash table // n28=NTOKENS + n22 @@ -88,6 +91,7 @@ int32_t pack28(const char *callsign) { //if (length > 13) return -1; + // TODO: // Treat this as a nonstandard callsign: compute its 22-bit hash // call save_hash_call(c13,n10,n12,n22) !Save callsign in hash table // n28=NTOKENS + n22 @@ -109,12 +113,15 @@ bool chkcall(const char *call, char *bc) { if (0 != strchr(call, '?')) return false; if (length > 6 && 0 != strchr(call, '/')) return false; + // TODO: implement suffix parsing (or rework?) //bc=w(1:6) //i0=index(w,'/') //if(max(i0-1,n1-i0).gt.6) go to 100 !Base call must be < 7 characters //if(i0.ge.2 .and. i0.le.n1-1) then !Extract base call from compound call // if(i0-1.le.n1-i0) bc=w(i0+1:n1)//' ' // if(i0-1.gt.n1-i0) bc=w(1:i0-1)//' ' + + return true; } @@ -171,6 +178,8 @@ int pack77_1(const char *msg, uint8_t *b77) { int32_t n28a = pack28(call1); int32_t n28b = pack28(call2); + + if (n28a < 0 || n28b < 0) return -1; uint16_t igrid4; @@ -184,9 +193,10 @@ int pack77_1(const char *msg, uint8_t *b77) { igrid4 = packgrid(0); } - uint8_t i3 = 1; + uint8_t i3 = 1; // No suffix or /R + + // TODO: check for suffixes // if(index(w(1),'/P').ge.4 .or. index(w(2),'/P').ge.4) i3=2 !Type 2, with "/P" - // if(index(w(1),'/P').ge.4 .or. index(w(1),'/R').ge.4) ipa=1 // if(index(w(2),'/P').ge.4 .or. index(w(2),'/R').ge.4) ipb=1 @@ -194,9 +204,9 @@ int pack77_1(const char *msg, uint8_t *b77) { n28a <<= 1; // ipa = 0 n28b <<= 1; // ipb = 0 + // Pack into (28 + 1) + (28 + 1) + (1 + 15) + 3 bits // write(c77,1000) n28a,ipa,n28b,ipb,ir,igrid4,i3 // 1000 format(2(b28.28,b1),b1,b15.15,b3.3) - // (28 + 1) + (28 + 1) + (1 + 15) + 3 b77[0] = (n28a >> 21); b77[1] = (n28a >> 13); @@ -242,11 +252,14 @@ void packtext77(const char *c13, uint8_t *b71) { } } + int pack77(const char *msg, uint8_t *c77) { // Check Type 1 (Standard 77-bit message) or Type 2, with optional "/P" //if (starts_with(msg, "CQ ")) { return pack77_1(msg, c77); //} + + // TODO: // Check 0.5 (telemetry) // i3=0 n3=5 write(c77,1006) ntel,n3,i3 1006 format(b23.23,2b24.24,2b3.3) @@ -257,4 +270,67 @@ int pack77(const char *msg, uint8_t *c77) { // i3=0 n3=0 // packtext77(msg(1:13),c77(1:71)) // write(c77(72:77),'(2b3.3)') n3,i3 -} \ No newline at end of file +} + +}; // namespace + +#ifdef UNIT_TEST + +#include + +using namespace std; + +bool test1() { + const char *inputs[] = { + "", + " ", + "ABC", + "A9", + "L9A", + "L7BC", + "L0ABC", + "LL3JG", + "LL3AJG", + "CQ ", + 0 + }; + + for (int i = 0; inputs[i]; ++i) { + int32_t result = ft8_v2::pack28(inputs[i]); + printf("pack28(\"%s\") = %d\n", inputs[i], result); + } + + return true; +} + +bool test2() { + const char *inputs[] = { + "CQ LL3JG", + "CQ LL3JG KO26", + "L0UAA LL3JG KO26", + "L0UAA LL3JG +02", + "L0UAA LL3JG RRR", + "L0UAA LL3JG 73", + 0 + }; + + for (int i = 0; inputs[i]; ++i) { + uint8_t result[10]; + int rc = ft8_v2::pack77_1(inputs[i], result); + printf("pack77_1(\"%s\") = %d\t[", inputs[i], rc); + for (int j = 0; j < 10; ++j) { + printf("%02x ", result[j]); + } + printf("]\n"); + } + + return true; +} + +int main() { + test1(); + test2(); + return 0; +} + +#endif \ No newline at end of file diff --git a/pack_77.h b/pack_77.h new file mode 100644 index 0000000..ba606b9 --- /dev/null +++ b/pack_77.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace ft8_v2 { + + // 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) + +}; \ No newline at end of file