2018-07-08 02:06:25 +02:00
|
|
|
#include "wspr.h"
|
|
|
|
|
2018-07-15 00:20:41 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
|
2018-07-08 02:06:25 +02:00
|
|
|
#include <util/delay.h>
|
2018-07-15 00:20:41 +02:00
|
|
|
#include <avr/pgmspace.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uint8_t WSPR_SYNC[WSPR_LENGTH] PROGMEM = {
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline uint8_t wspr_call_char(char c) {
|
|
|
|
c = toupper(c);
|
|
|
|
if(c >= '0' && c <= '9') {
|
|
|
|
return c - '0';
|
|
|
|
}
|
|
|
|
else if(c >= 'A' && c <= 'Z') {
|
|
|
|
return 10 + (c - 'A');
|
|
|
|
}
|
|
|
|
// Turn everything else to space
|
|
|
|
else {
|
|
|
|
return 36;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint8_t wspr_loc_char(char c) {
|
|
|
|
c = toupper(c);
|
|
|
|
if(c >= '0' && c <= '9') {
|
|
|
|
return c - '0';
|
|
|
|
}
|
|
|
|
else if(c >= 'A' && c <= 'R') {
|
|
|
|
return c - 'A';
|
|
|
|
}
|
|
|
|
// Everthing else becomes 9/I
|
|
|
|
else {
|
|
|
|
return 9;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint8_t wspr_power(uint8_t power) {
|
|
|
|
if(power > 60) {
|
|
|
|
return 60;
|
|
|
|
}
|
|
|
|
return power;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint8_t WSPR_POWER_LOC_BITS = 22;
|
|
|
|
uint64_t wspr_message(char *call, char *loc, uint8_t power) {
|
|
|
|
uint32_t m, n;
|
|
|
|
|
|
|
|
n = wspr_call_char(call[0]);
|
|
|
|
n = n * 36 + wspr_call_char(call[1]);
|
|
|
|
n = n * 10 + wspr_call_char(call[2]);
|
|
|
|
n = n * 27 + wspr_call_char(call[3]) - 10;
|
|
|
|
n = n * 27 + wspr_call_char(call[4]) - 10;
|
|
|
|
n = n * 27 + wspr_call_char(call[5]) - 10;
|
|
|
|
|
|
|
|
m = 179 - 10 * wspr_loc_char(loc[0]) - wspr_loc_char(loc[2]);
|
|
|
|
m = m * 180 + 10 * wspr_loc_char(loc[1]) + wspr_loc_char(loc[3]);
|
|
|
|
m = m * 128 + wspr_power(power) + 64;
|
|
|
|
|
|
|
|
return ((uint64_t) n) << WSPR_POWER_LOC_BITS | m;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const uint32_t WSPR_FEC_TAPS1 = 0xF2D05351;
|
|
|
|
const uint32_t WSPR_FEC_TAPS2 = 0xE4613C47;
|
|
|
|
|
|
|
|
uint8_t wspr_parity32(uint32_t x) {
|
|
|
|
x ^= x >> 16;
|
|
|
|
x ^= x >> 8;
|
|
|
|
x ^= x >> 4;
|
|
|
|
x ^= x >> 2;
|
|
|
|
x ^= x >> 1;
|
|
|
|
return x & 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void wspr_fec(uint64_t msg, uint8_t *output) {
|
|
|
|
memset(output, 0, WSPR_LENGTH);
|
|
|
|
|
|
|
|
uint32_t reg = 0;
|
|
|
|
for(uint8_t i = 0; i < 81; i++) {
|
|
|
|
uint8_t bit = 0;
|
|
|
|
if(i < 50) {
|
|
|
|
bit = (msg >> (50 - i - 1)) & 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
reg = (reg << 1) | bit;
|
|
|
|
|
|
|
|
uint32_t tabs1 = reg & WSPR_FEC_TAPS1;
|
|
|
|
uint32_t tabs2 = reg & WSPR_FEC_TAPS2;
|
|
|
|
|
|
|
|
output[i * 2] = wspr_parity32(tabs1);
|
|
|
|
output[i * 2 + 1] = wspr_parity32(tabs2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t wspr_reverse_bits(uint8_t bits) {
|
|
|
|
//https://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/AVR-Built_002din-Functions.html
|
|
|
|
return __builtin_avr_insert_bits (0x01234567, bits, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void wspr_interleave(uint8_t *input, uint8_t *output) {
|
|
|
|
uint8_t p = 0;
|
|
|
|
uint8_t i = 0;
|
|
|
|
while(p < WSPR_LENGTH) {
|
|
|
|
uint8_t rev_i = wspr_reverse_bits(i);
|
|
|
|
if(rev_i < WSPR_LENGTH) {
|
|
|
|
output[rev_i] = input[p];
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void wspr_add_sync(uint8_t *symbols) {
|
|
|
|
for(uint8_t i = 0; i < WSPR_LENGTH; i++) {
|
|
|
|
symbols[i] = pgm_read_byte(&(WSPR_SYNC[i])) + 2 * symbols[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void wspr_encode(char *call, char *loc, uint8_t power, uint8_t *buffer) {
|
|
|
|
uint8_t tmp_buffer[WSPR_LENGTH];
|
|
|
|
|
|
|
|
uint64_t msg = wspr_message(call, loc, power);
|
|
|
|
wspr_fec(msg, tmp_buffer);
|
|
|
|
wspr_interleave(tmp_buffer, buffer);
|
|
|
|
wspr_add_sync(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// For PLL with 800MHz
|
|
|
|
const struct si5351_params WSPR_SYMBOLS[4] = {
|
|
|
|
// 7040100.000000: 113 + 44687 / 70401
|
|
|
|
// actual: 7040100.000000
|
|
|
|
{14033, 17455, 70401},
|
|
|
|
// 7040101.464844: 113 + 447801 / 705503
|
|
|
|
// actual: 7040101.464844
|
|
|
|
{14033, 172785, 705503},
|
|
|
|
// 7040102.929688: 113 + 38515 / 60682
|
|
|
|
// actual: 7040102.929688
|
|
|
|
{14033, 14678, 60682},
|
|
|
|
// 7040104.394531: 113 + 123233 / 194166
|
|
|
|
// actual: 7040104.394531
|
|
|
|
{14033, 46378, 194166},
|
|
|
|
};
|
|
|
|
|
2018-07-08 02:06:25 +02:00
|
|
|
|
2018-07-15 00:20:41 +02:00
|
|
|
const uint16_t WSPR_PERIOD = 683;
|
2018-07-08 02:06:25 +02:00
|
|
|
|
|
|
|
void wspr_transmit(enum si5351_multisynth synth, uint8_t *msg) {
|
|
|
|
si5351_ms_enable_output(synth);
|
|
|
|
for(uint8_t i = 0; i < WSPR_LENGTH; i++) {
|
|
|
|
uint8_t sym = msg[i];
|
|
|
|
si5351_ms_write_params(synth, WSPR_SYMBOLS[sym]);
|
|
|
|
_delay_ms(WSPR_PERIOD);
|
|
|
|
}
|
|
|
|
si5351_ms_disable_output(synth);
|
|
|
|
}
|