diff --git a/firmware/include/wspr.h b/firmware/include/wspr.h index 1f6fb1c..78018f3 100644 --- a/firmware/include/wspr.h +++ b/firmware/include/wspr.h @@ -6,40 +6,11 @@ #include "si5351.h" +#define WSPR_LENGTH 162 -// For PLL with 800MHz -static 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}, -}; - - -static const uint8_t WSPR_LENGTH = 162; -static const uint16_t WSPR_PERIOD = 683; - -// DL1SSK at JN39we -static uint8_t default_wspr_msg[162] = { - 1, 1, 0, 0, 2, 0, 0, 2, 1, 0, 0, 0, 1, 3, 3, 2, 2, 2, 3, 2, 0, 1, 0, 3, 1, 1, 1, 0, 2, - 2, 0, 2, 2, 0, 3, 0, 0, 1, 0, 3, 2, 0, 0, 0, 0, 0, 3, 0, 1, 1, 0, 0, 3, 1, 2, 1, 0, 2, - 2, 3, 1, 2, 1, 2, 2, 2, 2, 3, 3, 2, 1, 2, 1, 2, 1, 2, 3, 2, 0, 3, 2, 0, 1, 0, 3, 3, 0, - 2, 2, 1, 1, 2, 1, 0, 1, 0, 2, 0, 1, 2, 2, 2, 0, 0, 3, 2, 2, 3, 0, 0, 1, 3, 3, 2, 3, 3, - 0, 2, 3, 1, 2, 1, 0, 2, 2, 3, 3, 3, 0, 2, 2, 0, 2, 1, 2, 1, 2, 0, 1, 3, 0, 0, 0, 0, 0, - 2, 0, 3, 1, 0, 1, 2, 3, 1, 2, 2, 2, 1, 1, 0, 0, 2 -}; - - - +void wspr_encode(char *call, char *loc, uint8_t power, uint8_t *buffer); void wspr_transmit(enum si5351_multisynth synth, uint8_t *msg); + #endif diff --git a/firmware/main.c b/firmware/main.c index 4c7334b..7a55f3e 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -19,7 +19,10 @@ int main(void) { //si5351_ms_write_params(SI5351_MS0, WSPR_SYMBOLS[3]); - wspr_transmit(SI5351_MS0, default_wspr_msg); + uint8_t wspr_buffer[WSPR_LENGTH]; + + wspr_encode("DL1SSK", "JN39", 27, wspr_buffer); + wspr_transmit(SI5351_MS0, wspr_buffer); while(1); diff --git a/firmware/wspr.c b/firmware/wspr.c index b5d0e79..6e62830 100644 --- a/firmware/wspr.c +++ b/firmware/wspr.c @@ -1,7 +1,161 @@ #include "wspr.h" -#include +#include +#include +#include +#include + + + +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}, +}; + + +const uint16_t WSPR_PERIOD = 683; void wspr_transmit(enum si5351_multisynth synth, uint8_t *msg) { si5351_ms_enable_output(synth);