Extra comments and minor updates

This commit is contained in:
Karlis Goba 2018-10-18 11:16:21 +03:00
parent 5aa5bd077c
commit 2da7757586
4 changed files with 77 additions and 50 deletions

View file

@ -1,5 +1,5 @@
CXXFLAGS = -std=c++14 CXXFLAGS = -std=c++14
LDFLAGS = -lm LDFLAGS = -lm
test: test.o encode.o pack.o gen_ft8: gen_ft8.o encode.o pack.o
$(CXX) $(LDFLAGS) -o $@ $^ $(CXX) $(LDFLAGS) -o $@ $^

View file

@ -1,11 +1,11 @@
#include "encode.h" #include "encode.h"
constexpr int N = 174, K = 87, M = N-K; constexpr int N = 174, K = 87, M = N-K; // Define the LDPC sizes
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
// Parity generator matrix for (174,87) code, stored in bitpacked format (MSB first) // Parity generator matrix for (174,87) LDPC code, stored in bitpacked format (MSB first)
constexpr uint8_t G[87][11] = { const uint8_t G[87][11] = {
{ 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 },
@ -128,11 +128,9 @@ uint8_t parity8(uint8_t x) {
// 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 // "colorder" to make the codeword compatible with the parity-check matrix
// Arguments: // Arguments:
// * message - array of 87 bits stored as 11 bytes (MSB first) // [IN] message - array of 87 bits stored as 11 bytes (MSB first)
// * 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) {
static bool first = true;
// 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 G
@ -186,18 +184,23 @@ 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
const int WIDTH = 12; // [IN] message - byte sequence (MSB first)
const uint16_t TOPBIT = (1 << (WIDTH - 1)); // [IN] num_bits - number of bits in the sequence
uint16_t ft8_crc(uint8_t *message, int num_bits) {
// Adapted from https://barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code
constexpr int WIDTH = 12;
constexpr uint16_t TOPBIT = (1 << (WIDTH - 1));
uint16_t remainder = 0; uint16_t remainder = 0;
int iByte = 0; int idx_byte = 0;
// Perform modulo-2 division, a bit at a time. // Perform modulo-2 division, a bit at a time.
for (int iBit = 0; iBit < nBits; ++iBit) { for (int idx_bit = 0; idx_bit < num_bits; ++idx_bit) {
if (iBit % 8 == 0) { if (idx_bit % 8 == 0) {
// Bring the next byte into the remainder. // Bring the next byte into the remainder.
remainder ^= (message[iByte] << (WIDTH - 8)); remainder ^= (message[idx_byte] << (WIDTH - 8));
++iByte; ++idx_byte;
} }
// Try to divide the current data bit. // Try to divide the current data bit.
@ -213,7 +216,7 @@ uint16_t ft8_crc(uint8_t *message, int nBits) {
// Generate FT8 tone sequence from payload data // Generate FT8 tone sequence from payload data
// [IN] payload - 9 byte array consisting of 72 bit payload // [IN] payload - 9 byte array consisting of 72 bit payload (MSB first)
// [IN] i3 - 3 bits containing message type (zero?) // [IN] i3 - 3 bits containing message type (zero?)
// [OUT] itone - array of NN (79) bytes to store the generated tones (encoded as 0..7) // [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 i3, uint8_t *itone) {

View file

@ -148,8 +148,22 @@ void test3() {
} }
void usage() {
printf("Generate a 15-second WAV file encoding a given message.\n");
printf("Usage:\n");
printf("\n");
printf("gen_ft8 MESSAGE WAV_FILE\n");
printf("\n");
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) {
if (argc < 3) return -1; // Expect two command-line arguments
if (argc < 3) {
usage();
return -1;
}
//const char *message = "G0UPL YL3JG 73"; //const char *message = "G0UPL YL3JG 73";
const char *message = argv[1]; const char *message = argv[1];

View file

@ -3,9 +3,13 @@
#include "pack.h" #include "pack.h"
constexpr int NBASE = 37*36*10*27*27*27; constexpr int NBASE = 37*36*10*27*27*27;
constexpr int NGBASE = 180*180; constexpr int NGBASE = 180*180;
// Utility functions for characters and strings
char to_upper(char c) { char to_upper(char c) {
return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c; return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c;
} }
@ -31,7 +35,7 @@ bool equals(const char *string1, const char *string2) {
} }
// Message formatting: // Text message formatting:
// - replaces lowercase letters with uppercase // - replaces lowercase letters with uppercase
// - merges consecutive spaces into single space // - merges consecutive spaces into single space
void fmtmsg(char *msg_out, const char *msg_in) { void fmtmsg(char *msg_out, const char *msg_in) {
@ -50,9 +54,9 @@ void fmtmsg(char *msg_out, const char *msg_in) {
// Returns integer encoding of a character (number/digit/space). // Returns integer encoding of a character (number/digit/space).
// * Digits are encoded as 0..9 // - Digits are encoded as 0..9
// * Letters a..z are encoded as 10..35 (case insensitive) // - Letters a..z are encoded as 10..35 (case insensitive)
// * Space is encoded as 36 // - Space is encoded as 36
uint8_t nchar(char c) { uint8_t nchar(char c) {
if (is_digit(c)) { if (is_digit(c)) {
return (c - '0'); return (c - '0');
@ -87,7 +91,7 @@ void pack3_8bit(uint32_t nc1, uint32_t nc2, uint16_t ng, uint8_t *payload) {
// Pack FT8 source/destination and grid data into 72 bits (stored as 12 bytes of 6-bit values) // Pack FT8 source/destination and grid data into 72 bits (stored as 12 bytes of 6-bit values)
// (For compatibility with WSJT-X and testing) // (Unused here, included for compatibility with WSJT-X and testing)
// [IN] nc1 - first callsign data (28 bits) // [IN] nc1 - first callsign data (28 bits)
// [IN] nc2 - second callsign data (28 bits) // [IN] nc2 - second callsign data (28 bits)
// [IN] ng - grid data (16 bits) // [IN] ng - grid data (16 bits)
@ -115,11 +119,11 @@ int dd_to_int(const char *str, int length) {
int i; int i;
if (str[0] == '-') { if (str[0] == '-') {
negative = true; negative = true;
i = 1; i = 1; // Consume the - sign
} }
else { else {
negative = false; negative = false;
i = (str[0] == '+') ? 1 : 0; i = (str[0] == '+') ? 1 : 0; // Consume a + sign if found
} }
while (i < length) { while (i < length) {
@ -134,15 +138,16 @@ 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) { void int_to_dd(char *str, int value, int width) {
if (value < 0) { if (value < 0) {
*str = '-'; *str = '-';
str++; ++str;
value = -value; value = -value;
} }
int divisor = 1; int divisor = 1;
for (int i = 0; i < width; i++) { for (int i = 0; i < width; ++i) {
divisor *= 10; divisor *= 10;
} }
@ -150,18 +155,18 @@ void int_to_dd(char *str, int value, int width) {
int digit = value / divisor; int digit = value / divisor;
*str = '0' + digit; *str = '0' + digit;
str++; ++str;
value -= digit * divisor; value -= digit * divisor;
divisor /= 10; divisor /= 10;
} }
*str = 0; *str = 0; // Add zero terminator
} }
// Pack a valid callsign into a 28-bit integer. // Pack a valid callsign into a 28-bit integer.
int32_t packcall(const char *callsign) { int32_t packcall(const char *callsign) {
printf("Callsign = [%s]\n", callsign); //LOG("Callsign = [%s]\n", callsign);
if (equals(callsign, "CQ")) { if (equals(callsign, "CQ")) {
// TODO: support 'CQ nnn' frequency specification // TODO: support 'CQ nnn' frequency specification
//if (callsign(4:4).ge.'0' .and. callsign(4:4).le.'9' .and. & //if (callsign(4:4).ge.'0' .and. callsign(4:4).le.'9' .and. &
@ -241,13 +246,14 @@ int32_t packcall(const char *callsign) {
// Pack a valid grid locator into an integer. // Pack a valid grid locator into an integer.
int16_t packgrid(const char *grid) { int16_t packgrid(const char *grid) {
printf("Grid = [%s]\n", grid); //LOG("Grid = [%s]\n", grid);
int len = strlen(grid); int len = strlen(grid);
if (len == 0) { if (len == 0) {
// Blank grid is OK // Blank grid is OK
return NGBASE + 1; return NGBASE + 1;
} }
// Check for RO, RRR, or 73 in the message field normally used for grid // Check for RO, RRR, or 73 in the message field normally used for grid
if (equals(grid, "RO")) { if (equals(grid, "RO")) {
return NGBASE + 62; return NGBASE + 62;
@ -259,7 +265,7 @@ int16_t packgrid(const char *grid) {
return NGBASE + 64; return NGBASE + 64;
} }
// TODO: // Attempt to parse signal reports (e.g. "-07", "R+20")
char c1 = grid[0]; char c1 = grid[0];
int n; int n;
if (c1 == 'R') { if (c1 == 'R') {
@ -282,7 +288,7 @@ int16_t packgrid(const char *grid) {
char grid4[4]; char grid4[4];
memcpy(grid4, grid, 4); memcpy(grid4, grid, 4);
// Check for extended-range signal reports: -50 to -31, and 0 to +49 // TODO: Check for extended-range signal reports: -50 to -31, and 0 to +49
// if (n >= -50 && n <= 49) { // if (n >= -50 && n <= 49) {
// if (c1 == 'R') { // if (c1 == 'R') {
// // write(grid,1002) n+50 1002 format('LA',i2.2) // // write(grid,1002) n+50 1002 format('LA',i2.2)
@ -297,14 +303,14 @@ int16_t packgrid(const char *grid) {
// return -1; // return -1;
// } // }
// Check the locator // Check if the grid locator is properly formatted
if (len != 4) return -1; if (len != 4) return -1;
if (grid4[0] < 'A' || grid4[0] > 'R') return -1; if (grid4[0] < 'A' || grid4[0] > 'R') return -1;
if (grid4[1] < 'A' || grid4[1] > 'R') return -1; if (grid4[1] < 'A' || grid4[1] > 'R') return -1;
if (grid4[2] < '0' || grid4[2] > '9') return -1; if (grid4[2] < '0' || grid4[2] > '9') return -1;
if (grid4[3] < '0' || grid4[3] > '9') return -1; if (grid4[3] < '0' || grid4[3] > '9') return -1;
// OK, we have a properly formatted grid locator // Extract latitude and longitude
int lng = (grid4[0] - 'A') * 20; int lng = (grid4[0] - 'A') * 20;
lng += (grid4[2] - '0') * 2; lng += (grid4[2] - '0') * 2;
lng = 179 - lng; lng = 179 - lng;
@ -313,6 +319,7 @@ int16_t packgrid(const char *grid) {
lat += (grid4[3] - '0') * 1; lat += (grid4[3] - '0') * 1;
lat -= 90; lat -= 90;
// Convert latitude and longitude into single number
int16_t ng = (lng + 180) / 2; int16_t ng = (lng + 180) / 2;
ng *= 180; ng *= 180;
ng += lat + 90; ng += lat + 90;
@ -322,16 +329,16 @@ int16_t packgrid(const char *grid) {
int packmsg(const char *msg, uint8_t *dat) { // , itype, bcontest int packmsg(const char *msg, uint8_t *dat) { // , itype, bcontest
// TODO: maximum allowed length? // TODO: check what is maximum allowed length?
if (strlen(msg) > 18) { if (strlen(msg) > 22) {
return -1; return -1;
} }
char msg2[19]; // Including zero terminator! char msg2[23]; // Including zero terminator!
fmtmsg(msg2, msg); fmtmsg(msg2, msg);
//printf("msg2 = [%s]\n", msg2); //LOG("msg2 = [%s]\n", msg2);
// TODO: Change 'CQ n ' type messages to 'CQ 00n ' // TODO: Change 'CQ n ' type messages to 'CQ 00n '
//if(msg(1:3).eq.'CQ ' .and. msg(4:4).ge.'0' .and. msg(4:4).le.'9' & //if(msg(1:3).eq.'CQ ' .and. msg(4:4).ge.'0' .and. msg(4:4).le.'9' &
@ -355,16 +362,19 @@ int packmsg(const char *msg, uint8_t *dat) { // , itype, bcontest
} }
} }
// Locate the first space in the message and replace it with zero terminator // Try to split the message into three space-delimited fields
// by locating spaces and changing them to zero terminators
// Locate the first delimiter in the message
char *s1 = strchr(msg2, ' '); char *s1 = strchr(msg2, ' ');
if (s1 == NULL) { if (s1 == NULL) {
// TODO: handle this (plain text message?) // TODO: handle this (plain text message?)
return -2; return -2;
} }
*s1 = 0; *s1 = 0; // Separate fields by zero terminator
++s1; ++s1; // Now s1 points to the second field
// Locate the second space in the message // Locate the second delimiter in the message
char *s2 = strchr(s1 + 1, ' '); char *s2 = strchr(s1 + 1, ' ');
if (s2 == NULL) { if (s2 == NULL) {
// If the second space is not found, point to the end of string // If the second space is not found, point to the end of string
@ -372,17 +382,17 @@ int packmsg(const char *msg, uint8_t *dat) { // , itype, bcontest
s2 = msg2 + strlen(msg2); s2 = msg2 + strlen(msg2);
} }
else { else {
*s2 = 0; *s2 = 0;// Separate fields by zero terminator
++s2; ++s2; // Now s2 points to the third field
} }
// TODO: process callsign prefixes/suffixes
// Pack message fields into integers // Pack message fields into integers
int nc1 = packcall(msg2); int nc1 = packcall(msg2);
int nc2 = packcall(s1); int nc2 = packcall(s1);
int ng = packgrid(s2); int ng = packgrid(s2);
// TODO: callsign prefixes/suffixes
// TODO: plain text messages // TODO: plain text messages
//call packtext(msg,nc1,nc2,ng) //call packtext(msg,nc1,nc2,ng)
//ng=ng+32768 //ng=ng+32768
@ -390,12 +400,12 @@ int packmsg(const char *msg, uint8_t *dat) { // , itype, bcontest
if (nc1 < 0 || nc2 < 0 || ng < 0) { if (nc1 < 0 || nc2 < 0 || ng < 0) {
return -3; return -3;
} }
//printf("nc1 = %d [%04X], nc2 = %d [%04X], ng = %d\n", nc1, nc1, nc2, nc2, ng); //LOG("nc1 = %d [%04X], nc2 = %d [%04X], ng = %d\n", nc1, nc1, nc2, nc2, ng);
// Originally the data was packed in bytes of 6 bits. // Originally the data was packed in bytes of 6 bits.
// This seems to waste memory unnecessary and complicate the code, so we pack it in 8 bit values. // This seems to waste memory unnecessary and complicate the code, so we pack it in 8 bit values.
pack3_8bit(nc1, nc2, ng, dat); pack3_8bit(nc1, nc2, ng, dat);
//pack3_6bit(nc1, nc2, ng, dat); //pack3_6bit(nc1, nc2, ng, dat);
return 0; return 0; // Success!
} }