sforkowany z mirror/RS41ng
Add simple templating and rotating messages per mode. Finish support for FSQ modes. Attempt to get power-off button working (WIP).
rodzic
5d24cdcd7e
commit
f2ff6cb8a7
|
@ -0,0 +1,16 @@
|
||||||
|
* TODO: Add support for power-off via button and possibility disable power-off button
|
||||||
|
|
||||||
|
* TODO: create RTTY / FSK encoder for Si5351
|
||||||
|
* TODO: create RTTY / FSK encoder for Si4032
|
||||||
|
* TODO: create CW / OOK encoder -- the same one should work for both Si5351 and Si4032
|
||||||
|
|
||||||
|
* TODO: Add relevant information about RS41 hardware
|
||||||
|
* TODO: Add relevant links to other sources of information, e.g.:
|
||||||
|
* http://happysat.nl/RS-41/RS41.html
|
||||||
|
* https://github.com/darksidelemm/RS41HUP - 4FSK + 2FSK
|
||||||
|
* https://github.com/darksidelemm/RS41FOX - RS41-FOX - RS41 Amateur Radio Direction Finding (Foxhunting) Beacon
|
||||||
|
* https://github.com/bazjo/RS41_Hardware
|
||||||
|
* https://github.com/bazjo/RS41_Decoding
|
||||||
|
* https://destevez.net/2018/06/flashing-a-vaisala-rs41-radiosonde/
|
||||||
|
* https://destevez.net/2017/11/tracking-an-rs41-sgp-radiosonde-and-reporting-to-aprs/
|
||||||
|
* http://www.om3bc.com/docs/rs41/rs41_en.html
|
54
README.md
54
README.md
|
@ -15,7 +15,7 @@ The main features this firmware aims to implement are:
|
||||||
* Support for custom sensors via the external I²C bus
|
* Support for custom sensors via the external I²C bus
|
||||||
* Extensibility to allow easy addition of new digital modes
|
* Extensibility to allow easy addition of new digital modes
|
||||||
|
|
||||||
## Features currently working
|
## Features
|
||||||
|
|
||||||
* APRS on 70cm amateur radio band using the internal Si4032 radio transmitter
|
* APRS on 70cm amateur radio band using the internal Si4032 radio transmitter
|
||||||
* Bell 202 frequencies are generated via hardware PWM, but the symbol timing is created in a loop with delay
|
* Bell 202 frequencies are generated via hardware PWM, but the symbol timing is created in a loop with delay
|
||||||
|
@ -23,13 +23,15 @@ The main features this firmware aims to implement are:
|
||||||
* Digital mode beacons on HF/VHF frequencies using a Si5351 clock generator connected to the external I²C bus of the RS41 radiosonde
|
* Digital mode beacons on HF/VHF frequencies using a Si5351 clock generator connected to the external I²C bus of the RS41 radiosonde
|
||||||
* The JTEncode library provides JT65/JT9/JT4/FT8/WSPR/FSQ beacon transmissions. I've decoded FT8 and WSPR successfully.
|
* The JTEncode library provides JT65/JT9/JT4/FT8/WSPR/FSQ beacon transmissions. I've decoded FT8 and WSPR successfully.
|
||||||
* GPS-based scheduling is available for modes that require specific timing for transmissions
|
* GPS-based scheduling is available for modes that require specific timing for transmissions
|
||||||
* *NOTE:* It is most likely not possible to implement 1200 bps Bell 202 modulation for APRS using Si5351,
|
|
||||||
because the Si5351 chip is too slow to change the generated frequency
|
|
||||||
* External I²C bus sensor drivers
|
* External I²C bus sensor drivers
|
||||||
* Bosch BMP280 barometric pressure / temperature / humidity sensor
|
* Bosch BMP280 barometric pressure / temperature / humidity sensor
|
||||||
|
|
||||||
### Planned features
|
### Planned features
|
||||||
|
|
||||||
|
* Investigate possibility to implement 1200 bps Bell 202 modulation (and
|
||||||
|
possibly also 300 bps Bell 103 modulation) for APRS using Si5351,
|
||||||
|
this requires special handling to make Si5351 change frequency quickly
|
||||||
|
* See: https://github.com/etherkit/Si5351Arduino/issues/22
|
||||||
* CW (on-off keying) on both Si4032 (70cm) and Si5351 (HF + 2m)
|
* CW (on-off keying) on both Si4032 (70cm) and Si5351 (HF + 2m)
|
||||||
* RTTY on both Si4032 (70cm, non-standard shift) and Si5351 (HF + 2m) with configurable shift
|
* RTTY on both Si4032 (70cm, non-standard shift) and Si5351 (HF + 2m) with configurable shift
|
||||||
* Support for more I²C sensors
|
* Support for more I²C sensors
|
||||||
|
@ -176,7 +178,51 @@ the timings are mostly off for some unknown reason.
|
||||||
Currently, the Bell 202 modulation implementation uses hardware PWM to generate the individual tone frequencies,
|
Currently, the Bell 202 modulation implementation uses hardware PWM to generate the individual tone frequencies,
|
||||||
but the symbol timing is created in a loop with delay that was chosen carefully via experiments.
|
but the symbol timing is created in a loop with delay that was chosen carefully via experiments.
|
||||||
|
|
||||||
|
## Debugging APRS
|
||||||
|
|
||||||
|
Here are some tools and command-line examples to receive and debug APRS messages using an
|
||||||
|
SDR receiver. There are examples for using both [rx_tools](https://github.com/rxseger/rx_tools)
|
||||||
|
and [rtl-sdr](https://github.com/osmocom/rtl-sdr) tools to interface with the SDR receiver.
|
||||||
|
The example commands assume you are using an RTL-SDR dongle, but `rx_fm` (from `rx_tools`)
|
||||||
|
supports other types of devices too, as it's based on SoapySDR.
|
||||||
|
|
||||||
|
### Dire Wolf
|
||||||
|
|
||||||
|
[Dire Wolf](https://github.com/wb2osz/direwolf) can decode APRS (and lots of other digital modes)
|
||||||
|
from audio streams.
|
||||||
|
|
||||||
|
rx_tools:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rx_fm -f 432500000 -M fm -s 250000 -r 48000 -g 22 -d driver=rtlsdr - | direwolf -n 1 -D 1 -r 48000 -b 16 -
|
||||||
|
```
|
||||||
|
|
||||||
|
rtl-sdr:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtl_fm -f 432500000 -M fm -s 250k -r 48000 -g 22 - | direwolf -n 1 -D 1 -r 48000 -b 16 -
|
||||||
|
```
|
||||||
|
|
||||||
|
### SigPlay
|
||||||
|
|
||||||
|
[SigPlay](https://bk.gnarf.org/creativity/sigplay/) is a set of tools for DSP and signal processing.
|
||||||
|
SigPlay also includes a command-line tool to decode and print out raw data from Bell 202 encoding,
|
||||||
|
which is really useful, as it allows you to see the bytes that actually get transmitted --
|
||||||
|
even if the packet is not a valid APRS packet!
|
||||||
|
|
||||||
|
rx_tools:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rx_fm -f 432500000 -M fm -s 250000 -r 48000 -g 22 -d driver=rtlsdr - | ./aprs -
|
||||||
|
```
|
||||||
|
|
||||||
|
rtl-sdr:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rtl_fm -f 432500000 -M fm -s 250k -r 48000 -g 22 - | ./aprs -
|
||||||
|
```
|
||||||
|
|
||||||
# Authors
|
# Authors
|
||||||
|
|
||||||
* Authors of the [RS41HUP](https://github.com/df8oe/RS41HUP) project
|
* Original codebase: DF8OE and other authors of the [RS41HUP](https://github.com/df8oe/RS41HUP) project
|
||||||
* Mikael Nousiainen OH3BHX <oh3bhx@sral.fi>
|
* Mikael Nousiainen OH3BHX <oh3bhx@sral.fi>
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
# APRS debugging
|
|
||||||
|
|
||||||
## Direwolf
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rx_fm -f 434250000 -M fm -s 250000 -r 48000 -g 22 -d driver=rtlsdr - | direwolf -n 1 -D 1 -r 48000 -b 16 -
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rtl_fm -f 434250000 -M fm -s 250k -r 48000 -g 22 - | direwolf -n 1 -D 1 -r 48000 -b 16 -
|
|
||||||
```
|
|
||||||
|
|
||||||
## SigPlay
|
|
||||||
|
|
||||||
See: https://bk.gnarf.org/creativity/sigplay/
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rx_fm -f 434250000 -M fm -s 250000 -r 48000 -g 22 -d driver=rtlsdr - | ./aprs -
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rtl_fm -f 434250000 -M fm -s 250k -r 48000 -g 22 - | ./aprs -
|
|
||||||
```
|
|
|
@ -1,93 +0,0 @@
|
||||||
//********** RTTY
|
|
||||||
#define SEND_RTTY 1 // Set to 0 to disable RTTY
|
|
||||||
//**************RTTY Data Format**********************
|
|
||||||
// $$$<callsign>,<frame#>,[<hh:mm:ss>],[<latitude>,<longitude>],[<height>],[<speed>],[<rtty comment>],[<radio chip temperature (°C)>],[<battery voltage>],[<used gps satellites>],[<good gps datasets>,<bad gps datasets>,<gps fix flags>]*<CRC>
|
|
||||||
#define RTTY_CALLSIGN "OH3BHX" // put your RTTY callsign here, max. 15 characters
|
|
||||||
#define SEND_RTTY_TIME 1
|
|
||||||
#define SEND_RTTY_LATLON 1
|
|
||||||
#define SEND_RTTY_HEIGHT 1
|
|
||||||
#define SEND_RTTY_SPEED 1
|
|
||||||
#define SEND_RTTY_MESSAGE 1
|
|
||||||
#define SEND_RTTY_TEMPERATURE 1
|
|
||||||
#define SEND_RTTY_VOLTAGE 1
|
|
||||||
#define SEND_RTTY_SATELLITES 1
|
|
||||||
#define SEND_RTTY_GPSDATA 1
|
|
||||||
|
|
||||||
#define RTTY_COMMENT " Hello from the sky!" // max. 25 characters
|
|
||||||
#define RTTY_WWL 1 // Send WWL instead of the comment
|
|
||||||
// World Wide Locator pairs (precision)
|
|
||||||
#define PAIR_COUNT 4 // max. 6 (12 characters WWL)
|
|
||||||
|
|
||||||
#define RTTY_FREQUENCY 434.500f //Mhz middle frequency
|
|
||||||
|
|
||||||
//************RTTY Shift*********************** si4032
|
|
||||||
#define RTTY_DEVIATION 0x2 // RTTY shift = RTTY_DEVIATION x 270Hz
|
|
||||||
|
|
||||||
//************RTTY Speed*********************** si4032
|
|
||||||
#define RTTY_SPEED 75 // RTTY baudrate
|
|
||||||
|
|
||||||
//************rtty bits************************ si4032
|
|
||||||
#define RTTY_7BIT 1 // if 0 --> 5 bits
|
|
||||||
|
|
||||||
//************rtty stop bits******************* si4032
|
|
||||||
#define RTTY_USE_2_STOP_BITS 0
|
|
||||||
|
|
||||||
|
|
||||||
//********** APRS
|
|
||||||
#define SEND_APRS 1 // Set to 0 to disable APRS
|
|
||||||
#define APRS_CALLSIGN "OH3BHX" // put your APRS callsign here, 6 characters. If your callsign is shorter add spaces
|
|
||||||
#define APRS_SSID 'B' // put your APRS SSID here
|
|
||||||
// 0 --> Your primary station usually fixed and message capable
|
|
||||||
// 1 --> generic additional station, digi, mobile, wx, etc.
|
|
||||||
// 2 --> generic additional station, digi, mobile, wx, etc.
|
|
||||||
// 3 --> generic additional station, digi, mobile, wx, etc.
|
|
||||||
// 4 --> generic additional station, digi, mobile, wx, etc.
|
|
||||||
// 5 --> Other network sources (Dstar, Iphones, Blackberry's etc)
|
|
||||||
// 6 --> Special activity, Satellite ops, camping or 6 meters, etc.
|
|
||||||
// 7 --> walkie talkies, HT's or other human portable
|
|
||||||
// 8 --> boats, sailboats, RV's or second main mobile
|
|
||||||
// 9 --> Primary Mobile (usually message capable)
|
|
||||||
// A --> internet, Igates, echolink, winlink, AVRS, APRN, etc.
|
|
||||||
// B --> balloons, aircraft, spacecraft, etc.
|
|
||||||
// C --> APRStt, DTMF, RFID, devices, one-way trackers*, etc.
|
|
||||||
// D --> Weather stations
|
|
||||||
// E --> Truckers or generally full time drivers
|
|
||||||
// F --> generic additional station, digi, mobile, wx, etc.
|
|
||||||
|
|
||||||
#define APRS_FREQUENCY 432.500f //Mhz middle frequency
|
|
||||||
|
|
||||||
#define APRS_COMMENT " Hello from the sky!"
|
|
||||||
#define RTTY_TO_APRS_RATIO 5 //transmit APRS packet with each x RTTY packet
|
|
||||||
|
|
||||||
//********** Morse (CW)
|
|
||||||
#define SEND_MORSE 1 // Set to 0 to disable CW
|
|
||||||
#define MORSE_PREFIX "DE OH3BHX-11" // Start of the message
|
|
||||||
#define SEND_MORSE_WWL 1 // in <WW-locator>
|
|
||||||
#define SEND_MORSE_HEIGHT 0 // ASL <altitude>
|
|
||||||
#define SEND_MORSE_VOLTAGE 0 // bat <voltage>
|
|
||||||
#define MORSE_SUFFIX " +" // AR^ (end of transmission)
|
|
||||||
#define MORSE_WPM 20 // Speed in words per minute
|
|
||||||
#define RTTY_TO_MORSE_RATIO 1 // Transmit morse message with each x RTTY packet
|
|
||||||
|
|
||||||
//********* power definition**************************
|
|
||||||
#define TX_POWER 0 // PWR 0...7 0- MIN ... 7 - MAX
|
|
||||||
// 0 --> -1dBm
|
|
||||||
// 1 --> 2dBm
|
|
||||||
// 2 --> 5dBm
|
|
||||||
// 3 --> 8dBm
|
|
||||||
// 4 --> 11dBm
|
|
||||||
// 5 --> 14dBm
|
|
||||||
// 6 --> 17dBm
|
|
||||||
// 7 --> 20dBm
|
|
||||||
//****************************************************
|
|
||||||
|
|
||||||
// Switch sonde ON/OFF via Button
|
|
||||||
// If this is a flight you might prevent sonde from powered off by button
|
|
||||||
#define ALLOW_DISABLE_BY_BUTTON 1
|
|
||||||
|
|
||||||
//********** Frame Delay in msec**********************
|
|
||||||
#define TX_DELAY 5000
|
|
||||||
|
|
||||||
// Enable/disable LED blinking
|
|
||||||
// when set to 0, LEDs will stop blinking approx. 10 minutes after powering on the sonde
|
|
||||||
#define LED_ENABLED 1
|
|
|
@ -21,8 +21,8 @@ add_definitions(-D__ASSEMBLY__)
|
||||||
SET(LINKER_SCRIPT arm-gcc-link.ld)
|
SET(LINKER_SCRIPT arm-gcc-link.ld)
|
||||||
SET(COMMON_FLAGS " -mcpu=cortex-m3 -mthumb -Wall -ffunction-sections -fdata-sections -g -O3 -nostartfiles")
|
SET(COMMON_FLAGS " -mcpu=cortex-m3 -mthumb -Wall -ffunction-sections -fdata-sections -g -O3 -nostartfiles")
|
||||||
SET(CMAKE_CXX_FLAGS "${COMMON_FLAGS} -std=c++11")
|
SET(CMAKE_CXX_FLAGS "${COMMON_FLAGS} -std=c++11")
|
||||||
SET(CMAKE_C_FLAGS "${COMMON_FLAGS} -std=gnu99 -Dprintf=iprintf")
|
SET(CMAKE_C_FLAGS "${COMMON_FLAGS} -std=gnu99")
|
||||||
SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=${CMAKE_BINARY_DIR}/${PROJECT_NAME}.map -lstdc++ -O3 -Dprintf=iprintf -mcpu=cortex-m3 -mthumb -Wl,--gc-sections --specs=nano.specs -T ${LINKER_SCRIPT}")
|
SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=${CMAKE_BINARY_DIR}/${PROJECT_NAME}.map -lstdc++ -O3 -mcpu=cortex-m3 -mthumb -Wl,--gc-sections --specs=nano.specs -T ${LINKER_SCRIPT}")
|
||||||
# -u _printf_float
|
# -u _printf_float
|
||||||
#SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=${CMAKE_BINARY_DIR}/${PROJECT_NAME}.map -lstdc++ -O3 -mcpu=cortex-m3 -mthumb -Wl,--gc-sections --specs=rdimon.specs -lc -lrdimon -T ${LINKER_SCRIPT}")
|
#SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=${CMAKE_BINARY_DIR}/${PROJECT_NAME}.map -lstdc++ -O3 -mcpu=cortex-m3 -mthumb -Wl,--gc-sections --specs=rdimon.specs -lc -lrdimon -T ${LINKER_SCRIPT}")
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ size_t aprs_generate_position(uint8_t *payload, size_t length, telemetry_data *d
|
||||||
|
|
||||||
return snprintf((char *) payload,
|
return snprintf((char *) payload,
|
||||||
length,
|
length,
|
||||||
("%s%02d%02d.%02u%c%c%03d%02u.%02u%c%c%03d/%03d/A=%06ld/P%dS%dT%02ldV%04dC%02d%s"),
|
("%s%02d%02d.%02u%c%c%03d%02u.%02u%c%c%03d/%03d/A=%06d/P%dS%dT%02dV%04dC%02d%s"),
|
||||||
timestamp,
|
timestamp,
|
||||||
abs(la_degrees), la_minutes, la_h_minutes,
|
abs(la_degrees), la_minutes, la_h_minutes,
|
||||||
la_degrees > 0 ? 'N' : 'S',
|
la_degrees > 0 ? 'N' : 'S',
|
||||||
|
|
|
@ -83,7 +83,7 @@ uint16_t ax25_encode_packet_aprs(char *source, uint8_t source_ssid, char *destin
|
||||||
|
|
||||||
memset(header->source, ' ', sizeof(header->source));
|
memset(header->source, ' ', sizeof(header->source));
|
||||||
memcpy(header->source, source, 6);
|
memcpy(header->source, source, 6);
|
||||||
header->source_ssid = (uint8_t) (source_ssid > '@' ? source_ssid - 6 : source_ssid);;
|
header->source_ssid = (uint8_t) (source_ssid >= 'A' ? source_ssid - 7 : source_ssid);;
|
||||||
|
|
||||||
memset(header->destination, ' ', sizeof(header->destination));
|
memset(header->destination, ' ', sizeof(header->destination));
|
||||||
memcpy(header->destination, destination, 6);
|
memcpy(header->destination, destination, 6);
|
||||||
|
|
|
@ -125,10 +125,8 @@ typedef struct _jtencode_encoder {
|
||||||
uint8_t wspr_dbm;
|
uint8_t wspr_dbm;
|
||||||
|
|
||||||
char *fsq_callsign_from;
|
char *fsq_callsign_from;
|
||||||
char *fsq_callsign_to;
|
|
||||||
char fsq_command;
|
|
||||||
|
|
||||||
JTEncode jtencode;
|
JTEncode *jtencode;
|
||||||
uint16_t symbol_count;
|
uint16_t symbol_count;
|
||||||
uint32_t tone_spacing;
|
uint32_t tone_spacing;
|
||||||
uint32_t tone_delay;
|
uint32_t tone_delay;
|
||||||
|
@ -141,7 +139,7 @@ typedef struct _jtencode_encoder {
|
||||||
|
|
||||||
bool jtencode_encoder_new(fsk_encoder *encoder, size_t symbol_data_length, uint8_t *symbol_data,
|
bool jtencode_encoder_new(fsk_encoder *encoder, size_t symbol_data_length, uint8_t *symbol_data,
|
||||||
jtencode_mode_type mode_type, char *wspr_callsign, char *wspr_locator, uint8_t wspr_dbm,
|
jtencode_mode_type mode_type, char *wspr_callsign, char *wspr_locator, uint8_t wspr_dbm,
|
||||||
char *fsq_callsign_from, char *fsq_callsign_to, char fsq_command)
|
char *fsq_callsign_from)
|
||||||
{
|
{
|
||||||
jtencode_mode_descriptor *mode_descriptor = &jtencode_modes[mode_type];
|
jtencode_mode_descriptor *mode_descriptor = &jtencode_modes[mode_type];
|
||||||
if (mode_descriptor->symbol_count > 0) {
|
if (mode_descriptor->symbol_count > 0) {
|
||||||
|
@ -167,8 +165,8 @@ bool jtencode_encoder_new(fsk_encoder *encoder, size_t symbol_data_length, uint8
|
||||||
jte->wspr_dbm = wspr_dbm;
|
jte->wspr_dbm = wspr_dbm;
|
||||||
|
|
||||||
jte->fsq_callsign_from = fsq_callsign_from;
|
jte->fsq_callsign_from = fsq_callsign_from;
|
||||||
jte->fsq_callsign_to = fsq_callsign_to;
|
|
||||||
jte->fsq_command = fsq_command;
|
jte->jtencode = new JTEncode();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -176,6 +174,8 @@ bool jtencode_encoder_new(fsk_encoder *encoder, size_t symbol_data_length, uint8
|
||||||
void jtencode_encoder_destroy(fsk_encoder *encoder)
|
void jtencode_encoder_destroy(fsk_encoder *encoder)
|
||||||
{
|
{
|
||||||
if (encoder->priv != nullptr) {
|
if (encoder->priv != nullptr) {
|
||||||
|
auto *jte = (jtencode_encoder *) encoder->priv;
|
||||||
|
delete jte->jtencode;
|
||||||
free(encoder->priv);
|
free(encoder->priv);
|
||||||
encoder->priv = nullptr;
|
encoder->priv = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ uint32_t jtencode_encoder_get_symbol_delay(fsk_encoder *encoder)
|
||||||
void jtencode_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data)
|
void jtencode_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data)
|
||||||
{
|
{
|
||||||
auto *jte = (jtencode_encoder *) encoder->priv;
|
auto *jte = (jtencode_encoder *) encoder->priv;
|
||||||
JTEncode *jtencode = &jte->jtencode;
|
JTEncode *jtencode = jte->jtencode;
|
||||||
uint8_t *symbol_data = jte->symbol_data;
|
uint8_t *symbol_data = jte->symbol_data;
|
||||||
jtencode_mode_type mode_type = jte->mode_type;
|
jtencode_mode_type mode_type = jte->mode_type;
|
||||||
|
|
||||||
|
@ -233,8 +233,7 @@ void jtencode_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8
|
||||||
case JTENCODE_MODE_FSQ_3:
|
case JTENCODE_MODE_FSQ_3:
|
||||||
case JTENCODE_MODE_FSQ_4_5:
|
case JTENCODE_MODE_FSQ_4_5:
|
||||||
case JTENCODE_MODE_FSQ_6:
|
case JTENCODE_MODE_FSQ_6:
|
||||||
jtencode->fsq_dir_encode(jte->fsq_callsign_from, jte->fsq_callsign_to,
|
jtencode->fsq_encode(jte->fsq_callsign_from, (const char *) data, symbol_data);
|
||||||
jte->fsq_command, (const char *) data, symbol_data);
|
|
||||||
|
|
||||||
uint8_t j = 0;
|
uint8_t j = 0;
|
||||||
while (symbol_data[j++] != 0xff);
|
while (symbol_data[j++] != 0xff);
|
||||||
|
|
|
@ -21,7 +21,7 @@ typedef enum _jtencode_mode_type {
|
||||||
|
|
||||||
bool jtencode_encoder_new(fsk_encoder *encoder, size_t symbol_data_length, uint8_t *symbol_data,
|
bool jtencode_encoder_new(fsk_encoder *encoder, size_t symbol_data_length, uint8_t *symbol_data,
|
||||||
jtencode_mode_type mode_type, char *wspr_callsign, char *wspr_locator, uint8_t wspr_dbm,
|
jtencode_mode_type mode_type, char *wspr_callsign, char *wspr_locator, uint8_t wspr_dbm,
|
||||||
char *fsq_callsign_from, char *fsq_callsign_to, char fsq_command);
|
char *fsq_callsign_from);
|
||||||
void jtencode_encoder_destroy(fsk_encoder *encoder);
|
void jtencode_encoder_destroy(fsk_encoder *encoder);
|
||||||
|
|
||||||
extern fsk_encoder_api jtencode_fsk_encoder_api;
|
extern fsk_encoder_api jtencode_fsk_encoder_api;
|
||||||
|
|
|
@ -41,7 +41,12 @@
|
||||||
JTEncode::JTEncode(void)
|
JTEncode::JTEncode(void)
|
||||||
{
|
{
|
||||||
// Initialize the Reed-Solomon encoder
|
// Initialize the Reed-Solomon encoder
|
||||||
rs_inst = (struct rs *)(intptr_t)init_rs_int(6, 0x43, 3, 1, 51, 0);
|
rs_inst = (struct rs *) init_rs_int(6, 0x43, 3, 1, 51, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
JTEncode::~JTEncode()
|
||||||
|
{
|
||||||
|
free_rs_int(rs_inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -211,6 +211,7 @@ class JTEncode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
JTEncode(void);
|
JTEncode(void);
|
||||||
|
~JTEncode();
|
||||||
void jt65_encode(const char *, uint8_t *);
|
void jt65_encode(const char *, uint8_t *);
|
||||||
void jt9_encode(const char *, uint8_t *);
|
void jt9_encode(const char *, uint8_t *);
|
||||||
void jt4_encode(const char *, uint8_t *);
|
void jt4_encode(const char *, uint8_t *);
|
||||||
|
|
61
src/config.c
61
src/config.c
|
@ -1,6 +1,67 @@
|
||||||
|
#include <stdlib.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
bool leds_enabled = LEDS_ENABLE;
|
||||||
bool bmp280_enabled = SENSOR_BMP280_ENABLE;
|
bool bmp280_enabled = SENSOR_BMP280_ENABLE;
|
||||||
bool si5351_enabled = RADIO_SI5351_ENABLE;
|
bool si5351_enabled = RADIO_SI5351_ENABLE;
|
||||||
|
|
||||||
volatile bool system_initialized = false;
|
volatile bool system_initialized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed message lengths:
|
||||||
|
*
|
||||||
|
* APRS comment - Free text up to 127 chars
|
||||||
|
* FT8 - Free text up to 13 chars (Type 0.0 free text message, Type 0.5 telemetry message)
|
||||||
|
* JT65 - Free text up to 13 chars (Plaintext Type 6 message)
|
||||||
|
* JT9 - Free text up to 13 chars (Plaintext Type 6 message)
|
||||||
|
* JT4 - Free text up to 13 chars (Plaintext Type 6 message)
|
||||||
|
* FSQ - Call sign up to 20 chars, free text up to 130 chars
|
||||||
|
* WSPR - Call sign up to 6 chars, locator 4 chars, output power in dBm
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported variable references in templates:
|
||||||
|
*
|
||||||
|
* $cs - Call sign
|
||||||
|
* $loc4 - Locator (4 chars)
|
||||||
|
* $loc6 - Locator (6 chars)
|
||||||
|
* $loc8 - Locator (8 chars)
|
||||||
|
* $loc12 - Locator (12 chars)
|
||||||
|
* $bv - Battery voltage in millivolts (up to 4 chars)
|
||||||
|
* $te - External temperature in C (up to 3 chars)
|
||||||
|
* $ti - Internal temperature in C (up to 3 chars)
|
||||||
|
* $hu - Humidity percentage (up to 3 chars)
|
||||||
|
* $pr - Atmospheric pressure in millibars (up to 4 chars)
|
||||||
|
* $tow - GPS time of week in milliseconds
|
||||||
|
* $hh - Current hour (2 chars)
|
||||||
|
* $mm - Current minute (2 chars)
|
||||||
|
* $ss - Current second (2 chars)
|
||||||
|
* $sv - GPS satellites visible (up to 2 chars)
|
||||||
|
* $lat - Latitude in degrees * 1000 (up to 6 chars)
|
||||||
|
* $lon - Longitude in degrees * 1000 (up to 6 chars)
|
||||||
|
* $alt - Altitude in meters (up to 5 chars)
|
||||||
|
* $gs - Ground speed in km/h (up to 3 chars)
|
||||||
|
* $cl - Climb in m/s (up to 2 chars)
|
||||||
|
* $he - Heading in degrees (up to 3 chars)
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *aprs_comment_templates[] = {
|
||||||
|
" B$bu $teC $hu% $prmb $hh:$mm:$ss @ $tow ms - RS41ng radiosonde firmware test",
|
||||||
|
" $loc12 - RS41ng radiosonde firmware test",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
char *fsq_comment_templates[] = {
|
||||||
|
" $lat $lon, $alt m, $cl m/s, $gs km/h, $he deg - RS41ng radiosonde firmware test",
|
||||||
|
" $loc12, $teC $hu% $prmb $hh:$mm:$ss @ $tow ms - RS41ng radiosonde firmware test",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
char *ftjt_message_templates[] = {
|
||||||
|
"$cs $loc4",
|
||||||
|
"$loc12",
|
||||||
|
"$altm $cl",
|
||||||
|
"$bvmV $tiC",
|
||||||
|
"$hu% $prmb",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
84
src/config.h
84
src/config.h
|
@ -8,9 +8,13 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define CALLSIGN "OH3BHX"
|
||||||
|
|
||||||
#define RADIO_PAYLOAD_MAX_LENGTH 256
|
#define RADIO_PAYLOAD_MAX_LENGTH 256
|
||||||
#define RADIO_SYMBOL_DATA_MAX_LENGTH 512
|
#define RADIO_SYMBOL_DATA_MAX_LENGTH 512
|
||||||
#define APRS_COMMENT_MAX_LENGTH 128
|
#define RADIO_PAYLOAD_MESSAGE_MAX_LENGTH 128
|
||||||
|
|
||||||
|
#define LEDS_ENABLE true
|
||||||
|
|
||||||
#define SENSOR_BMP280_ENABLE false
|
#define SENSOR_BMP280_ENABLE false
|
||||||
|
|
||||||
|
@ -19,74 +23,82 @@
|
||||||
#define RADIO_POST_TRANSMIT_DELAY_MS 5000
|
#define RADIO_POST_TRANSMIT_DELAY_MS 5000
|
||||||
#define RADIO_TIME_SYNC_THRESHOLD_MS 1500
|
#define RADIO_TIME_SYNC_THRESHOLD_MS 1500
|
||||||
|
|
||||||
// Si4032 transmit power: 0..100%
|
/**
|
||||||
#define RADIO_SI4032_TX_POWER 100
|
* Si4032 transmit power: 0..7
|
||||||
|
* 0 = -1dBm, 1 = 2dBm, 2 = 5dBm, 3 = 8dBm, 4 = 11dBm, 5 = 14dBm, 6 = 17dBm, 7 = 20dBm
|
||||||
|
*/
|
||||||
|
#define RADIO_SI4032_TX_POWER 7
|
||||||
#define RADIO_SI4032_TX_FREQUENCY_CW 432060000
|
#define RADIO_SI4032_TX_FREQUENCY_CW 432060000
|
||||||
#define RADIO_SI4032_TX_FREQUENCY_RTTY 432060000
|
#define RADIO_SI4032_TX_FREQUENCY_RTTY 432060000
|
||||||
#define RADIO_SI4032_TX_FREQUENCY_APRS 432500000
|
#define RADIO_SI4032_TX_FREQUENCY_APRS_1200 432500000
|
||||||
|
|
||||||
#define RADIO_SI5351_TX_POWER 100
|
/**
|
||||||
#define RADIO_SI5351_TX_FREQUENCY_JT9 14078700UL
|
* Si5351 transmit power: 0..3
|
||||||
#define RADIO_SI5351_TX_FREQUENCY_JT65 14078300UL
|
* Si5351 drive strength: 0 = 2mA, 1 = 4mA, 2 = 6mA, 3 = 8mA
|
||||||
#define RADIO_SI5351_TX_FREQUENCY_JT4 14078500UL
|
*/
|
||||||
|
#define RADIO_SI5351_TX_POWER 3
|
||||||
|
#define RADIO_SI5351_TX_FREQUENCY_JT9 14085000UL // Was: 14078700UL
|
||||||
|
#define RADIO_SI5351_TX_FREQUENCY_JT65 14085000UL // Was: 14078300UL
|
||||||
|
#define RADIO_SI5351_TX_FREQUENCY_JT4 14085000UL // Was: 14078500UL
|
||||||
#define RADIO_SI5351_TX_FREQUENCY_WSPR 14085000UL // Was: 14097200UL
|
#define RADIO_SI5351_TX_FREQUENCY_WSPR 14085000UL // Was: 14097200UL
|
||||||
#define RADIO_SI5351_TX_FREQUENCY_FSQ 7105350UL // Base freq is 1350 Hz higher than dial freq in USB
|
#define RADIO_SI5351_TX_FREQUENCY_FSQ 14085000UL // Was: 7105350UL // Base freq is 1350 Hz higher than dial freq in USB
|
||||||
#define RADIO_SI5351_TX_FREQUENCY_FT8 14085000UL // Was: 14075000UL
|
#define RADIO_SI5351_TX_FREQUENCY_FT8 14085000UL // Was: 14075000UL
|
||||||
|
|
||||||
#define LOCATOR_PAIR_COUNT_FULL 6 // max. 6 (12 characters WWL)
|
#define LOCATOR_PAIR_COUNT_FULL 6 // max. 6 (12 characters WWL)
|
||||||
|
|
||||||
#define WSPR_CALLSIGN "OH3BHX"
|
#define WSPR_CALLSIGN CALLSIGN
|
||||||
#define WSPR_LOCATOR_FIXED_ENABLED false
|
#define WSPR_LOCATOR_FIXED_ENABLED false
|
||||||
#define WSPR_LOCATOR_FIXED "AA00"
|
#define WSPR_LOCATOR_FIXED "AA00"
|
||||||
#define WSPR_DBM 10
|
#define WSPR_DBM 10
|
||||||
|
|
||||||
#define FT8_CALLSIGN "OH3BHX"
|
#define FSQ_CALLSIGN_FROM CALLSIGN
|
||||||
#define FT8_LOCATOR_FIXED_ENABLED false
|
|
||||||
#define FT8_LOCATOR_FIXED "AA00"
|
|
||||||
|
|
||||||
#define FSQ_CALLSIGN_FROM "OH3BHX"
|
|
||||||
#define FSQ_CALLSIGN_TO "N0CALL"
|
|
||||||
#define FSQ_COMMAND ' '
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* APRS SSID:
|
* APRS SSID:
|
||||||
*
|
*
|
||||||
* 0 Your primary station usually fixed and message capable
|
* '0' = (-0) Your primary station usually fixed and message capable
|
||||||
* 1 generic additional station, digi, mobile, wx, etc
|
* '1' = (-1) generic additional station, digi, mobile, wx, etc
|
||||||
* 2 generic additional station, digi, mobile, wx, etc
|
* '2' = (-2) generic additional station, digi, mobile, wx, etc
|
||||||
* 3 generic additional station, digi, mobile, wx, etc
|
* '3' = (-3) generic additional station, digi, mobile, wx, etc
|
||||||
* 4 generic additional station, digi, mobile, wx, etc
|
* '4' = (-4) generic additional station, digi, mobile, wx, etc
|
||||||
* 5 Other networks (Dstar, Iphones, Androids, Blackberry's etc)
|
* '5' = (-5) Other networks (Dstar, Iphones, Androids, Blackberry's etc)
|
||||||
* 6 Special activity, Satellite ops, camping or 6 meters, etc
|
* '6' = (-6) Special activity, Satellite ops, camping or 6 meters, etc
|
||||||
* 7 walkie talkies, HT's or other human portable
|
* '7' = (-7) walkie talkies, HT's or other human portable
|
||||||
* 8 boats, sailboats, RV's or second main mobile
|
* '8' = (-8) boats, sailboats, RV's or second main mobile
|
||||||
* 9 Primary Mobile (usually message capable)
|
* '9' = (-9) Primary Mobile (usually message capable)
|
||||||
* A internet, Igates, echolink, winlink, AVRS, APRN, etc
|
* 'A' = (-10) internet, Igates, echolink, winlink, AVRS, APRN, etc
|
||||||
* B balloons, aircraft, spacecraft, etc
|
* 'B' = (-11) balloons, aircraft, spacecraft, etc
|
||||||
* C APRStt, DTMF, RFID, devices, one-way trackers*, etc
|
* 'C' = (-12) APRStt, DTMF, RFID, devices, one-way trackers*, etc
|
||||||
* D Weather stations
|
* 'D' = (-13) Weather stations
|
||||||
* E Truckers or generally full time drivers
|
* 'E' = (-14) Truckers or generally full time drivers
|
||||||
* F generic additional station, digi, mobile, wx, etc
|
* 'F' = (-15) generic additional station, digi, mobile, wx, etc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define APRS_CALLSIGN "OH3BHX"
|
#define APRS_CALLSIGN CALLSIGN
|
||||||
#define APRS_SSID 'B'
|
#define APRS_SSID 'B'
|
||||||
// See APRS symbol table documentation in: http://www.aprs.org/symbols/symbolsX.txt
|
// See APRS symbol table documentation in: http://www.aprs.org/symbols/symbolsX.txt
|
||||||
#define APRS_SYMBOL_TABLE '/' // '/' denotes primary and '\\' denotes alternate APRS symbol table
|
#define APRS_SYMBOL_TABLE '/' // '/' denotes primary and '\\' denotes alternate APRS symbol table
|
||||||
#define APRS_SYMBOL '['
|
#define APRS_SYMBOL 'O'
|
||||||
#define APRS_COMMENT " RS41ng custom radiosonde firmware testing"
|
#define APRS_COMMENT " RS41ng radiosonde firmware test"
|
||||||
#define APRS_RELAYS "WIDE1-1,WIDE2-1"
|
#define APRS_RELAYS "WIDE1-1,WIDE2-1"
|
||||||
#define APRS_DESTINATION "APZ41N"
|
#define APRS_DESTINATION "APZ41N"
|
||||||
#define APRS_DESTINATION_SSID '0'
|
#define APRS_DESTINATION_SSID '0'
|
||||||
|
|
||||||
|
// TODO: RTTY and CW settings (once modes are implemented)
|
||||||
|
|
||||||
#define RTTY_LOCATOR_PAIR_COUNT 4 // max. 6 (12 characters WWL)
|
#define RTTY_LOCATOR_PAIR_COUNT 4 // max. 6 (12 characters WWL)
|
||||||
#define RTTY_7BIT 1 // if 0 --> 5 bits
|
#define RTTY_7BIT 1 // if 0 --> 5 bits
|
||||||
|
|
||||||
#define CW_LOCATOR_PAIR_COUNT 4 // max. 6 (12 characters WWL)
|
#define CW_LOCATOR_PAIR_COUNT 4 // max. 6 (12 characters WWL)
|
||||||
|
|
||||||
|
extern bool leds_enabled;
|
||||||
extern bool bmp280_enabled;
|
extern bool bmp280_enabled;
|
||||||
extern bool si5351_enabled;
|
extern bool si5351_enabled;
|
||||||
|
|
||||||
extern volatile bool system_initialized;
|
extern volatile bool system_initialized;
|
||||||
|
|
||||||
|
extern char *aprs_comment_templates[];
|
||||||
|
extern char *fsq_comment_templates[];
|
||||||
|
extern char *ftjt_message_templates[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -78,13 +78,15 @@ static void gpio_init()
|
||||||
GPIO_Init(GPIOA, &gpio_init);
|
GPIO_Init(GPIOA, &gpio_init);
|
||||||
|
|
||||||
// Battery voltage (analog)
|
// Battery voltage (analog)
|
||||||
gpio_init.GPIO_Mode = GPIO_Mode_AIN;
|
|
||||||
gpio_init.GPIO_Pin = GPIO_Pin_5;
|
gpio_init.GPIO_Pin = GPIO_Pin_5;
|
||||||
|
gpio_init.GPIO_Mode = GPIO_Mode_AIN;
|
||||||
|
gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
|
||||||
GPIO_Init(GPIOA, &gpio_init);
|
GPIO_Init(GPIOA, &gpio_init);
|
||||||
|
|
||||||
// Button state (analog)
|
// Button state (analog)
|
||||||
gpio_init.GPIO_Mode = GPIO_Mode_AIN;
|
|
||||||
gpio_init.GPIO_Pin = GPIO_Pin_6;
|
gpio_init.GPIO_Pin = GPIO_Pin_6;
|
||||||
|
gpio_init.GPIO_Mode = GPIO_Mode_AIN;
|
||||||
|
gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
|
||||||
GPIO_Init(GPIOA, &gpio_init);
|
GPIO_Init(GPIOA, &gpio_init);
|
||||||
|
|
||||||
// LEDs
|
// LEDs
|
||||||
|
@ -156,6 +158,11 @@ uint16_t system_get_battery_voltage_millivolts()
|
||||||
return (uint16_t) (((float) dma_buffer_adc[0]) * 10.0f * 600.0f / 4096.0f);
|
return (uint16_t) (((float) dma_buffer_adc[0]) * 10.0f * 600.0f / 4096.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t system_get_button_adc_value()
|
||||||
|
{
|
||||||
|
return (uint16_t) dma_buffer_adc[1];
|
||||||
|
}
|
||||||
|
|
||||||
void system_shutdown()
|
void system_shutdown()
|
||||||
{
|
{
|
||||||
GPIO_SetBits(GPIOA, GPIO_Pin_12);
|
GPIO_SetBits(GPIOA, GPIO_Pin_12);
|
||||||
|
@ -163,10 +170,13 @@ void system_shutdown()
|
||||||
|
|
||||||
void system_handle_button()
|
void system_handle_button()
|
||||||
{
|
{
|
||||||
static uint16_t button_pressed_threshold = 0;
|
static uint16_t button_pressed_threshold = 2000;
|
||||||
static bool shutdown = false;
|
static bool shutdown = false;
|
||||||
|
|
||||||
uint16_t current_value = dma_buffer_adc[1];
|
// ~1450-1600 - button up
|
||||||
|
// ~1780-1850 - button down
|
||||||
|
|
||||||
|
uint16_t current_value = system_get_button_adc_value();
|
||||||
|
|
||||||
if (current_value > button_pressed_threshold) {
|
if (current_value > button_pressed_threshold) {
|
||||||
button_pressed++;
|
button_pressed++;
|
||||||
|
@ -306,6 +316,6 @@ void TIM4_IRQHandler(void)
|
||||||
|
|
||||||
system_handle_timer_tick();
|
system_handle_timer_tick();
|
||||||
|
|
||||||
// TODO: system_handle_button();
|
// TODO: fix detection of button state and enable: system_handle_button();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#define SYSTEM_SCHEDULER_TIMER_TICKS_PER_SECOND 10000
|
#define SYSTEM_SCHEDULER_TIMER_TICKS_PER_SECOND 10000
|
||||||
|
|
||||||
void system_init();
|
void system_init();
|
||||||
|
void system_shutdown();
|
||||||
uint32_t system_get_tick();
|
uint32_t system_get_tick();
|
||||||
void system_disable_tick();
|
void system_disable_tick();
|
||||||
void system_enable_tick();
|
void system_enable_tick();
|
||||||
|
@ -17,6 +18,7 @@ void system_enable_irq();
|
||||||
void system_set_green_led(bool enabled);
|
void system_set_green_led(bool enabled);
|
||||||
void system_set_red_led(bool enabled);
|
void system_set_red_led(bool enabled);
|
||||||
uint16_t system_get_battery_voltage_millivolts();
|
uint16_t system_get_battery_voltage_millivolts();
|
||||||
|
uint16_t system_get_button_adc_value();
|
||||||
|
|
||||||
extern void (*system_handle_timer_tick)();
|
extern void (*system_handle_timer_tick)();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include "hal/system.h"
|
#include "hal/system.h"
|
||||||
#include "hal/i2c.h"
|
#include "hal/i2c.h"
|
||||||
#include "hal/spi.h"
|
#include "hal/spi.h"
|
||||||
|
@ -30,6 +29,7 @@ void handle_timer_tick()
|
||||||
ubxg6010_get_current_gps_data(¤t_gps_data);
|
ubxg6010_get_current_gps_data(¤t_gps_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (leds_enabled) {
|
||||||
// Blink fast until GPS fix is acquired
|
// Blink fast until GPS fix is acquired
|
||||||
if (counter % (SYSTEM_SCHEDULER_TIMER_TICKS_PER_SECOND / 4) == 0) {
|
if (counter % (SYSTEM_SCHEDULER_TIMER_TICKS_PER_SECOND / 4) == 0) {
|
||||||
if (current_gps_data.fix >= 3) {
|
if (current_gps_data.fix >= 3) {
|
||||||
|
@ -43,6 +43,7 @@ void handle_timer_tick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
@ -90,8 +91,13 @@ gps_init:
|
||||||
|
|
||||||
log_info("System initialized!\n");
|
log_info("System initialized!\n");
|
||||||
|
|
||||||
|
if (leds_enabled) {
|
||||||
system_set_green_led(true);
|
system_set_green_led(true);
|
||||||
system_set_red_led(false);
|
system_set_red_led(false);
|
||||||
|
} else {
|
||||||
|
system_set_green_led(false);
|
||||||
|
system_set_red_led(false);
|
||||||
|
}
|
||||||
|
|
||||||
system_initialized = true;
|
system_initialized = true;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "telemetry.h"
|
#include "telemetry.h"
|
||||||
|
|
||||||
typedef struct _payload_encoder {
|
typedef struct _payload_encoder {
|
||||||
uint16_t (*encode)(uint8_t *payload, uint16_t length, telemetry_data *data);
|
uint16_t (*encode)(uint8_t *payload, uint16_t length, telemetry_data *data, char *message);
|
||||||
} payload_encoder;
|
} payload_encoder;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
121
src/radio.c
121
src/radio.c
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "template.h"
|
||||||
#include "hal/system.h"
|
#include "hal/system.h"
|
||||||
#include "hal/delay.h"
|
#include "hal/delay.h"
|
||||||
#include "hal/usart_gps.h"
|
#include "hal/usart_gps.h"
|
||||||
|
@ -13,30 +14,27 @@
|
||||||
#include "radio_si5351.h"
|
#include "radio_si5351.h"
|
||||||
#include "radio_payload_aprs.h"
|
#include "radio_payload_aprs.h"
|
||||||
#include "radio_payload_wspr.h"
|
#include "radio_payload_wspr.h"
|
||||||
#include "radio_payload_ft8.h"
|
#include "radio_payload_jtencode.h"
|
||||||
|
#include "radio_payload_fsq.h"
|
||||||
// TODO: create RTTY / FSK encoder for Si5351
|
|
||||||
// TODO: create RTTY / FSK encoder for Si4032
|
|
||||||
// TODO: create CW / OOK encoder -- the same one should work for both Si5351 and Si4032
|
|
||||||
|
|
||||||
radio_transmit_entry radio_transmit_schedule[] = {
|
radio_transmit_entry radio_transmit_schedule[] = {
|
||||||
{
|
{
|
||||||
.enabled = true,
|
.enabled = true,
|
||||||
.radio_type = RADIO_TYPE_SI4032,
|
.radio_type = RADIO_TYPE_SI4032,
|
||||||
.data_mode = RADIO_DATA_MODE_APRS,
|
.data_mode = RADIO_DATA_MODE_APRS_1200,
|
||||||
.time_sync_seconds = 0,
|
.time_sync_seconds = 0,
|
||||||
.time_sync_seconds_offset = 0,
|
.time_sync_seconds_offset = 0,
|
||||||
.frequency = RADIO_SI4032_TX_FREQUENCY_APRS,
|
.frequency = RADIO_SI4032_TX_FREQUENCY_APRS_1200,
|
||||||
.tx_power = RADIO_SI4032_TX_POWER,
|
.tx_power = RADIO_SI4032_TX_POWER,
|
||||||
.symbol_rate = 1200,
|
.symbol_rate = 1200,
|
||||||
.payload_encoder = &radio_aprs_payload_encoder,
|
.payload_encoder = &radio_aprs_payload_encoder,
|
||||||
.fsk_encoder_api = &bell_fsk_encoder_api,
|
.fsk_encoder_api = &bell_fsk_encoder_api,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.enabled = true,
|
.enabled = false,
|
||||||
.radio_type = RADIO_TYPE_SI5351,
|
.radio_type = RADIO_TYPE_SI5351,
|
||||||
.data_mode = RADIO_DATA_MODE_FT8,
|
.data_mode = RADIO_DATA_MODE_FT8,
|
||||||
.time_sync_seconds = 5,
|
.time_sync_seconds = 15,
|
||||||
.time_sync_seconds_offset = 0,
|
.time_sync_seconds_offset = 0,
|
||||||
.frequency = RADIO_SI5351_TX_FREQUENCY_FT8,
|
.frequency = RADIO_SI5351_TX_FREQUENCY_FT8,
|
||||||
.tx_power = RADIO_SI5351_TX_POWER,
|
.tx_power = RADIO_SI5351_TX_POWER,
|
||||||
|
@ -47,8 +45,56 @@ radio_transmit_entry radio_transmit_schedule[] = {
|
||||||
{
|
{
|
||||||
.enabled = false,
|
.enabled = false,
|
||||||
.radio_type = RADIO_TYPE_SI5351,
|
.radio_type = RADIO_TYPE_SI5351,
|
||||||
.time_sync_seconds = 300,
|
.data_mode = RADIO_DATA_MODE_JT9,
|
||||||
|
.time_sync_seconds = 60,
|
||||||
|
.time_sync_seconds_offset = 1,
|
||||||
|
.frequency = RADIO_SI5351_TX_FREQUENCY_JT9,
|
||||||
|
.tx_power = RADIO_SI5351_TX_POWER,
|
||||||
|
.payload_encoder = &radio_jt9_payload_encoder,
|
||||||
|
.fsk_encoder_api = &jtencode_fsk_encoder_api,
|
||||||
|
.jtencode_mode_type = JTENCODE_MODE_JT9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.enabled = false,
|
||||||
|
.radio_type = RADIO_TYPE_SI5351,
|
||||||
|
.data_mode = RADIO_DATA_MODE_JT4,
|
||||||
|
.time_sync_seconds = 60,
|
||||||
|
.time_sync_seconds_offset = 1,
|
||||||
|
.frequency = RADIO_SI5351_TX_FREQUENCY_JT4,
|
||||||
|
.tx_power = RADIO_SI5351_TX_POWER,
|
||||||
|
.payload_encoder = &radio_jt4_payload_encoder,
|
||||||
|
.fsk_encoder_api = &jtencode_fsk_encoder_api,
|
||||||
|
.jtencode_mode_type = JTENCODE_MODE_JT4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.enabled = false,
|
||||||
|
.radio_type = RADIO_TYPE_SI5351,
|
||||||
|
.data_mode = RADIO_DATA_MODE_JT65,
|
||||||
|
.time_sync_seconds = 60,
|
||||||
|
.time_sync_seconds_offset = 1,
|
||||||
|
.frequency = RADIO_SI5351_TX_FREQUENCY_JT65,
|
||||||
|
.tx_power = RADIO_SI5351_TX_POWER,
|
||||||
|
.payload_encoder = &radio_jt65_payload_encoder,
|
||||||
|
.fsk_encoder_api = &jtencode_fsk_encoder_api,
|
||||||
|
.jtencode_mode_type = JTENCODE_MODE_JT65,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.enabled = true,
|
||||||
|
.radio_type = RADIO_TYPE_SI5351,
|
||||||
|
.data_mode = RADIO_DATA_MODE_FSQ_6,
|
||||||
|
.time_sync_seconds = 0,
|
||||||
.time_sync_seconds_offset = 0,
|
.time_sync_seconds_offset = 0,
|
||||||
|
.frequency = RADIO_SI5351_TX_FREQUENCY_FSQ,
|
||||||
|
.tx_power = RADIO_SI5351_TX_POWER,
|
||||||
|
.payload_encoder = &radio_fsq_payload_encoder,
|
||||||
|
.fsk_encoder_api = &jtencode_fsk_encoder_api,
|
||||||
|
.jtencode_mode_type = JTENCODE_MODE_FSQ_6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.enabled = false,
|
||||||
|
.radio_type = RADIO_TYPE_SI5351,
|
||||||
|
.time_sync_seconds = 120,
|
||||||
|
.time_sync_seconds_offset = 1,
|
||||||
.data_mode = RADIO_DATA_MODE_WSPR,
|
.data_mode = RADIO_DATA_MODE_WSPR,
|
||||||
.frequency = RADIO_SI5351_TX_FREQUENCY_WSPR,
|
.frequency = RADIO_SI5351_TX_FREQUENCY_WSPR,
|
||||||
.tx_power = RADIO_SI5351_TX_POWER,
|
.tx_power = RADIO_SI5351_TX_POWER,
|
||||||
|
@ -76,6 +122,8 @@ static radio_transmit_entry *radio_start_transmit_entry = NULL;
|
||||||
|
|
||||||
static uint32_t radio_previous_time_sync_scheduled = 0;
|
static uint32_t radio_previous_time_sync_scheduled = 0;
|
||||||
|
|
||||||
|
char radio_current_payload_message[RADIO_PAYLOAD_MESSAGE_MAX_LENGTH];
|
||||||
|
|
||||||
uint8_t radio_current_payload[RADIO_PAYLOAD_MAX_LENGTH];
|
uint8_t radio_current_payload[RADIO_PAYLOAD_MAX_LENGTH];
|
||||||
uint16_t radio_current_payload_length = 0;
|
uint16_t radio_current_payload_length = 0;
|
||||||
|
|
||||||
|
@ -127,8 +175,16 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
|
||||||
|
|
||||||
telemetry_collect(¤t_telemetry_data);
|
telemetry_collect(¤t_telemetry_data);
|
||||||
|
|
||||||
|
if (entry->messages != NULL && entry->message_count > 0) {
|
||||||
|
template_replace(radio_current_payload_message, sizeof(radio_current_payload_message),
|
||||||
|
entry->messages[entry->current_message_index], ¤t_telemetry_data);
|
||||||
|
} else {
|
||||||
|
radio_current_payload_message[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
radio_current_payload_length = entry->payload_encoder->encode(
|
radio_current_payload_length = entry->payload_encoder->encode(
|
||||||
radio_current_payload, sizeof(radio_current_payload), ¤t_telemetry_data);
|
radio_current_payload, sizeof(radio_current_payload),
|
||||||
|
¤t_telemetry_data, radio_current_payload_message);
|
||||||
|
|
||||||
log_info("Full payload length: %d\n", radio_current_payload_length);
|
log_info("Full payload length: %d\n", radio_current_payload_length);
|
||||||
|
|
||||||
|
@ -150,7 +206,7 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
|
||||||
break;
|
break;
|
||||||
case RADIO_DATA_MODE_RTTY:
|
case RADIO_DATA_MODE_RTTY:
|
||||||
break;
|
break;
|
||||||
case RADIO_DATA_MODE_APRS:
|
case RADIO_DATA_MODE_APRS_1200:
|
||||||
// TODO: make bell tones and flag field count configurable
|
// TODO: make bell tones and flag field count configurable
|
||||||
bell_encoder_new(&entry->fsk_encoder, entry->symbol_rate, BELL_FLAG_FIELD_COUNT_1200, bell202_tones);
|
bell_encoder_new(&entry->fsk_encoder, entry->symbol_rate, BELL_FLAG_FIELD_COUNT_1200, bell202_tones);
|
||||||
radio_shared_state.radio_current_symbol_rate = entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder);
|
radio_shared_state.radio_current_symbol_rate = entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder);
|
||||||
|
@ -176,8 +232,7 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
success = jtencode_encoder_new(&entry->fsk_encoder, sizeof(radio_current_symbol_data), radio_current_symbol_data,
|
success = jtencode_encoder_new(&entry->fsk_encoder, sizeof(radio_current_symbol_data), radio_current_symbol_data,
|
||||||
entry->jtencode_mode_type, WSPR_CALLSIGN, locator,
|
entry->jtencode_mode_type, WSPR_CALLSIGN, locator, WSPR_DBM, FSQ_CALLSIGN_FROM);
|
||||||
WSPR_DBM, FSQ_CALLSIGN_FROM, FSQ_CALLSIGN_TO, FSQ_COMMAND);
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -214,7 +269,9 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
|
||||||
|
|
||||||
log_info("TX start\n");
|
log_info("TX start\n");
|
||||||
|
|
||||||
|
if (leds_enabled) {
|
||||||
system_set_red_led(true);
|
system_set_red_led(true);
|
||||||
|
}
|
||||||
|
|
||||||
radio_shared_state.radio_transmission_active = true;
|
radio_shared_state.radio_transmission_active = true;
|
||||||
|
|
||||||
|
@ -261,7 +318,7 @@ static bool radio_stop_transmit(radio_transmit_entry *entry)
|
||||||
break;
|
break;
|
||||||
case RADIO_DATA_MODE_RTTY:
|
case RADIO_DATA_MODE_RTTY:
|
||||||
break;
|
break;
|
||||||
case RADIO_DATA_MODE_APRS:
|
case RADIO_DATA_MODE_APRS_1200:
|
||||||
bell_encoder_destroy(&entry->fsk_encoder);
|
bell_encoder_destroy(&entry->fsk_encoder);
|
||||||
break;
|
break;
|
||||||
case RADIO_DATA_MODE_WSPR:
|
case RADIO_DATA_MODE_WSPR:
|
||||||
|
@ -280,7 +337,9 @@ static bool radio_stop_transmit(radio_transmit_entry *entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
usart_gps_enable(true);
|
usart_gps_enable(true);
|
||||||
|
if (leds_enabled) {
|
||||||
system_set_red_led(false);
|
system_set_red_led(false);
|
||||||
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
@ -314,6 +373,9 @@ static void radio_reset_transmit_delay_counter()
|
||||||
|
|
||||||
static void radio_next_transmit_entry()
|
static void radio_next_transmit_entry()
|
||||||
{
|
{
|
||||||
|
radio_current_transmit_entry->current_message_index =
|
||||||
|
(radio_current_transmit_entry->current_message_index + 1) % radio_current_transmit_entry->message_count;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
radio_current_transmit_entry_index = (radio_current_transmit_entry_index + 1) % radio_transmit_entry_count;
|
radio_current_transmit_entry_index = (radio_current_transmit_entry_index + 1) % radio_transmit_entry_count;
|
||||||
radio_current_transmit_entry = &radio_transmit_schedule[radio_current_transmit_entry_index];
|
radio_current_transmit_entry = &radio_transmit_schedule[radio_current_transmit_entry_index];
|
||||||
|
@ -503,6 +565,35 @@ void radio_init()
|
||||||
|
|
||||||
memset(¤t_telemetry_data, 0, sizeof(current_telemetry_data));
|
memset(¤t_telemetry_data, 0, sizeof(current_telemetry_data));
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < radio_transmit_entry_count; i++) {
|
||||||
|
radio_transmit_entry *entry = &radio_transmit_schedule[i];
|
||||||
|
switch (entry->data_mode) {
|
||||||
|
case RADIO_DATA_MODE_APRS_1200:
|
||||||
|
entry->messages = aprs_comment_templates;
|
||||||
|
break;
|
||||||
|
case RADIO_DATA_MODE_FT8:
|
||||||
|
case RADIO_DATA_MODE_JT65:
|
||||||
|
case RADIO_DATA_MODE_JT9:
|
||||||
|
case RADIO_DATA_MODE_JT4:
|
||||||
|
entry->messages = ftjt_message_templates;
|
||||||
|
break;
|
||||||
|
case RADIO_DATA_MODE_FSQ_6:
|
||||||
|
case RADIO_DATA_MODE_FSQ_4_5:
|
||||||
|
case RADIO_DATA_MODE_FSQ_3:
|
||||||
|
case RADIO_DATA_MODE_FSQ_2:
|
||||||
|
entry->messages = fsq_comment_templates;
|
||||||
|
break;
|
||||||
|
case RADIO_DATA_MODE_WSPR:
|
||||||
|
// No messages
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (entry-> messages != NULL) {
|
||||||
|
for (entry->message_count = 0; entry->messages[entry->message_count] != NULL; entry->message_count++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
radio_current_transmit_entry = &radio_transmit_schedule[radio_current_transmit_entry_index];
|
radio_current_transmit_entry = &radio_transmit_schedule[radio_current_transmit_entry_index];
|
||||||
|
|
||||||
while (!radio_current_transmit_entry->enabled) {
|
while (!radio_current_transmit_entry->enabled) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ typedef enum _radio_type {
|
||||||
typedef enum _radio_data_mode {
|
typedef enum _radio_data_mode {
|
||||||
RADIO_DATA_MODE_CW = 1,
|
RADIO_DATA_MODE_CW = 1,
|
||||||
RADIO_DATA_MODE_RTTY,
|
RADIO_DATA_MODE_RTTY,
|
||||||
RADIO_DATA_MODE_APRS,
|
RADIO_DATA_MODE_APRS_1200,
|
||||||
RADIO_DATA_MODE_WSPR,
|
RADIO_DATA_MODE_WSPR,
|
||||||
RADIO_DATA_MODE_FT8,
|
RADIO_DATA_MODE_FT8,
|
||||||
RADIO_DATA_MODE_JT65,
|
RADIO_DATA_MODE_JT65,
|
||||||
|
@ -41,6 +41,10 @@ typedef struct _radio_transmit_entry {
|
||||||
uint8_t tx_power;
|
uint8_t tx_power;
|
||||||
uint32_t symbol_rate;
|
uint32_t symbol_rate;
|
||||||
|
|
||||||
|
char **messages;
|
||||||
|
uint8_t current_message_index;
|
||||||
|
uint8_t message_count;
|
||||||
|
|
||||||
payload_encoder *payload_encoder;
|
payload_encoder *payload_encoder;
|
||||||
fsk_encoder_api *fsk_encoder_api;
|
fsk_encoder_api *fsk_encoder_api;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "codecs/ax25/ax25.h"
|
#include "codecs/ax25/ax25.h"
|
||||||
|
@ -9,21 +8,11 @@
|
||||||
#include "radio_payload_aprs.h"
|
#include "radio_payload_aprs.h"
|
||||||
|
|
||||||
uint8_t aprs_packet[RADIO_PAYLOAD_MAX_LENGTH];
|
uint8_t aprs_packet[RADIO_PAYLOAD_MAX_LENGTH];
|
||||||
char aprs_comment[APRS_COMMENT_MAX_LENGTH];
|
|
||||||
|
|
||||||
const char *aprs_comment_format = " RS41ng test, time is %02d:%02d:%02d, locator %s, TOW %lu ms, gs %lu, hd %ld";
|
uint16_t radio_aprs_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
|
||||||
|
|
||||||
uint16_t radio_aprs_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data)
|
|
||||||
{
|
{
|
||||||
gps_data *gps = &telemetry_data->gps;
|
|
||||||
|
|
||||||
snprintf(aprs_comment, sizeof(aprs_comment),
|
|
||||||
aprs_comment_format,
|
|
||||||
gps->hours, gps->minutes, gps->seconds, telemetry_data->locator, gps->time_of_week_millis,
|
|
||||||
gps->ground_speed_cm_per_second, gps->heading_degrees_100000);
|
|
||||||
|
|
||||||
aprs_generate_position(aprs_packet, sizeof(aprs_packet), telemetry_data,
|
aprs_generate_position(aprs_packet, sizeof(aprs_packet), telemetry_data,
|
||||||
APRS_SYMBOL_TABLE, APRS_SYMBOL, false, aprs_comment);
|
APRS_SYMBOL_TABLE, APRS_SYMBOL, false, message);
|
||||||
|
|
||||||
log_debug("APRS packet: %s\n", aprs_packet);
|
log_debug("APRS packet: %s\n", aprs_packet);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "radio_payload_fsq.h"
|
||||||
|
|
||||||
|
uint16_t radio_fsq_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
|
||||||
|
{
|
||||||
|
return snprintf((char *) payload, length, "%s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
payload_encoder radio_fsq_payload_encoder = {
|
||||||
|
.encode = radio_fsq_encode,
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef __RADIO_PAYLOAD_FSQ_H
|
||||||
|
#define __RADIO_PAYLOAD_FSQ_H
|
||||||
|
|
||||||
|
#include "payload.h"
|
||||||
|
|
||||||
|
extern payload_encoder radio_fsq_payload_encoder;
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,24 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "radio_payload_ft8.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
const bool ft8_locator_fixed_enabled = FT8_LOCATOR_FIXED_ENABLED;
|
|
||||||
|
|
||||||
uint16_t radio_ft8_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data)
|
|
||||||
{
|
|
||||||
char locator[5];
|
|
||||||
|
|
||||||
if (ft8_locator_fixed_enabled) {
|
|
||||||
strlcpy(locator, FT8_LOCATOR_FIXED, 4 + 1);
|
|
||||||
} else {
|
|
||||||
strlcpy(locator, telemetry_data->locator, 4 + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return snprintf((char *) payload, length, "%s %s", FT8_CALLSIGN, locator);
|
|
||||||
}
|
|
||||||
|
|
||||||
payload_encoder radio_ft8_payload_encoder = {
|
|
||||||
.encode = radio_ft8_encode,
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
#ifndef __RADIO_PAYLOAD_FT8_H
|
|
||||||
#define __RADIO_PAYLOAD_FT8_H
|
|
||||||
|
|
||||||
#include "payload.h"
|
|
||||||
|
|
||||||
extern payload_encoder radio_ft8_payload_encoder;
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "radio_payload_jtencode.h"
|
||||||
|
|
||||||
|
uint16_t radio_ft8_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
|
||||||
|
{
|
||||||
|
return snprintf((char *) payload, length, "%s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
payload_encoder radio_ft8_payload_encoder = {
|
||||||
|
.encode = radio_ft8_encode,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t radio_jt9_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
|
||||||
|
{
|
||||||
|
return snprintf((char *) payload, length, "%s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
payload_encoder radio_jt9_payload_encoder = {
|
||||||
|
.encode = radio_jt9_encode,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t radio_jt4_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
|
||||||
|
{
|
||||||
|
return snprintf((char *) payload, length, "%s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
payload_encoder radio_jt4_payload_encoder = {
|
||||||
|
.encode = radio_jt4_encode,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t radio_jt65_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
|
||||||
|
{
|
||||||
|
return snprintf((char *) payload, length, "%s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
payload_encoder radio_jt65_payload_encoder = {
|
||||||
|
.encode = radio_jt65_encode,
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef __RADIO_PAYLOAD_JTENCODE_H
|
||||||
|
#define __RADIO_PAYLOAD_JTENCODE_H
|
||||||
|
|
||||||
|
#include "payload.h"
|
||||||
|
|
||||||
|
extern payload_encoder radio_ft8_payload_encoder;
|
||||||
|
extern payload_encoder radio_jt9_payload_encoder;
|
||||||
|
extern payload_encoder radio_jt4_payload_encoder;
|
||||||
|
extern payload_encoder radio_jt65_payload_encoder;
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,7 +2,7 @@
|
||||||
#include "telemetry.h"
|
#include "telemetry.h"
|
||||||
#include "radio_payload_wspr.h"
|
#include "radio_payload_wspr.h"
|
||||||
|
|
||||||
uint16_t radio_wspr_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data)
|
uint16_t radio_wspr_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
|
||||||
{
|
{
|
||||||
// Not used for WSPR
|
// Not used for WSPR
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -10,6 +10,16 @@
|
||||||
|
|
||||||
#include "radio_si4032.h"
|
#include "radio_si4032.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I have attempted to implement Bell 202 frequency generation using hardware DMA and PWM, but have failed to generate
|
||||||
|
* correct symbol rate that other APRS equipment are able to decode. I have tried to decode the DMA-based modulation with
|
||||||
|
* some tools intended for debugging APRS and while some bytes are decoded correctly every once in a while,
|
||||||
|
* the timings are mostly off for some unknown reason.
|
||||||
|
*
|
||||||
|
* The Bell 202 modulation implementation uses hardware PWM to generate the individual tone frequencies,
|
||||||
|
* but when si4032_use_dma is false, the symbol timing is created in a loop with delay that was chosen
|
||||||
|
* carefully via experiments.
|
||||||
|
*/
|
||||||
static bool si4032_use_dma = false;
|
static bool si4032_use_dma = false;
|
||||||
|
|
||||||
// TODO: Add support for multiple APRS baud rates
|
// TODO: Add support for multiple APRS baud rates
|
||||||
|
@ -40,7 +50,7 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state
|
||||||
modulation_type = SI4032_MODULATION_TYPE_NONE;
|
modulation_type = SI4032_MODULATION_TYPE_NONE;
|
||||||
use_direct_mode = false;
|
use_direct_mode = false;
|
||||||
break;
|
break;
|
||||||
case RADIO_DATA_MODE_APRS:
|
case RADIO_DATA_MODE_APRS_1200:
|
||||||
frequency_offset = 0;
|
frequency_offset = 0;
|
||||||
modulation_type = SI4032_MODULATION_TYPE_FSK;
|
modulation_type = SI4032_MODULATION_TYPE_FSK;
|
||||||
use_direct_mode = true;
|
use_direct_mode = true;
|
||||||
|
@ -53,7 +63,7 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state
|
||||||
}
|
}
|
||||||
|
|
||||||
si4032_set_tx_frequency(((float) entry->frequency) / 1000000.0f);
|
si4032_set_tx_frequency(((float) entry->frequency) / 1000000.0f);
|
||||||
si4032_set_tx_power(entry->tx_power * 7 / 100);
|
si4032_set_tx_power(entry->tx_power);
|
||||||
si4032_set_frequency_offset(frequency_offset);
|
si4032_set_frequency_offset(frequency_offset);
|
||||||
si4032_set_modulation_type(modulation_type);
|
si4032_set_modulation_type(modulation_type);
|
||||||
|
|
||||||
|
@ -67,7 +77,7 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (entry->data_mode) {
|
switch (entry->data_mode) {
|
||||||
case RADIO_DATA_MODE_APRS:
|
case RADIO_DATA_MODE_APRS_1200:
|
||||||
if (si4032_use_dma) {
|
if (si4032_use_dma) {
|
||||||
shared_state->radio_dma_transfer_active = true;
|
shared_state->radio_dma_transfer_active = true;
|
||||||
radio_dma_transfer_stop_after_counter = -1;
|
radio_dma_transfer_stop_after_counter = -1;
|
||||||
|
@ -91,7 +101,7 @@ static uint32_t radio_next_symbol_si4032(radio_transmit_entry *entry, radio_modu
|
||||||
return 0;
|
return 0;
|
||||||
case RADIO_DATA_MODE_RTTY:
|
case RADIO_DATA_MODE_RTTY:
|
||||||
return 0;
|
return 0;
|
||||||
case RADIO_DATA_MODE_APRS: {
|
case RADIO_DATA_MODE_APRS_1200: {
|
||||||
int8_t next_tone_index = entry->fsk_encoder_api->next_tone(&entry->fsk_encoder);
|
int8_t next_tone_index = entry->fsk_encoder_api->next_tone(&entry->fsk_encoder);
|
||||||
if (next_tone_index < 0) {
|
if (next_tone_index < 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -130,7 +140,7 @@ static void radio_handle_main_loop_manual_si4032(radio_transmit_entry *entry, ra
|
||||||
system_disable_tick();
|
system_disable_tick();
|
||||||
|
|
||||||
switch (entry->data_mode) {
|
switch (entry->data_mode) {
|
||||||
case RADIO_DATA_MODE_APRS: {
|
case RADIO_DATA_MODE_APRS_1200: {
|
||||||
int8_t tone_index;
|
int8_t tone_index;
|
||||||
|
|
||||||
while ((tone_index = fsk_encoder_api->next_tone(fsk_enc)) >= 0) {
|
while ((tone_index = fsk_encoder_api->next_tone(fsk_enc)) >= 0) {
|
||||||
|
@ -179,11 +189,8 @@ bool radio_stop_transmit_si4032(radio_transmit_entry *entry, radio_module_state
|
||||||
case RADIO_DATA_MODE_RTTY:
|
case RADIO_DATA_MODE_RTTY:
|
||||||
use_direct_mode = false;
|
use_direct_mode = false;
|
||||||
break;
|
break;
|
||||||
case RADIO_DATA_MODE_APRS:
|
case RADIO_DATA_MODE_APRS_1200:
|
||||||
use_direct_mode = true;
|
use_direct_mode = true;
|
||||||
if (si4032_use_dma) {
|
|
||||||
system_enable_tick();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -199,7 +206,7 @@ bool radio_stop_transmit_si4032(radio_transmit_entry *entry, radio_module_state
|
||||||
si4032_inhibit_tx();
|
si4032_inhibit_tx();
|
||||||
|
|
||||||
switch (entry->data_mode) {
|
switch (entry->data_mode) {
|
||||||
case RADIO_DATA_MODE_APRS:
|
case RADIO_DATA_MODE_APRS_1200:
|
||||||
if (si4032_use_dma) {
|
if (si4032_use_dma) {
|
||||||
system_enable_tick();
|
system_enable_tick();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ static volatile uint64_t radio_si5351_freq = 0;
|
||||||
|
|
||||||
bool radio_start_transmit_si5351(radio_transmit_entry *entry, radio_module_state *shared_state)
|
bool radio_start_transmit_si5351(radio_transmit_entry *entry, radio_module_state *shared_state)
|
||||||
{
|
{
|
||||||
si5351_set_drive_strength(SI5351_CLOCK_CLK0, entry->tx_power * 3 / 100);
|
si5351_set_drive_strength(SI5351_CLOCK_CLK0, entry->tx_power);
|
||||||
si5351_set_frequency(SI5351_CLOCK_CLK0, ((uint64_t) entry->frequency) * 100ULL);
|
si5351_set_frequency(SI5351_CLOCK_CLK0, ((uint64_t) entry->frequency) * 100ULL);
|
||||||
si5351_output_enable(SI5351_CLOCK_CLK0, true);
|
si5351_output_enable(SI5351_CLOCK_CLK0, true);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
void telemetry_collect(telemetry_data *data)
|
void telemetry_collect(telemetry_data *data)
|
||||||
{
|
{
|
||||||
|
data->button_adc_value = system_get_button_adc_value();
|
||||||
data->battery_voltage_millivolts = system_get_battery_voltage_millivolts();
|
data->battery_voltage_millivolts = system_get_battery_voltage_millivolts();
|
||||||
data->internal_temperature_celsius_100 = si4032_read_temperature_celsius_100();
|
data->internal_temperature_celsius_100 = si4032_read_temperature_celsius_100();
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
typedef struct _telemetry_data {
|
typedef struct _telemetry_data {
|
||||||
uint16_t battery_voltage_millivolts;
|
uint16_t battery_voltage_millivolts;
|
||||||
|
uint16_t button_adc_value;
|
||||||
int32_t internal_temperature_celsius_100;
|
int32_t internal_temperature_celsius_100;
|
||||||
|
|
||||||
int32_t temperature_celsius_100;
|
int32_t temperature_celsius_100;
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "utils.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "template.h"
|
||||||
|
|
||||||
|
size_t template_replace(char *dest, size_t dest_len, char *src, telemetry_data *data)
|
||||||
|
{
|
||||||
|
char *temp = malloc(dest_len);
|
||||||
|
if (temp == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char replacement[32];
|
||||||
|
|
||||||
|
strlcpy(replacement, CALLSIGN, sizeof(replacement));
|
||||||
|
strlcpy(temp, src, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$cs", replacement);
|
||||||
|
|
||||||
|
strlcpy(replacement, data->locator, 4 + 1);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$loc4", replacement);
|
||||||
|
|
||||||
|
strlcpy(replacement, data->locator, 6 + 1);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$loc6", replacement);
|
||||||
|
|
||||||
|
strlcpy(replacement, data->locator, 8 + 1);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$loc8", replacement);
|
||||||
|
|
||||||
|
strlcpy(replacement, data->locator, 12 + 1);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$loc12", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%d", data->battery_voltage_millivolts);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$bv", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%d", data->button_adc_value);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$bu", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%d", data->temperature_celsius_100 / 100);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$te", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%d", data->internal_temperature_celsius_100 / 100);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$ti", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%d", data->humidity_percentage_100 / 100);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$hu", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%d", data->pressure_mbar_100 / 100);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$pr", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%u", data->gps.time_of_week_millis);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$tow", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%02d", data->gps.hours);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$hh", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%02d", data->gps.minutes);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$mm", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%02d", data->gps.seconds);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$ss", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%d", data->gps.satellites_visible);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$sv", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%05d", data->gps.latitude_degrees_1000000 / 10000);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$lat", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%05d", data->gps.longitude_degrees_1000000 / 10000);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$lon", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%d", data->gps.altitude_mm / 1000);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$alt", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%d", (int) ((float) data->gps.ground_speed_cm_per_second * 3.6f / 100.0f));
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$gs", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%d", data->gps.climb_cm_per_second / 100);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
str_replace(dest, dest_len, temp, "$cl", replacement);
|
||||||
|
|
||||||
|
snprintf(replacement, sizeof(replacement), "%03d", data->gps.heading_degrees_100000 / 100000);
|
||||||
|
strlcpy(temp, dest, dest_len);
|
||||||
|
size_t len = str_replace(dest, dest_len, temp, "$he", replacement);
|
||||||
|
|
||||||
|
free(temp);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef __TEMPLATE_H
|
||||||
|
#define __TEMPLATE_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "telemetry.h"
|
||||||
|
|
||||||
|
size_t template_replace(char *dest, size_t dest_len, char *src, telemetry_data *data);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
size_t str_replace(char *dest, size_t dest_len, char *orig, char *rep, char *with)
|
||||||
|
{
|
||||||
|
char *ins; // the next insert point
|
||||||
|
char *tmp; // varies
|
||||||
|
size_t len_rep; // length of rep (the string to remove)
|
||||||
|
size_t len_with; // length of with (the string to replace rep with)
|
||||||
|
size_t len_front; // distance between rep and end of last rep
|
||||||
|
size_t count; // number of replacements
|
||||||
|
|
||||||
|
// sanity checks and initialization
|
||||||
|
if (!orig || !rep) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
len_rep = strlen(rep);
|
||||||
|
if (len_rep == 0) {
|
||||||
|
return 0; // empty rep causes infinite loop during count
|
||||||
|
}
|
||||||
|
if (!with) {
|
||||||
|
with = "";
|
||||||
|
}
|
||||||
|
len_with = strlen(with);
|
||||||
|
|
||||||
|
// count the number of replacements needed
|
||||||
|
ins = orig;
|
||||||
|
for (count = 0; (tmp = strstr(ins, rep)); ++count) {
|
||||||
|
ins = tmp + len_rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t required_len = strlen(orig) + (len_with - len_rep) * count + 1;
|
||||||
|
if (dest_len < required_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = dest;
|
||||||
|
|
||||||
|
// first time through the loop, all the variable are set correctly
|
||||||
|
// from here on,
|
||||||
|
// tmp points to the end of the result string
|
||||||
|
// ins points to the next occurrence of rep in orig
|
||||||
|
// orig points to the remainder of orig after "end of rep"
|
||||||
|
while (count--) {
|
||||||
|
ins = strstr(orig, rep);
|
||||||
|
len_front = ins - orig;
|
||||||
|
tmp = strncpy(tmp, orig, len_front) + len_front;
|
||||||
|
tmp = strcpy(tmp, with) + len_with;
|
||||||
|
orig += len_front + len_rep; // move to next "end of rep"
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(tmp, orig);
|
||||||
|
|
||||||
|
return required_len - 1;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef __STRING_H
|
||||||
|
#define __STRING_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
size_t str_replace(char *dest, size_t dest_len, char *orig, char *rep, char *with);
|
||||||
|
|
||||||
|
#endif
|
|
@ -7,9 +7,9 @@ project(RS41ng_test C CXX)
|
||||||
|
|
||||||
set(BINARY ${CMAKE_PROJECT_NAME})
|
set(BINARY ${CMAKE_PROJECT_NAME})
|
||||||
|
|
||||||
file(GLOB_RECURSE USER_SOURCES "../src/codecs/*.c")
|
file(GLOB_RECURSE USER_SOURCES "../src/codecs/*.c" "../src/template.c" "../src/utils.c")
|
||||||
file(GLOB_RECURSE USER_SOURCES_CXX "../src/codecs/*.cpp")
|
file(GLOB_RECURSE USER_SOURCES_CXX "../src/codecs/*.cpp")
|
||||||
file(GLOB_RECURSE USER_HEADERS"../src/codecs/*.h")
|
file(GLOB_RECURSE USER_HEADERS "../src/codecs/*.h" "../src/template.h" "../src/utils.h" "../src/config.h")
|
||||||
|
|
||||||
file(GLOB_RECURSE TEST_SOURCES "*.c")
|
file(GLOB_RECURSE TEST_SOURCES "*.c")
|
||||||
file(GLOB_RECURSE TEST_SOURCES_CXX "*.cpp")
|
file(GLOB_RECURSE TEST_SOURCES_CXX "*.cpp")
|
||||||
|
@ -17,6 +17,8 @@ file(GLOB_RECURSE TEST_HEADERS "*.h")
|
||||||
|
|
||||||
set(SOURCES ${TEST_SOURCES})
|
set(SOURCES ${TEST_SOURCES})
|
||||||
|
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "-lbsd")
|
||||||
|
|
||||||
add_executable(${BINARY} ${TEST_SOURCES} ${USER_SOURCES})
|
add_executable(${BINARY} ${TEST_SOURCES} ${USER_SOURCES})
|
||||||
|
|
||||||
add_test(NAME ${BINARY} COMMAND ${BINARY})
|
add_test(NAME ${BINARY} COMMAND ${BINARY})
|
||||||
|
|
|
@ -7,18 +7,33 @@
|
||||||
#include "telemetry.h"
|
#include "telemetry.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
int main(void)
|
int main2(void)
|
||||||
{
|
{
|
||||||
fsk_encoder fsk_encoder;
|
fsk_encoder fsk_encoder;
|
||||||
|
|
||||||
|
char *gg = "gasfa";
|
||||||
|
char test[100];
|
||||||
|
snprintf(test, sizeof(test), "%3$s %4$s %2$s\n", "1fd", gg, "3aa", "4ab");
|
||||||
|
printf("%s\n", test);
|
||||||
|
|
||||||
bell_encoder_new(&fsk_encoder, 1200, 0, bell202_tones);
|
bell_encoder_new(&fsk_encoder, 1200, 0, bell202_tones);
|
||||||
|
|
||||||
telemetry_data telemetry;
|
telemetry_data telemetry;
|
||||||
memset(&telemetry, 0, sizeof(telemetry_data));
|
memset(&telemetry, 0, sizeof(telemetry_data));
|
||||||
|
|
||||||
|
char aprs_comment[256];
|
||||||
|
snprintf(aprs_comment, sizeof(aprs_comment),
|
||||||
|
APRS_COMMENT,
|
||||||
|
telemetry.locator,
|
||||||
|
telemetry.temperature_celsius_100 / 100,
|
||||||
|
telemetry.humidity_percentage_100 / 100,
|
||||||
|
telemetry.pressure_mbar_100 / 100,
|
||||||
|
telemetry.gps.time_of_week_millis,
|
||||||
|
telemetry.gps.hours, telemetry.gps.minutes, telemetry.gps.seconds);
|
||||||
|
|
||||||
uint8_t aprs_packet[256];
|
uint8_t aprs_packet[256];
|
||||||
size_t aprs_length = aprs_generate_position(
|
size_t aprs_length = aprs_generate_position(
|
||||||
aprs_packet, sizeof(aprs_packet), &telemetry, APRS_SYMBOL_TABLE, APRS_SYMBOL, false, APRS_COMMENT);
|
aprs_packet, sizeof(aprs_packet), &telemetry, APRS_SYMBOL_TABLE, APRS_SYMBOL, false, aprs_comment);
|
||||||
|
|
||||||
uint8_t payload[256];
|
uint8_t payload[256];
|
||||||
size_t payload_length = ax25_encode_packet_aprs(APRS_CALLSIGN, APRS_SSID, APRS_DESTINATION, APRS_DESTINATION_SSID, APRS_RELAYS,
|
size_t payload_length = ax25_encode_packet_aprs(APRS_CALLSIGN, APRS_SSID, APRS_DESTINATION, APRS_DESTINATION_SSID, APRS_RELAYS,
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <bsd/string.h>
|
||||||
|
#include "template.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
char *source = "DE $cs: $bv $loc6, $hh:$mm:$ss, $tow, Ti$ti Te$te $hu $pr";
|
||||||
|
char dest[512];
|
||||||
|
telemetry_data data;
|
||||||
|
|
||||||
|
data.battery_voltage_millivolts = 3247;
|
||||||
|
data.internal_temperature_celsius_100 = 27 * 100;
|
||||||
|
data.temperature_celsius_100 = 24 * 100;
|
||||||
|
data.humidity_percentage_100 = 68 * 100;
|
||||||
|
data.pressure_mbar_100 = 1023 * 100;
|
||||||
|
strlcpy(data.locator, "KP21FA35jk45", sizeof(data.locator));
|
||||||
|
|
||||||
|
data.gps.time_of_week_millis = 110022330;
|
||||||
|
data.gps.hours = 18;
|
||||||
|
data.gps.minutes = 33;
|
||||||
|
data.gps.seconds = 51;
|
||||||
|
|
||||||
|
template_replace(dest, sizeof(dest), source, &data);
|
||||||
|
|
||||||
|
printf("%s\n", dest);
|
||||||
|
}
|
Ładowanie…
Reference in New Issue