Last active
September 18, 2021 06:43
-
-
Save bd1es/a782e2529b8289288fadd35e407f6440 to your computer and use it in GitHub Desktop.
WSPR encoder AVR porting and testing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* wspr_enc.c | |
* | |
* This file is part of the AVR WSPR beacon. | |
* | |
* Copyright (C) 2010-2020 BD1ES. | |
* License: GNU GPL v3+ | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |
*/ | |
/* | |
* Function "wspr_enc" is designed for AVR MCUs and is used to encode the input | |
* callsign, Maidenhead locator, and transmission power into different types of | |
* WSPR messages. The composition of the callsign and the length of the locator | |
* will determine the type of encoded message. Since this function supports the | |
* type 3 message, the K1JT NHASH algorithm is therefore required to complete a | |
* callsign digest. And the source code "nhash.c" and "nhash.h" should be added | |
* to the project while building the target. | |
* | |
* wspr_enc has been tested on some AVR development boards and "Proteus VSM for | |
* AVR" by compiling the code using Atmel Studio 6 with its built-in toolchain. | |
* This function also works on Raspberry Pi OS, Armbian, Ubuntu, and Windows. | |
* | |
* wspr_enc takes advantage of the following open-source software: | |
* Joe Taylor, K1JT: WSPR, WSJT-X, and the NHASH code nhash.c, nhash.h | |
* https://sourceforge.net/p/wsjt/wsjtx/ci/master/tree/lib/wsprd | |
* | |
* James Peroulas: wspr.cpp | |
* https://github.com/JamesP6000/WsprryPi | |
* | |
* Reference: | |
* Joe Taylor, K1JT: WSPR 2.0 User's Guide | |
* https://www.physics.princeton.edu/pulsar/K1JT/WSPR_2.0_User.pdf | |
* | |
* Andy Talbot, G4JNT: The WSPR Coding Process | |
* http://www.g4jnt.com/wspr_coding_process.pdf | |
* | |
* Nick Massey, VA7NRM: Inside WSPR, JT65 and JT9 Weak-signal HF Modes | |
* http://archive.nsarc.ca/hf/jt_modes.pdf | |
* | |
* History: | |
* 21 FEB 2020: | |
* Released to the Gist | |
* https://gist.github.com/bd1es/a782e2529b8289288fadd35e407f6440 | |
* 31 MAR 2020: | |
* Replaced the parity table with an algorithm introduced by Sean Anderson | |
* http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel | |
* 06 FEB 2021: | |
* Function "char_enc" has been added to refine the scope of "wspr_cenc." | |
* 16 SEP 2021: | |
* Minor changes (mostly typing.) | |
*/ | |
#include <string.h> | |
#include <stdlib.h> | |
#include <ctype.h> | |
#if !defined(__AVR_ARCH__) | |
#include <stdio.h> | |
#endif | |
#include "nhash.h" | |
#include "wspr_enc.h" | |
// These macros are defined for sharing AVR GCC code with PCs. | |
#if defined(__GNUC__) && defined(__AVR_ARCH__) | |
#include <avr/pgmspace.h> | |
#define PGM_ROM_SPACE const PROGMEM | |
#else | |
#define PGM_ROM_SPACE const | |
#define pgm_read_byte * | |
#define pgm_read_word * | |
#define pgm_read_dword * | |
#endif | |
static uint8_t char_enc(uint8_t c) | |
{ | |
/* Character encoding table, based on "The WSPR Coding Process, G4JNT." | |
* The 37 allowed characters are allocated values from 0 to 36 such that | |
* '0' - '9' give 0 - 9, 'A' to 'Z' give 10 to 35, and [space] is given | |
* the value 36. | |
*/ | |
static PGM_ROM_SPACE uint8_t wspr_cenc[128] = { /* in ASCII order */ | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, | |
6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, | |
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, | |
35, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, | |
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, | |
0, 0, | |
}; | |
if(c < 128){ | |
return pgm_read_byte(&wspr_cenc[c]); | |
}else{ | |
return 0; | |
} | |
} | |
// This function was ported from James Peroulas's wspr(). | |
static void pack_call(const char *call, uint8_t clen, uint32_t *n1, | |
uint32_t *ng, uint8_t *nadd) | |
{ | |
const uint8_t *s = (uint8_t *)strchr(call, '/'); | |
const uint8_t *c = (uint8_t *)call; | |
uint8_t n, i; | |
uint32_t m = 0; | |
if(s){ | |
*nadd = 2; | |
// stroke position | |
i = s - c; | |
// suffix len, prefix-call len | |
n = clen - i - 1; | |
// 1-digit suffix /A to /Z, /0 to /9 | |
if(n == 1) m = 60000 - 32768 + char_enc(c[i+1]); | |
// 2-digit suffix /10 to /99 | |
if(n == 2) m = 60000 + 26 + 10*char_enc(c[i+1]) + char_enc(c[i+2]); | |
// prefix EA8/, right align | |
if(n > 2){ | |
m = i < 3 ? 36 : char_enc(c[i-3]); | |
m = 37 * m + (i < 2 ? 36 : char_enc(c[i-2])); | |
m = 37 * m + (i < 1 ? 36 : char_enc(c[i-1])); | |
if(m < 32768){ | |
*nadd = 1; | |
}else{ | |
m -= 32768; | |
} | |
c += i + 1; | |
clen -= i + 1; | |
}else{ | |
clen -= n + 1; | |
} | |
// for the type 2 message, ng contains either a call prefix or suffix | |
*ng = m; | |
}else{ | |
*nadd = 0; | |
} | |
// n1 contains an ordinary call | |
i = isdigit(c[2]) ? 2 : isdigit(c[1]) ? 1 : 0; | |
n = clen - i - 1; | |
m = i < 2 ? 36 : char_enc(c[i-2]); | |
m = 36 * m + (i < 1 ? 36 : char_enc(c[i-1])); | |
m = 10 * m + char_enc(c[i]); | |
m = 27 * m + (n < 1 ? 26 : char_enc(c[i+1])-10); | |
m = 27 * m + (n < 2 ? 26 : char_enc(c[i+2])-10); | |
m = 27 * m + (n < 3 ? 26 : char_enc(c[i+3])-10); | |
*n1 = m; | |
} | |
// Pack the given parameters into a single WSPR code block. | |
static uint8_t pack_message(const char *call, const char *grid, | |
const char *dBm, uint8_t *packed) | |
{ | |
uint32_t n1, ng; | |
uint8_t nadd, mtype; | |
uint8_t gridlen = strlen(grid); | |
if(gridlen == 4){ | |
pack_call(call, strlen(call), &n1, &ng, &nadd); | |
// for the type 1 message, ng contains a 4-character grid locator | |
if(nadd == 0){ | |
const uint8_t *g = (uint8_t *)grid; | |
ng = 180 * (179 - 10*(char_enc(g[0])-10) - char_enc(g[2])) | |
+ 10*(char_enc(g[1])-10) + char_enc(g[3]); | |
mtype = 1; | |
}else{ | |
mtype = 2; | |
} | |
}else if(gridlen == 6){ | |
char tmp[WSPR_MAX_CALLLEN_TYPE3]; | |
uint8_t i, clen = 0; | |
// for the type 3 message, ng contains a 15-bit callsign digest | |
for(i = 0; i < WSPR_MAX_CALLLEN_TYPE3; i++){ | |
if(call[i]){ | |
tmp[i] = toupper(call[i]); | |
clen++; | |
}else{ | |
break; | |
} | |
} | |
ng = nhash(tmp, clen, (uint32_t)146); | |
// n1 has a left-rotated 6-character locator that acts as a call | |
for(i = 0; i < 5; i++) { | |
tmp[i] = grid[i+1]; | |
} | |
tmp[5] = grid[0]; | |
tmp[6]=0; | |
pack_call(tmp, 6, &n1, &ng, &nadd); | |
mtype = 3; | |
}else{ | |
return 0; | |
} | |
// EIRP in dBm = {0,3,7,10,13,17,20,23,27,30,33,37,40,43,47,50,53,57,60} | |
static PGM_ROM_SPACE int8_t corr[] = {0, -1, 1, 0, -1, 2, 1, 0, -1, 1}; | |
int pwr = atoi(dBm); | |
pwr = pwr>60 ? 60 : pwr<0 ? 0 : pwr + (int8_t)pgm_read_byte(&corr[pwr%10]); | |
// add the converted power level to ng according to the message type | |
int8_t ntype = gridlen!=6 ? pwr+nadd : -(pwr+1); | |
ng = 128*ng + ntype + 64; | |
// pack n1, ng, and zero-tail to 50 bits | |
packed[0] = (uint8_t)(n1>>20); | |
packed[1] = (uint8_t)(n1>>12), | |
packed[2] = (uint8_t)(n1>>4), | |
packed[3] = (uint8_t)(((n1&0x0f)<<4) | ((ng>>18)&0x0f)), | |
packed[4] = (uint8_t)(ng>>10), | |
packed[5] = (uint8_t)(ng>>2), | |
packed[6] = (uint8_t)((ng&0x03)<<6), | |
packed[7] = packed[8] = packed[9] = packed[10] = 0; | |
return mtype; | |
} | |
/* Calculate the parity of 32-bit given "v" and double the returning result. | |
* Such a value can be added directly to the channel sync. | |
*/ | |
static uint8_t sparity(uint32_t v) | |
{ | |
v ^= v >> 16; | |
v ^= v >> 8; | |
v ^= v >> 4; | |
v &= 0xf; | |
return ((0x6996u*2) >> v) & 2; | |
} | |
// Encode the WSPR code block into a set of channel symbols, a WSPR message. | |
static void encode_symbols(const uint8_t *packed, uint8_t *symbols) | |
{ | |
/* symbol interleaving table */ | |
static PGM_ROM_SPACE uint8_t interleave[162] = { | |
0, 128, 64, 32, 160, 96, 16, 144, 80, 48, 112, 8, 136, 72, | |
40, 104, 24, 152, 88, 56, 120, 4, 132, 68, 36, 100, 20, 148, | |
84, 52, 116, 12, 140, 76, 44, 108, 28, 156, 92, 60, 124, 2, | |
130, 66, 34, 98, 18, 146, 82, 50, 114, 10, 138, 74, 42, 106, | |
26, 154, 90, 58, 122, 6, 134, 70, 38, 102, 22, 150, 86, 54, | |
118, 14, 142, 78, 46, 110, 30, 158, 94, 62, 126, 1, 129, 65, | |
33, 161, 97, 17, 145, 81, 49, 113, 9, 137, 73, 41, 105, 25, | |
153, 89, 57, 121, 5, 133, 69, 37, 101, 21, 149, 85, 53, 117, | |
13, 141, 77, 45, 109, 29, 157, 93, 61, 125, 3, 131, 67, 35, | |
99, 19, 147, 83, 51, 115, 11, 139, 75, 43, 107, 27, 155, 91, | |
59, 123, 7, 135, 71, 39, 103, 23, 151, 87, 55, 119, 15, 143, | |
79, 47, 111, 31, 159, 95, 63, 127, | |
}; | |
// channel syncs | |
static PGM_ROM_SPACE uint8_t sync[162] = { | |
1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, | |
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, | |
1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, | |
1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, | |
0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, | |
0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, | |
0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, | |
}; | |
/* The data will be expanded to add FEC with a rate ½, constraint length | |
* 32, convolutional encoder. (G4JNT) | |
*/ | |
uint8_t i, j, k, conv_ptr = 0; | |
uint32_t reg = 0; | |
for(i = 0; i < 11; i++){ | |
for(j = 7; j < 255; j--){ | |
if(packed[i] & (1<<j)) reg |= 1; | |
k = pgm_read_byte(&interleave[conv_ptr++]); | |
symbols[k] = sparity(reg & 0xF2D05351) | pgm_read_byte(&sync[k]); | |
k = pgm_read_byte(&interleave[conv_ptr++]); | |
symbols[k] = sparity(reg & 0xE4613C47) | pgm_read_byte(&sync[k]); | |
if(conv_ptr == 162){ | |
break; | |
}else{ | |
reg <<= 1; | |
} | |
} | |
} | |
} | |
// The WSPR encoder. | |
uint8_t wspr_enc(const char *call, const char *grid, const char *dBm, | |
uint8_t *symbols) | |
{ | |
uint8_t packed[11]; | |
uint8_t msgtype = pack_message(call, grid, dBm, packed); | |
if(msgtype != 0){ | |
encode_symbols(packed, symbols); | |
} | |
return msgtype; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* wspr_enc.h | |
* | |
* This file is part of the AVR WSPR beacon. | |
* | |
* Copyright (C) 2010-2020 BD1ES. | |
*/ | |
#ifndef WSPR_ENC_H | |
#define WSPR_ENC_H | |
#include <stdint.h> | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
/* These macros are defined for WSPR specific message types. Developers can use | |
* them to constrain the length of the callsign input. | |
*/ | |
#define WSPR_MAX_CALLLEN_TYPE1 6 | |
#define WSPR_MAX_CALLLEN_TYPE2 10 | |
#define WSPR_MAX_CALLLEN_TYPE3 10 | |
/* WSPR encoder | |
* | |
* Parameters | |
* call: Pointer to a callsign. | |
* A compound callsign will produce a type 2 message. (And 3DA0xx | |
* will always look forward to its alternative form of 3D0xx.) | |
* grid: Pointer to a Maidenhead grid locator. | |
* A 6-character locator will produce a type 3 message. | |
* dBm: Pointer to TX power level, ranging from "0" to "60." | |
* symbols: Pointer to an 8-bit array holding 162 channel symbols. | |
* | |
* Return value: | |
* 0: Error occurred. (The length of locator is neither 4 nor 6.) | |
* 1 - 3: The type of generated message. | |
* | |
* Comments: | |
* This function is mainly designed for AVRs, with fewer input checks. | |
* For improved stability, developers might need to prepare additional | |
* validations in their application. | |
*/ | |
uint8_t wspr_enc(const char *call, const char *grid, const char *dBm, | |
uint8_t *symbols); | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif // WSPR_ENC_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* wspr_enc_test.c | |
* | |
* This file is part of the AVR WSPR beacon. | |
* | |
* Copyright (C) 2010-2020 BD1ES. | |
*/ | |
/* | |
* This program is designed for developers to test the WSPR encoding function | |
* "wspr_enc," which I have used to generate WSPR messages in my project. | |
* | |
* On top of the test, K1JT's WSPR message simulation tool "WSPRcode" needs to | |
* be installed to provide original reference symbols. You can build this tool | |
* from the source by typing "./go.sh" on any platform equipped with a Fortran | |
* 90 compiler. To download the source code, please visit | |
* https://sourceforge.net/p/wsjt/wsjtx/ci/master/tree/lib/wsprcode | |
* | |
* Reference: | |
* Joe Taylor, K1JT: WSPR 2.0 User's Guide | |
* https://www.physics.princeton.edu/pulsar/K1JT/WSPR_2.0_User.pdf | |
* | |
* Andy Talbot, G4JNT: The WSPR Coding Process | |
* http://www.g4jnt.com/wspr_coding_process.pdf | |
* | |
* History: | |
* 21 FEB 2020: | |
* Released to the Gist: | |
* https://gist.github.com/bd1es/a782e2529b8289288fadd35e407f6440 | |
* 31 MAR 2020: | |
* The parity table has been removed due to the optimization of the encoder. | |
* 01 APR 2020: | |
* Encoder-generated symbols can be auto-compared by invoking "WSPRcode." | |
* 06 FEB 2021: | |
* Minor changes. | |
* 16 SEP 2021: | |
* Minor changes (mostly typing.) | |
*/ | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <ctype.h> | |
#include <getopt.h> | |
#include <stdarg.h> | |
#include "wspr_enc.h" | |
// This function generates a WSPR character encoding table. | |
static void gen_char_enc_table() | |
{ | |
puts("/* Character encoding table, based on \"The WSPR Coding Process, G4JNT.\"\n" | |
" * The 37 allowed characters are allocated values from 0 to 36 such that\n" | |
" * '0' - '9' give 0 - 9, 'A' to 'Z' give 10 to 35, and [space] is given\n" | |
" * the value 36.\n" | |
" */" | |
); | |
printf("static PGM_ROM_SPACE uint8_t wspr_cenc[128] = { /* in ASCII order */"); | |
int i; | |
for(i = 0; i < 128; i++){ | |
int k; | |
if(i == ' '){ | |
k = 36; | |
}else if(i >= '0' && i <= '9'){ | |
k = i - '0'; | |
}else if(i >= 'A' && i <= 'Z'){ | |
k = i - 'A' + 10; | |
}else if(i >= 'a' && i <= 'z'){ | |
k = i - 'a' + 10; | |
}else{ | |
k = 0; | |
} | |
if((i%18) == 0){ | |
printf("\n "); | |
} | |
printf("%2d, ", k); | |
} | |
puts("\n};"); | |
} | |
// This function generates a WSPR specific symbol interleaving table. | |
static void gen_interleave_table() | |
{ | |
puts("/* symbol interleaving table. */"); | |
printf("static PGM_ROM_SPACE uint8_t interleave[162] = {"); | |
int i, j, k; | |
for(i = 0, k = 0; i < 256; i++){ | |
uint8_t reversed_byte = 0; | |
for(j = 0; j < 8; j++){ | |
uint8_t temp = (i & (1 << j)); | |
if(temp){ | |
reversed_byte |= (1 << ((8 - 1) - j)); | |
} | |
} | |
if(reversed_byte < 162){ | |
if((k++)%14 == 0){ | |
printf("\n "); | |
} | |
printf("%3d, ", reversed_byte); | |
if(k == 162){ | |
break; | |
} | |
} | |
} | |
puts("\n};"); | |
} | |
// ---------------------------------------------------------------------------- | |
static void str_to_upper(char *dist, const char *str) | |
{ | |
while(*str) *dist++ = toupper(*str++); | |
*dist = 0; | |
} | |
static void print_help(const char *pname) | |
{ | |
printf("This program is designed to test the WSPR encoding function \"wspr_enc.\"\n"); | |
printf("Usage: %s [options] <callsign> <grid> <dBm>\n", pname); | |
printf("Examples: %s BD1XYZ OM89 10 (message type 1)\n", pname); | |
printf(" %s BD1XYZ/M xxxx 20 (message type 2)\n", pname); | |
printf(" %s BD1XYZ OM89dw 30 (message type 3)\n", pname); | |
printf("Options:\n"); | |
printf(" -h --help\n"); | |
printf(" Display this help.\n"); | |
printf(" --gen_tables\n"); | |
printf(" Generate encoder lookup tables.\n"); | |
printf("Arguments:\n"); | |
printf(" callsign: BD1XYZ BD1XYZ/9 EA8/BD1XYZ ...\n"); | |
printf(" in which a compound callsign will produce a type 2 message.\n"); | |
printf(" grid: 4- or 6-character grid locator\n"); | |
printf(" in which a 6-character locator will produce a type 3 message.\n"); | |
printf(" dBm: The TX power (EIRP) in dBm, ranging from 0 to 60.\n"); | |
printf("\n"); | |
printf("%s requires K1JT's WSPRcode to check any generated messages.\n", pname); | |
} | |
struct strings_t{ | |
int size; | |
char line[256][256]; | |
}; | |
static int run_pipe(struct strings_t *strs, const char *command) | |
{ | |
FILE *fpipe; | |
if(!(fpipe = (FILE*)popen(command, "r"))){ | |
perror(""); | |
return -1; | |
} | |
strs->size = 0; | |
while(fgets(strs->line[strs->size], 256, fpipe)){ | |
strs->size++; | |
if(strs->size == 256) break; | |
} | |
pclose(fpipe); | |
return 0; | |
} | |
static int check_channel_symbols(uint8_t *symbols, struct strings_t *strs) | |
{ | |
int i, symbol_pos = 0; | |
// find the starting position of channel symbols given by WSPRcode | |
for(i = 0; i < strs->size; i++){ | |
if(strncmp(strs->line[i], "Channel symbols:", 16) == 0){ | |
symbol_pos = i+1; | |
break; | |
} | |
} | |
if(symbol_pos == 0){ | |
printf("No channel symbols were found in WSPRcode output.\n" | |
"Symbol verification cannot continue.\n"); | |
return -1; | |
} | |
char line[256]; | |
int verified = 0; | |
for(i = 0; i < 6; i++){ | |
int j; | |
char *line_p = line; | |
for(j = 0; (j < 30) && ((i*30 + j) < 162); j++){ | |
*line_p++ = '0' + symbols[i*30 + j]; | |
*line_p++ = ' '; | |
} | |
*line_p = 0; | |
if(strncmp(strs->line[symbol_pos+i]+6, line, line_p-line-1) != 0){ | |
printf("Inconsistent channel symbols were found in line %d:\n" | |
"%s--> %s\n", | |
i+1, strs->line[symbol_pos+i], line); | |
verified = -1; | |
} | |
} | |
return verified; | |
} | |
/* run this program using the console pauser or add your own getch, system("pause") or input loop */ | |
int main(int argc, char *argv[]) | |
{ | |
static const char *PROG_NAME = "wspr_enc_test"; | |
char *call = NULL, *grid = NULL, *dBm = NULL; | |
char call_u[256], grid_u[256]; | |
static const struct option long_options[] = { | |
{"help", no_argument, 0, 'h'}, | |
{"gen_tables", no_argument, 0, 1}, | |
{0, 0, 0, 0} | |
}; | |
// get options | |
while(1){ | |
int option_index; | |
int c = getopt_long(argc, argv, "h", long_options, &option_index); | |
if(c == -1){ | |
break; | |
} | |
switch(c){ | |
case 'h': | |
print_help(PROG_NAME); | |
return 0; | |
case 1: | |
gen_char_enc_table(); | |
gen_interleave_table(); | |
return 0; | |
default: | |
return -1; | |
} | |
} | |
// get arguments | |
size_t nargs = 0; | |
char *endp; | |
while(optind < argc){ | |
switch(nargs){ | |
case 0: | |
call = argv[optind++]; | |
if(strlen(call) > (sizeof(call_u)-1)) call[sizeof(call_u)-1] = 0; | |
if((strlen(call) < 2) || (strlen(call) > 10)){ | |
printf("Warning, invalid length of the given callsign %s.\n", call); | |
} | |
str_to_upper(call_u, call); | |
// for Swaziland 3DA0YZ, 3D0YZ will be used for proper decoding | |
if(strncmp(call_u, "3DA0", 4) == 0){ | |
size_t i; | |
for(i = 3; i < strlen(call); i++){ | |
call[i-1] = call[i]; | |
} | |
call[i-1] = 0; | |
} | |
str_to_upper(call_u, call); | |
break; | |
case 1: | |
grid = argv[optind++]; | |
if(strlen(grid) > (sizeof(grid_u)-1)) grid[sizeof(grid_u)-1] = 0; | |
if((strlen(grid)!=4) && (strlen(grid)!=6)){ | |
printf("Warning, invalid length of the given locator %s.\n", grid); | |
} | |
str_to_upper(grid_u, grid); | |
break; | |
case 2: | |
dBm = argv[optind++]; | |
strtol(dBm, &endp, 10); | |
if((dBm==endp) || (*endp!='\0')){ | |
printf("Invalid dBm value %s.\n", dBm); | |
return -1; | |
} | |
break; | |
default: | |
puts("Too many parameters.\n"); | |
print_help(PROG_NAME); | |
return -1; | |
} | |
nargs++; | |
} | |
if(nargs < 3){ | |
puts("Too few parameters.\n"); | |
print_help(PROG_NAME); | |
return -1; | |
} | |
// invoke "wspr_enc" to generate WSPR channel symbols | |
printf("Converting given parameters:\n" | |
"callsign = %s, " | |
"grid = %s, " | |
"power = %s dBm\n", call, grid, dBm); | |
uint8_t symbols[162]; | |
uint8_t msgtype = wspr_enc(call, grid, dBm, symbols); | |
printf("into the following channel symbols (with message type %d:)\n", msgtype); | |
int i; | |
for(i = 0; i < 162; i++){ | |
if(i%30 == 0){ | |
printf("\n %d", symbols[i]); | |
}else{ | |
printf(" %d", symbols[i]); | |
} | |
} | |
printf("\n"); | |
// build command for invoking WSPRcode according to the "msgtype" | |
char cmd[1024], msg[1024]; | |
if(msgtype == 1){ | |
sprintf(cmd, "WSPRcode \"%s %s %s\"", call_u, grid_u, dBm); | |
sprintf(msg, "Message: %s %s %s", call_u, grid_u, dBm); | |
}else if(msgtype == 2){ | |
sprintf(cmd, "WSPRcode \"%s %s\"", call_u, dBm); | |
sprintf(msg, "Message: %s %s", call_u, dBm); | |
}else if(msgtype == 3){ | |
sprintf(cmd, "WSPRcode \"<%s> %s %s\"", call_u, grid_u, dBm); | |
sprintf(msg, "Message: <%s> %s %s", call_u, grid_u, dBm); | |
}else{ | |
printf("Message type %d is unknown. Check the given locator.\n", msgtype); | |
return -1; | |
} | |
// run WSPRcode and collect its output from the pipe. | |
printf("\nChecking symbols by invoking %s ...\n", cmd); | |
struct strings_t strs; | |
int res = run_pipe(&strs, cmd); | |
if(res || (strncmp(strs.line[0], msg, strlen(msg))!=0)){ | |
printf("Command WSPRcode returned unexpected \"%s\".\n", strs.line[0]); | |
return -1; | |
}else{ | |
int i; | |
for(i = 0; i < strs.size; i++){ | |
printf("%s", strs.line[i]); | |
} | |
} | |
printf("\n"); | |
// perform the verification | |
res = check_channel_symbols(symbols, &strs); | |
if(res == 0){ | |
printf("Symbols have been verified.\n"); | |
}else{ | |
printf("Symbol verification failed.\n"); | |
} | |
return res; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment