Add simple templating and rotating messages per mode. Finish support for FSQ modes. Attempt to get power-off button working (WIP).

pull/11/head
Mikael Nousiainen 2020-09-10 22:57:41 +03:00
rodzic 5d24cdcd7e
commit f2ff6cb8a7
38 zmienionych plików z 662 dodań i 267 usunięć

16
NOTES.md 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -15,7 +15,7 @@ The main features this firmware aims to implement are:
* Support for custom sensors via the external I²C bus
* 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
* 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
* 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
* *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
* Bosch BMP280 barometric pressure / temperature / humidity sensor
### 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)
* RTTY on both Si4032 (70cm, non-standard shift) and Si5351 (HF + 2m) with configurable shift
* 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,
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 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>

Wyświetl plik

@ -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 -
```

Wyświetl plik

@ -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

Wyświetl plik

@ -21,8 +21,8 @@ add_definitions(-D__ASSEMBLY__)
SET(LINKER_SCRIPT arm-gcc-link.ld)
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_C_FLAGS "${COMMON_FLAGS} -std=gnu99 -Dprintf=iprintf")
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_C_FLAGS "${COMMON_FLAGS} -std=gnu99")
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
#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}")

Wyświetl plik

@ -54,7 +54,7 @@ size_t aprs_generate_position(uint8_t *payload, size_t length, telemetry_data *d
return snprintf((char *) payload,
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,
abs(la_degrees), la_minutes, la_h_minutes,
la_degrees > 0 ? 'N' : 'S',

Wyświetl plik

@ -83,7 +83,7 @@ uint16_t ax25_encode_packet_aprs(char *source, uint8_t source_ssid, char *destin
memset(header->source, ' ', sizeof(header->source));
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));
memcpy(header->destination, destination, 6);

Wyświetl plik

@ -125,10 +125,8 @@ typedef struct _jtencode_encoder {
uint8_t wspr_dbm;
char *fsq_callsign_from;
char *fsq_callsign_to;
char fsq_command;
JTEncode jtencode;
JTEncode *jtencode;
uint16_t symbol_count;
uint32_t tone_spacing;
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,
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];
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->fsq_callsign_from = fsq_callsign_from;
jte->fsq_callsign_to = fsq_callsign_to;
jte->fsq_command = fsq_command;
jte->jtencode = new JTEncode();
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)
{
if (encoder->priv != nullptr) {
auto *jte = (jtencode_encoder *) encoder->priv;
delete jte->jtencode;
free(encoder->priv);
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)
{
auto *jte = (jtencode_encoder *) encoder->priv;
JTEncode *jtencode = &jte->jtencode;
JTEncode *jtencode = jte->jtencode;
uint8_t *symbol_data = jte->symbol_data;
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_4_5:
case JTENCODE_MODE_FSQ_6:
jtencode->fsq_dir_encode(jte->fsq_callsign_from, jte->fsq_callsign_to,
jte->fsq_command, (const char *) data, symbol_data);
jtencode->fsq_encode(jte->fsq_callsign_from, (const char *) data, symbol_data);
uint8_t j = 0;
while (symbol_data[j++] != 0xff);

Wyświetl plik

@ -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,
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);
extern fsk_encoder_api jtencode_fsk_encoder_api;

Wyświetl plik

@ -41,7 +41,12 @@
JTEncode::JTEncode(void)
{
// 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);
}
/*

Wyświetl plik

@ -211,6 +211,7 @@ class JTEncode
{
public:
JTEncode(void);
~JTEncode();
void jt65_encode(const char *, uint8_t *);
void jt9_encode(const char *, uint8_t *);
void jt4_encode(const char *, uint8_t *);

Wyświetl plik

@ -1,6 +1,67 @@
#include <stdlib.h>
#include "config.h"
bool leds_enabled = LEDS_ENABLE;
bool bmp280_enabled = SENSOR_BMP280_ENABLE;
bool si5351_enabled = RADIO_SI5351_ENABLE;
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
};

Wyświetl plik

@ -8,9 +8,13 @@
#include <stdbool.h>
#define CALLSIGN "OH3BHX"
#define RADIO_PAYLOAD_MAX_LENGTH 256
#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
@ -19,74 +23,82 @@
#define RADIO_POST_TRANSMIT_DELAY_MS 5000
#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_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
#define RADIO_SI5351_TX_FREQUENCY_JT65 14078300UL
#define RADIO_SI5351_TX_FREQUENCY_JT4 14078500UL
#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
/**
* Si5351 transmit power: 0..3
* Si5351 drive strength: 0 = 2mA, 1 = 4mA, 2 = 6mA, 3 = 8mA
*/
#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_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 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 "AA00"
#define WSPR_DBM 10
#define FT8_CALLSIGN "OH3BHX"
#define FT8_LOCATOR_FIXED_ENABLED false
#define FT8_LOCATOR_FIXED "AA00"
#define FSQ_CALLSIGN_FROM "OH3BHX"
#define FSQ_CALLSIGN_TO "N0CALL"
#define FSQ_COMMAND ' '
#define FSQ_CALLSIGN_FROM CALLSIGN
/**
* APRS SSID:
*
* 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 networks (Dstar, Iphones, Androids, 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
* '0' = (-0) Your primary station usually fixed and message capable
* '1' = (-1) generic additional station, digi, mobile, wx, etc
* '2' = (-2) generic additional station, digi, mobile, wx, etc
* '3' = (-3) generic additional station, digi, mobile, wx, etc
* '4' = (-4) generic additional station, digi, mobile, wx, etc
* '5' = (-5) Other networks (Dstar, Iphones, Androids, Blackberry's etc)
* '6' = (-6) Special activity, Satellite ops, camping or 6 meters, etc
* '7' = (-7) walkie talkies, HT's or other human portable
* '8' = (-8) boats, sailboats, RV's or second main mobile
* '9' = (-9) Primary Mobile (usually message capable)
* 'A' = (-10) internet, Igates, echolink, winlink, AVRS, APRN, etc
* 'B' = (-11) balloons, aircraft, spacecraft, etc
* 'C' = (-12) APRStt, DTMF, RFID, devices, one-way trackers*, etc
* 'D' = (-13) Weather stations
* 'E' = (-14) Truckers or generally full time drivers
* 'F' = (-15) generic additional station, digi, mobile, wx, etc
*/
#define APRS_CALLSIGN "OH3BHX"
#define APRS_CALLSIGN CALLSIGN
#define APRS_SSID 'B'
// 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 '['
#define APRS_COMMENT " RS41ng custom radiosonde firmware testing"
#define APRS_SYMBOL 'O'
#define APRS_COMMENT " RS41ng radiosonde firmware test"
#define APRS_RELAYS "WIDE1-1,WIDE2-1"
#define APRS_DESTINATION "APZ41N"
#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_7BIT 1 // if 0 --> 5 bits
#define CW_LOCATOR_PAIR_COUNT 4 // max. 6 (12 characters WWL)
extern bool leds_enabled;
extern bool bmp280_enabled;
extern bool si5351_enabled;
extern volatile bool system_initialized;
extern char *aprs_comment_templates[];
extern char *fsq_comment_templates[];
extern char *ftjt_message_templates[];
#endif

Wyświetl plik

@ -78,13 +78,15 @@ static void gpio_init()
GPIO_Init(GPIOA, &gpio_init);
// Battery voltage (analog)
gpio_init.GPIO_Mode = GPIO_Mode_AIN;
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);
// Button state (analog)
gpio_init.GPIO_Mode = GPIO_Mode_AIN;
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);
// 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);
}
uint16_t system_get_button_adc_value()
{
return (uint16_t) dma_buffer_adc[1];
}
void system_shutdown()
{
GPIO_SetBits(GPIOA, GPIO_Pin_12);
@ -163,10 +170,13 @@ void system_shutdown()
void system_handle_button()
{
static uint16_t button_pressed_threshold = 0;
static uint16_t button_pressed_threshold = 2000;
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) {
button_pressed++;
@ -306,6 +316,6 @@ void TIM4_IRQHandler(void)
system_handle_timer_tick();
// TODO: system_handle_button();
// TODO: fix detection of button state and enable: system_handle_button();
}
}

Wyświetl plik

@ -9,6 +9,7 @@
#define SYSTEM_SCHEDULER_TIMER_TICKS_PER_SECOND 10000
void system_init();
void system_shutdown();
uint32_t system_get_tick();
void system_disable_tick();
void system_enable_tick();
@ -17,6 +18,7 @@ void system_enable_irq();
void system_set_green_led(bool enabled);
void system_set_red_led(bool enabled);
uint16_t system_get_battery_voltage_millivolts();
uint16_t system_get_button_adc_value();
extern void (*system_handle_timer_tick)();

Wyświetl plik

@ -1,4 +1,3 @@
#include <stdio.h>
#include "hal/system.h"
#include "hal/i2c.h"
#include "hal/spi.h"
@ -30,16 +29,18 @@ void handle_timer_tick()
ubxg6010_get_current_gps_data(&current_gps_data);
}
// Blink fast until GPS fix is acquired
if (counter % (SYSTEM_SCHEDULER_TIMER_TICKS_PER_SECOND / 4) == 0) {
if (current_gps_data.fix >= 3) {
if (counter == 0) {
if (leds_enabled) {
// Blink fast until GPS fix is acquired
if (counter % (SYSTEM_SCHEDULER_TIMER_TICKS_PER_SECOND / 4) == 0) {
if (current_gps_data.fix >= 3) {
if (counter == 0) {
led_state = !led_state;
system_set_green_led(led_state);
}
} else {
led_state = !led_state;
system_set_green_led(led_state);
}
} else {
led_state = !led_state;
system_set_green_led(led_state);
}
}
}
@ -90,8 +91,13 @@ gps_init:
log_info("System initialized!\n");
system_set_green_led(true);
system_set_red_led(false);
if (leds_enabled) {
system_set_green_led(true);
system_set_red_led(false);
} else {
system_set_green_led(false);
system_set_red_led(false);
}
system_initialized = true;

Wyświetl plik

@ -7,7 +7,7 @@
#include "telemetry.h"
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;
#endif

Wyświetl plik

@ -2,6 +2,7 @@
#include "config.h"
#include "log.h"
#include "template.h"
#include "hal/system.h"
#include "hal/delay.h"
#include "hal/usart_gps.h"
@ -13,30 +14,27 @@
#include "radio_si5351.h"
#include "radio_payload_aprs.h"
#include "radio_payload_wspr.h"
#include "radio_payload_ft8.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
#include "radio_payload_jtencode.h"
#include "radio_payload_fsq.h"
radio_transmit_entry radio_transmit_schedule[] = {
{
.enabled = true,
.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_offset = 0,
.frequency = RADIO_SI4032_TX_FREQUENCY_APRS,
.frequency = RADIO_SI4032_TX_FREQUENCY_APRS_1200,
.tx_power = RADIO_SI4032_TX_POWER,
.symbol_rate = 1200,
.payload_encoder = &radio_aprs_payload_encoder,
.fsk_encoder_api = &bell_fsk_encoder_api,
},
{
.enabled = true,
.enabled = false,
.radio_type = RADIO_TYPE_SI5351,
.data_mode = RADIO_DATA_MODE_FT8,
.time_sync_seconds = 5,
.time_sync_seconds = 15,
.time_sync_seconds_offset = 0,
.frequency = RADIO_SI5351_TX_FREQUENCY_FT8,
.tx_power = RADIO_SI5351_TX_POWER,
@ -47,8 +45,56 @@ radio_transmit_entry radio_transmit_schedule[] = {
{
.enabled = false,
.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,
.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,
.frequency = RADIO_SI5351_TX_FREQUENCY_WSPR,
.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;
char radio_current_payload_message[RADIO_PAYLOAD_MESSAGE_MAX_LENGTH];
uint8_t radio_current_payload[RADIO_PAYLOAD_MAX_LENGTH];
uint16_t radio_current_payload_length = 0;
@ -127,8 +175,16 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
telemetry_collect(&current_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], &current_telemetry_data);
} else {
radio_current_payload_message[0] = '\0';
}
radio_current_payload_length = entry->payload_encoder->encode(
radio_current_payload, sizeof(radio_current_payload), &current_telemetry_data);
radio_current_payload, sizeof(radio_current_payload),
&current_telemetry_data, radio_current_payload_message);
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;
case RADIO_DATA_MODE_RTTY:
break;
case RADIO_DATA_MODE_APRS:
case RADIO_DATA_MODE_APRS_1200:
// 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);
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,
entry->jtencode_mode_type, WSPR_CALLSIGN, locator,
WSPR_DBM, FSQ_CALLSIGN_FROM, FSQ_CALLSIGN_TO, FSQ_COMMAND);
entry->jtencode_mode_type, WSPR_CALLSIGN, locator, WSPR_DBM, FSQ_CALLSIGN_FROM);
if (!success) {
return false;
}
@ -214,7 +269,9 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
log_info("TX start\n");
system_set_red_led(true);
if (leds_enabled) {
system_set_red_led(true);
}
radio_shared_state.radio_transmission_active = true;
@ -261,7 +318,7 @@ static bool radio_stop_transmit(radio_transmit_entry *entry)
break;
case RADIO_DATA_MODE_RTTY:
break;
case RADIO_DATA_MODE_APRS:
case RADIO_DATA_MODE_APRS_1200:
bell_encoder_destroy(&entry->fsk_encoder);
break;
case RADIO_DATA_MODE_WSPR:
@ -280,7 +337,9 @@ static bool radio_stop_transmit(radio_transmit_entry *entry)
}
usart_gps_enable(true);
system_set_red_led(false);
if (leds_enabled) {
system_set_red_led(false);
}
return success;
}
@ -314,6 +373,9 @@ static void radio_reset_transmit_delay_counter()
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 {
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];
@ -503,6 +565,35 @@ void radio_init()
memset(&current_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];
while (!radio_current_transmit_entry->enabled) {

Wyświetl plik

@ -15,7 +15,7 @@ typedef enum _radio_type {
typedef enum _radio_data_mode {
RADIO_DATA_MODE_CW = 1,
RADIO_DATA_MODE_RTTY,
RADIO_DATA_MODE_APRS,
RADIO_DATA_MODE_APRS_1200,
RADIO_DATA_MODE_WSPR,
RADIO_DATA_MODE_FT8,
RADIO_DATA_MODE_JT65,
@ -41,6 +41,10 @@ typedef struct _radio_transmit_entry {
uint8_t tx_power;
uint32_t symbol_rate;
char **messages;
uint8_t current_message_index;
uint8_t message_count;
payload_encoder *payload_encoder;
fsk_encoder_api *fsk_encoder_api;

Wyświetl plik

@ -1,4 +1,3 @@
#include <stdio.h>
#include <stdint.h>
#include "codecs/ax25/ax25.h"
@ -9,21 +8,11 @@
#include "radio_payload_aprs.h"
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)
uint16_t radio_aprs_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
{
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_SYMBOL_TABLE, APRS_SYMBOL, false, aprs_comment);
APRS_SYMBOL_TABLE, APRS_SYMBOL, false, message);
log_debug("APRS packet: %s\n", aprs_packet);

Wyświetl plik

@ -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,
};

Wyświetl plik

@ -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

Wyświetl plik

@ -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,
};

Wyświetl plik

@ -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

Wyświetl plik

@ -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,
};

Wyświetl plik

@ -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

Wyświetl plik

@ -2,7 +2,7 @@
#include "telemetry.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
return 0;

Wyświetl plik

@ -10,6 +10,16 @@
#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;
// 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;
use_direct_mode = false;
break;
case RADIO_DATA_MODE_APRS:
case RADIO_DATA_MODE_APRS_1200:
frequency_offset = 0;
modulation_type = SI4032_MODULATION_TYPE_FSK;
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_power(entry->tx_power * 7 / 100);
si4032_set_tx_power(entry->tx_power);
si4032_set_frequency_offset(frequency_offset);
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) {
case RADIO_DATA_MODE_APRS:
case RADIO_DATA_MODE_APRS_1200:
if (si4032_use_dma) {
shared_state->radio_dma_transfer_active = true;
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;
case RADIO_DATA_MODE_RTTY:
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);
if (next_tone_index < 0) {
return 0;
@ -130,7 +140,7 @@ static void radio_handle_main_loop_manual_si4032(radio_transmit_entry *entry, ra
system_disable_tick();
switch (entry->data_mode) {
case RADIO_DATA_MODE_APRS: {
case RADIO_DATA_MODE_APRS_1200: {
int8_t tone_index;
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:
use_direct_mode = false;
break;
case RADIO_DATA_MODE_APRS:
case RADIO_DATA_MODE_APRS_1200:
use_direct_mode = true;
if (si4032_use_dma) {
system_enable_tick();
}
break;
default:
break;
@ -199,7 +206,7 @@ bool radio_stop_transmit_si4032(radio_transmit_entry *entry, radio_module_state
si4032_inhibit_tx();
switch (entry->data_mode) {
case RADIO_DATA_MODE_APRS:
case RADIO_DATA_MODE_APRS_1200:
if (si4032_use_dma) {
system_enable_tick();
}

Wyświetl plik

@ -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)
{
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_output_enable(SI5351_CLOCK_CLK0, true);
return true;

Wyświetl plik

@ -8,6 +8,7 @@
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->internal_temperature_celsius_100 = si4032_read_temperature_celsius_100();

Wyświetl plik

@ -9,6 +9,7 @@
typedef struct _telemetry_data {
uint16_t battery_voltage_millivolts;
uint16_t button_adc_value;
int32_t internal_temperature_celsius_100;
int32_t temperature_celsius_100;

107
src/template.c 100644
Wyświetl plik

@ -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;
}

9
src/template.h 100644
Wyświetl plik

@ -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

54
src/utils.c 100644
Wyświetl plik

@ -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;
}

8
src/utils.h 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -7,9 +7,9 @@ project(RS41ng_test C CXX)
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_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_CXX "*.cpp")
@ -17,6 +17,8 @@ file(GLOB_RECURSE TEST_HEADERS "*.h")
set(SOURCES ${TEST_SOURCES})
set(CMAKE_EXE_LINKER_FLAGS "-lbsd")
add_executable(${BINARY} ${TEST_SOURCES} ${USER_SOURCES})
add_test(NAME ${BINARY} COMMAND ${BINARY})

Wyświetl plik

@ -7,18 +7,33 @@
#include "telemetry.h"
#include "config.h"
int main(void)
int main2(void)
{
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);
telemetry_data telemetry;
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];
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];
size_t payload_length = ax25_encode_packet_aprs(APRS_CALLSIGN, APRS_SSID, APRS_DESTINATION, APRS_DESTINATION_SSID, APRS_RELAYS,

Wyświetl plik

@ -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);
}