diff --git a/docs/WSPR.md b/docs/WSPR.md new file mode 100644 index 0000000000..4ee782eccf --- /dev/null +++ b/docs/WSPR.md @@ -0,0 +1,29 @@ +# WSPR Design + +## Introduction + +WSPR is a stands for Weak Signal Propogation Reporter, more information can be found at [https://www.sigidwiki.com/wiki/WSPR]https://www.sigidwiki.com/wiki/WSPR + +WSPR is designed to be able to report incredibly weak signals, and has various base points for detection around the world. This allows communication at an extremely slow speed for location data to provide telemetry. This can be seen on WSPRNet. + +[http://www.wsprnet.org/drupal/WSPRnet/map]WSPRNet + +For use the protocol requires an amateur radio license. For this project, I have aquired one for use of WSPRNet, however anyone using this project will need to aquire a license for themselves in order to use this technology. + +## Base Design + +### Code + +The project is based on [https://github.com/alexf91/WSPR-Beacon/]alexf91/WSPR-Beacon however has been completely rewritten. + +The initial code was designed for an Arduino based solution with a USB interface. The re-write here disregards the code apart from the WSPR encoding library. + +A special mention here is [https://github.com/threeme3/WsprryPi]threeme3/WsprryPi. I's not used in this project, but was vastly helpful for design purposes. + +### Electronics + +The WSPR part of the project requires an oscillator to function at around 14MHz. + +For this it was chosen to use the si5351, for the good community support, and the ease of the i2c interface. + +Technically, it a low pass filter should be added, but given the harmonics of a square wave and the transmission power, for this indiviual use case, I would consider unnecessary. \ No newline at end of file diff --git a/local.sh b/local.sh index ff0a2a76ac..d67f3915c3 100755 --- a/local.sh +++ b/local.sh @@ -3,4 +3,7 @@ if ./build.sh ; then sudo cp -rf output/image ~/nfs/image else echo "Build failure, not copying" -fi \ No newline at end of file +fi + +# Note to self, compiling a single thing is a lot easier when you add the toolchain! +# export PATH=/home/aj/lorawan-balloon/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin:$PATH \ No newline at end of file diff --git a/sysdrv/cfg/package.mk b/sysdrv/cfg/package.mk index 7b39036270..c0fbd86cd4 100644 --- a/sysdrv/cfg/package.mk +++ b/sysdrv/cfg/package.mk @@ -41,3 +41,7 @@ $(eval $(call MACRO_CHECK_ENABLE_PKG, RK_ENABLE_OTA)) # Enable build strace CONFIG_SYSDRV_ENABLE_STRACE=n $(eval $(call MACRO_CHECK_ENABLE_PKG, RK_ENABLE_STRACE)) + +# Enable build WSPR +CONFIG_SYSDRV_ENABLE_WSPR=y +$(eval $(call MACRO_CHECK_ENABLE_PKG, RK_ENABLE_WSPR)) diff --git a/sysdrv/source/kernel/arch/arm/boot/dts/rv1103g-luckfox-pico-mini-b.dts b/sysdrv/source/kernel/arch/arm/boot/dts/rv1103g-luckfox-pico-mini-b.dts index e8ea642fd7..fbc2eebfae 100755 --- a/sysdrv/source/kernel/arch/arm/boot/dts/rv1103g-luckfox-pico-mini-b.dts +++ b/sysdrv/source/kernel/arch/arm/boot/dts/rv1103g-luckfox-pico-mini-b.dts @@ -20,6 +20,15 @@ regulator-name = "gpio1_pd0"; regulator-always-on; }; + + clocks { + /* 25MHz reference crystal */ + ref25: ref25M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + }; }; &sfc { @@ -67,14 +76,85 @@ // status = "disabled"; //}; + // /**********I2C**********/ &i2c3 { status = "okay"; pinctrl-0 = <&i2c3m1_xfer>; clock-frequency = <100000>; +/* Si5351a msop10 i2c clock generator */ + si5351a: clock-generator@60 { + compatible = "silabs,si5351a-msop"; + reg = <0x60>; + #address-cells = <1>; + #size-cells = <0>; + #clock-cells = <1>; + + /* connect xtal input to 25MHz reference */ + clocks = <&ref25>; + clock-names = "xtal"; + + /* connect xtal input as source of pll0 and pll1 */ + silabs,pll-source = <0 0>, <1 0>; + + /* + * overwrite clkout0 configuration with: + * - 8mA output drive strength + * - pll0 as clock source of multisynth0 + * - multisynth0 as clock source of output divider + * - multisynth0 can change pll0 + * - set initial clock frequency of 74.25MHz + */ + clkout0 { + reg = <0>; + silabs,drive-strength = <8>; + silabs,multisynth-source = <0>; + silabs,clock-source = <0>; + silabs,pll-master; + /* clock-frequency = <74250000>;*/ + }; + + /* + * overwrite clkout1 configuration with: + * - 4mA output drive strength + * - pll1 as clock source of multisynth1 + * - multisynth1 as clock source of output divider + * - multisynth1 can change pll1 + */ + clkout1 { + reg = <1>; + silabs,drive-strength = <4>; + silabs,multisynth-source = <1>; + silabs,clock-source = <0>; + pll-master; + }; + + /* + * overwrite clkout2 configuration with: + * - xtal as clock source of output divider + */ + clkout2 { + reg = <2>; + silabs,clock-source = <2>; + }; + }; + +}; + +&pinctrl { + i2c3 { + /omit-if-no-ref/ + i2c3m1_xfer: i2c3m1-xfer { + rockchip,pins = + /* i2c3_scl_m1 */ + <1 RK_PD3 3 &pcfg_pull_up>, + /* i2c3_sda_m1 */ + <1 RK_PD2 3 &pcfg_pull_up>; + }; + }; }; -// /**********SPI**********/ +/**********SPI**********/ &spi0 { status = "okay"; pinctrl-names = "default"; diff --git a/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig b/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig index 4c54b69658..d1652b1ee2 100755 --- a/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig +++ b/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig @@ -320,3 +320,4 @@ CONFIG_DEBUG_FS=y # CONFIG_SCHED_DEBUG is not set # CONFIG_FTRACE is not set # CONFIG_RUNTIME_TESTING_MENU is not set +CONFIG_COMMON_CLK_SI5351=y diff --git a/sysdrv/tools/board/Makefile.tools.board.mk b/sysdrv/tools/board/Makefile.tools.board.mk index a4bf0eab27..0c8a2fc5ff 100644 --- a/sysdrv/tools/board/Makefile.tools.board.mk +++ b/sysdrv/tools/board/Makefile.tools.board.mk @@ -9,7 +9,8 @@ tools_board-builds: \ board-build-rockchip_test \ board-build-e2fsprogs \ board-build-sysstat \ - board-build-mtd_utils + board-build-mtd_utils \ + board-build-wspr @echo "build tools board done" tools_board-clean: @@ -23,6 +24,7 @@ tools_board-clean: $(MAKE) -C $(SYSDRV_DIR)/tools/board/stressapptest distclean $(MAKE) -C $(SYSDRV_DIR)/tools/board/rk_ota distclean $(MAKE) -C $(SYSDRV_DIR)/tools/board/sysstat distclean + $(MAKE) -C $(SYSDRV_DIR)/tools/board/wspr distclean board-build-toolkits: $(MAKE) -C $(SYSDRV_DIR)/tools/board/toolkits @@ -74,7 +76,13 @@ ifeq ($(BOOT_MEDIUM),spi_nor) popd; endif endif + board-build-sysstat: ifeq ($(ENABLE_SYSSTAT),y) $(MAKE) -C $(SYSDRV_DIR)/tools/board/sysstat endif + +board-build-wspr: +ifeq ($(ENABLE_WSPR),y) + $(MAKE) -C $(SYSDRV_DIR)/tools/board/wspr +endif diff --git a/sysdrv/tools/board/wspr/Makefile b/sysdrv/tools/board/wspr/Makefile new file mode 100644 index 0000000000..225a406de3 --- /dev/null +++ b/sysdrv/tools/board/wspr/Makefile @@ -0,0 +1,26 @@ + +#ifeq ($(SYSDRV_PARAM), ) + WSPR_PARAM:=../../../Makefile.param + include $(WSPR_PARAM) +#endif + +export LC_ALL=C +SHELL:=/bin/bash + +CURRENT_DIR := $(shell pwd) +PKG_NAME := wspr +PKG_BIN := out + +all: + @test -f $(PKG_BIN)/usr/sbin/$(PKG_NAME)_noexist || (\ + mkdir -p $(CURRENT_DIR)/$(PKG_BIN)/usr/sbin; \ + $(SYSDRV_CROSS)-gcc -g main.c wspr.c -o $(CURRENT_DIR)/$(PKG_BIN)/usr/sbin/$(PKG_NAME); \ + ) + $(call MAROC_COPY_PKG_TO_SYSDRV_OUTPUT, $(SYSDRV_DIR_OUT_ROOTFS), $(PKG_BIN)) +# $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(shell pwd) $@ -j12 + +clean: distclean + +distclean: + -rm -rf $(PKG_NAME) $(PKG_BIN) + diff --git a/sysdrv/tools/board/wspr/README.md b/sysdrv/tools/board/wspr/README.md new file mode 100644 index 0000000000..73aa05fef3 --- /dev/null +++ b/sysdrv/tools/board/wspr/README.md @@ -0,0 +1,6 @@ +# WSPR Transmission Code + +This code is designed to transmit WSPR tones and is a largely rewritten version of: +https://github.com/alexf91/WSPR-Beacon/ + +The code directly interfaces with the clock sources through the unix filesystem. \ No newline at end of file diff --git a/sysdrv/tools/board/wspr/debug.h b/sysdrv/tools/board/wspr/debug.h new file mode 100644 index 0000000000..098e8fc8d8 --- /dev/null +++ b/sysdrv/tools/board/wspr/debug.h @@ -0,0 +1,32 @@ +/* + * Copyright 2018 Alexander Fasching + * + * 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 . + */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include +#include + +static inline void debug(const char *format, ...) { +#ifdef DEBUG + va_list args; + va_start(args, format); + vprintf(format, args); +#endif +} + +#endif /* DEBUG_H */ diff --git a/sysdrv/tools/board/wspr/main.c b/sysdrv/tools/board/wspr/main.c new file mode 100644 index 0000000000..8530259123 --- /dev/null +++ b/sysdrv/tools/board/wspr/main.c @@ -0,0 +1,164 @@ +/* + * Copyright 2023 Alistair Jordan + * + * 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 . + */ + #include + #include + #include + #include + #include + #include + #include "wspr.h" + #include + #include + #include // for clock_gettime() + + #define BASE_FREQ_LOW 14097000 + // high is end of 200hz spectrum - 6 hz to take account for transmission. + // Useful docs here: https://www.qrp-labs.com/images/ultimate3s/operation3.11.pdf + #define BASE_FREQ_HIGH 14097194 + +/** + * @brief Convert GPS coordinates into the 6 character grid locator. + * @param grid Buffer for the result + * @param lon Longitude in 15.16 signed fixed point format. + * @param lat Latitude in 15.16 signed fixed point format. + * @returns 0 on uccess. + */ +static int8_t gps_to_grid(char *grid, int32_t lon, int32_t lat) { + const int32_t SCALE = 65536; + + int32_t tmp_lon = lon + 180 * SCALE; + int32_t tmp_lat = lat + 90 * SCALE; + + if (tmp_lon < 0 || tmp_lon > 360 * SCALE) + return 1; + if (tmp_lat < 0 || tmp_lat > 180 * SCALE) + return 2; + + /* Convert the longitude */ + grid[0] = tmp_lon / (20 * SCALE) + 'A'; + tmp_lon %= (20 * SCALE); + + grid[2] = tmp_lon / (2 * SCALE) + '0'; + tmp_lon %= (2 * SCALE); + + grid[4] = tmp_lon * 12 / SCALE + 'a'; + + /* Convert the latitude */ + grid[1] = tmp_lat / (10 * SCALE) + 'A'; + tmp_lat %= (10 * SCALE); + + grid[3] = tmp_lat / SCALE + '0'; + tmp_lat %= SCALE; + + grid[5] = tmp_lat * 24 / SCALE + 'a'; + + return 0; +} + +int init_wspr(char *clk) { + char echo_command[100]; + // use system calls for the time being, direct file access seems to be throwing all kinds of errors, and I can't workout why.. + // this needs FIXING! + snprintf(echo_command, sizeof(echo_command), "echo 'enable clk0' > /proc/clk/enable"); + return (system(echo_command)); +} + +int send_tone(uint32_t base_freq, char *data, int index, char *clk) { + char echo_command[100]; + // set freq + // echo [clk_name] [rate(Hz)] > /proc/clk/rate + uint8_t tone = wspr_get_tone(data, index); + uint32_t f = base_freq + (3 * tone) / 2; + printf("Sending tone %i of %i at %i Hz\n",index+1,WSPR_SYMBOL_COUNT,f); + snprintf(echo_command, sizeof(echo_command), "echo 'clk0 %d' > /proc/clk/rate", tone); + return (system(echo_command)); +} + +int stop_wspr(char *clk) { + //disable clk0 + // echo disable [clk_name] > /proc/clk/enable + char echo_command[100]; + // use system calls for the time being, direct file access seems to be throwing all kinds of errors, and I can't workout why.. + // this needs FIXING! + snprintf(echo_command, sizeof(echo_command), "echo 'disable clk0' > /proc/clk/enable"); + return (system(echo_command)); +} + + int main(int argc, char *argv[]) { + char grid[6]; + char callsign[6];//"=2X0UAJ"; + char clk_name[5] = "clk0\0"; + int32_t lon = 0; + int32_t lat = 0; + uint8_t wspr_data[WSPR_BUFFER_SIZE]; + + if (argc < 4) { + printf("Usage is ./wspr lon[int32] lat[int32] CALLSIGN[char[6]]"); + return 1; + } + sscanf(argv[1],"%ld",&lon); + sscanf(argv[2],"%ld",&lat); + sscanf(argv[3],"%s.6",&callsign); + + if (gps_to_grid(grid, lon, lat) != 0) { + printf("Cannot translate long/lat\n"); + return 1; + } + if (wspr_encode(wspr_data, callsign, grid, 7) != 0) { + printf("Cannot encode WSPR message\n"); + return 1; + } + + printf("Decide random frequency within band for transmission\n"); + // Decide base frequency for transmission + srand(time(NULL)); + uint32_t base_freq = BASE_FREQ_LOW + (rand() % (BASE_FREQ_HIGH - BASE_FREQ_LOW)); + printf("Freqeuency decided at %i\n",base_freq); + + printf("Attempt to init %s for transmission\n", clk_name); + // Start transmit + if (init_wspr(clk_name) != 0) { + printf("%s unable to init!\n", clk_name); + return 1; + } + printf("%s init completete, start sending tones.\n", clk_name); + + int msec = 0; + struct timeval start, end; + long secs_used,micros_used; + //printf("Start time: %d\n", start_time); + for (int i=0; i < WSPR_SYMBOL_COUNT; i++) { + gettimeofday(&start, NULL); + if (send_tone(base_freq, wspr_data, i, clk_name) != 0) { + printf("Transmit failure\n"); + break; + } + gettimeofday(&end, NULL); + // account for how long it took for send_tone to work with drift correction + secs_used=(end.tv_sec - start.tv_sec); //avoid overflow by subtracting first + micros_used= ((secs_used*1000000) + end.tv_usec) - (start.tv_usec); + usleep((WSPR_SYMBOL_TIME*1000 - micros_used)); + } + + if (stop_wspr(clk_name) != 0) { + printf("Disconnect device immediately!\n"); + return 2; + } + + printf("Transmission Complete\n"); + +} \ No newline at end of file diff --git a/sysdrv/tools/board/wspr/wspr.c b/sysdrv/tools/board/wspr/wspr.c new file mode 100644 index 0000000000..fc9c675513 --- /dev/null +++ b/sysdrv/tools/board/wspr/wspr.c @@ -0,0 +1,192 @@ +/* + * Copyright 2018 Alexander Fasching + * + * 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 . + */ + +#include +#include +#include + +#include "debug.h" + + +const uint8_t sync_vector[] = { + 0x03, 0x71, 0xa4, 0x07, 0xa4, 0x40, 0xb3, 0x58, 0x58, 0x95, 0x34, 0x56, + 0x04, 0xc9, 0xcd, 0xe2, 0xa0, 0x0c, 0x58, 0x63, 0x00 +}; + + +/** + * Calculate the parity bits from the value of the shift register. + * The parity bits are returned in bit0 and bit1 of the returned value. + */ +static uint8_t get_parity(uint32_t * shiftreg0, uint32_t * shiftreg1) { + // use Galois-type shift register calculation + // for this to work, polynomials have to be bit-reversed + const uint32_t rev_poly0 = 0x8ACA0B4E; // invert lowest bit because it + const uint32_t rev_poly1 = 0xE23C8626; // is set by having Data put there + + uint8_t par0; + uint8_t par1; + + // if new data bit is 1, apply polynomial to SR + // new data bit is the same in both SRs, so we only need to test LSB once + if(*shiftreg0 & 1){ + *shiftreg0 ^= rev_poly0; + *shiftreg1 ^= rev_poly1; + } + // parity is at MSB + par0 = *shiftreg0 & 0x80000000 ? 1 : 0; + // implement shift in constant + par1 = *shiftreg1 & 0x80000000 ? 2 : 0; + return par1 | par0; +} + + +static void set_bit(uint8_t *bitvector, int index, uint8_t value) { + if (value) + bitvector[index >> 3] |= (1 << (index & 0x07)); + else + bitvector[index >> 3] &= ~(1 << (index & 0x07)); +} + +static uint8_t get_bit(const uint8_t *bitvector, int index) { + return !!(bitvector[index >> 3] & (1 << (index & 0x07))); +} + +static uint8_t bit_reverse(uint8_t byte) { + uint8_t result = 0; + for (int i = 0; i < 8; i++) { + if (byte & (1 << i)) + result |= (1 << (7 - i)); + } + return result; +} + + +//#ifdef DEBUG +static void print_vector(const char *name, const uint8_t *bitvec) { + printf("%s:", name); + for (int i = 0; i < 162; i++) { + if (i % 30 == 0) + printf("\n "); + printf("%d", get_bit(bitvec, i)); + + if (i % 30 != 29) + printf(" "); + } + printf("\n"); +} +//#endif + + +/** + * @brief Encode a WSPR message + * @param call Callsign as string in the format AABCCC + * @param loc 4 character QTH locator as string + * @param power Power in dBm + */ +int wspr_encode(uint8_t *result, const char *call, const char *loc, int power) { + uint32_t call_enc = 0; + + /* Map the callsign to integers between 0 and 36. */ + uint8_t call_mapped[6]; + for (int i = 0; i < 6; i++) { + if ('0' <= call[i] && call[i] <= '9') + call_mapped[i] = call[i] - '0'; + else if ('A' <= call[i] && call[i] <= 'Z') + call_mapped[i] = 10 + call[i] - 'A'; + else if (call[i] == ' ') + call_mapped[i] = 36; + else + return 1; + } + + /* Encode the mapped callsign. */ + call_enc += call_mapped[0]; + call_enc = 36 * call_enc + call_mapped[1]; + call_enc = 10 * call_enc + call_mapped[2]; + call_enc = 27 * call_enc + (call_mapped[3] - 10); + call_enc = 27 * call_enc + (call_mapped[4] - 10); + call_enc = 27 * call_enc + (call_mapped[5] - 10); + + /* Encode the grid locator. */ + uint32_t loc_pwr_enc = (179 - 10 * (loc[0] - 'A') - (loc[2] - '0')) * 180 + + 10 * (loc[1] - 'A') + (loc[3] - '0'); + /* Encode the transmit power. */ + loc_pwr_enc = 128 * loc_pwr_enc + power + 64; + + /* Forward Error Correction */ + uint32_t shiftreg0 = 0; + uint32_t shiftreg1 = 0; + uint8_t packed[21] = {0}; + + for (int i = 0; i < 50; i++) { + uint8_t bit; + if (i < 28) { + uint8_t index = 27 - i; + bit = !!(call_enc & (1UL << index)); + } else { + uint8_t index = 21 - (i - 28); + bit = !!(loc_pwr_enc & (1UL << index)); + } + + shiftreg0 = (shiftreg0 << 1) | bit; + shiftreg1 = (shiftreg1 << 1) | bit; + + uint32_t parity = get_parity(&shiftreg0, &shiftreg1); + uint8_t par0 = !!(parity & (1UL << 0)); + uint8_t par1 = !!(parity & (1UL << 1)); + + set_bit(packed, 2 * i, par0); + set_bit(packed, 2 * i + 1, par1); + } + for (int i = 50; i < 81; i++) { + shiftreg0 <<= 1; + shiftreg1 <<= 1; + + uint32_t parity = get_parity(&shiftreg0, &shiftreg1); + uint8_t par0 = !!(parity & (1 << 0)); + uint8_t par1 = !!(parity & (1 << 1)); + + set_bit(packed, 2 * i, par0); + set_bit(packed, 2 * i + 1, par1); + } + + /* Shuffle the result. */ + memset(result, 0, 21); + uint8_t p = 0; + for (int i = 0; i < 256; i++) { + uint8_t j = bit_reverse(i); + if (j < 162) { + set_bit(result, j, get_bit(packed, p)); + p++; + } + if (p >= 162) + break; + } + +//#ifdef DEBUG + //print_vector("Data symbols", result); + //printf("\n"); + //print_vector("Sync symbols", sync_vector); +//#endif + return 0; +} + + +uint8_t wspr_get_tone(const uint8_t *enc, uint8_t index) { + return get_bit(sync_vector, index) + 2 * get_bit(enc, index); +} diff --git a/sysdrv/tools/board/wspr/wspr.h b/sysdrv/tools/board/wspr/wspr.h new file mode 100644 index 0000000000..d2592ae729 --- /dev/null +++ b/sysdrv/tools/board/wspr/wspr.h @@ -0,0 +1,46 @@ +/* + * Copyright 2018 Alexander Fasching + * + * 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 . + */ + +#ifndef WSPR_H +#define WSPR_H + +#define WSPR_BUFFER_SIZE 21 +#define WSPR_SYMBOL_TIME 683 +#define WSPR_SYMBOL_COUNT 162 + + +/** + * @brief Encode a WSPR message + * @param result Buffer of size WSPR_BUFFER_LEN in which the result is stored. + * @param call Callsign as string in the format AABCCC + * @param loc 4 character QTH locator as string + * @param power Power in dBm + */ +int8_t wspr_encode(uint8_t *result, const char *call, const char *loc, int power); + + +/** + * @brief Combine the encoded message with the synchronization vector and + * return the tone that is sent over the air. + * @param enc Encoded message + * @param index Index of the symbol. + * @returns Number of the tone that is sent (0-3) + */ +uint8_t wspr_get_tone(const uint8_t *enc, uint8_t index); + + +#endif /* WSPR_H */