Allow relaying of GPS NMEA output to the external RS41 serial port to be integrated with other tracker hardware. Allow GPS position recording (by enabling GPS serial port interrupts) during CW and Horus transmissions to avoid transmitting stale GPS data.

pull/11/head
Mikael Nousiainen 2021-11-11 18:15:52 +02:00
rodzic 4549c676aa
commit 9e399160de
10 zmienionych plików z 184 dodań i 34 usunięć

Wyświetl plik

@ -7,12 +7,12 @@ Some of the code is based on an earlier RS41 firmware project called [RS41HUP](h
but most of it has been rewritten from scratch. The Horus 4FSK code has been adapted from
the [darksidelemm fork of RS41HUP](https://github.com/darksidelemm/RS41HUP).
## Asking questions, filing feature requests and issues
## Asking questions, filing feature requests and reporting issues
* Please use [GitHub discussions](../../discussions) for asking questions and for sharing info about your RS41-based projects
* For example, questions about firmware configuration and connecting of external chips to the sonde belong here
* Please use [GitHub issues](../../issues) to file new feature requests or issues that you have already identified with the firmware
* Please remember to post questions about usage to [GitHub discussions](../../discussions)
* However, please remember to post questions about usage to [GitHub discussions](../../discussions)
## What are the Vaisala RS41 radiosondes and how can I get one?
@ -37,7 +37,7 @@ For your own receiver station, you will need:
The [Vaisala RS41 radiosondes](https://www.vaisala.com/en/products/instruments-sensors-and-other-measurement-devices/soundings-products/rs41)
uses an off-the-shelf [STM32F100C8](https://www.st.com/en/microcontrollers-microprocessors/stm32f100c8.html)
32-bit microcontroller, which can be reprogrammed using an [ST-LINK v2 programmer](https://www.st.com/en/development-tools/st-link-v2.html)
or a smaller [ST-LINK v2 USB dongle]((https://www.adafruit.com/product/2548).
or a smaller [ST-LINK v2 USB dongle](https://www.adafruit.com/product/2548).
The RS41 hardware can be programmed to transmit different kinds of RF modulations (morse code, APRS and different FSK modulations)
on the 70 cm (~433 MHz) amateur radio band. The radiosonde contains a [UBX-G6010](https://www.u-blox.com/en/product/ubx-g6010-st-chip)

Wyświetl plik

@ -43,6 +43,7 @@
bool leds_enabled = LEDS_ENABLE;
bool bmp280_enabled = SENSOR_BMP280_ENABLE;
bool si5351_enabled = RADIO_SI5351_ENABLE;
bool gps_nmea_output_enabled = GPS_NMEA_OUTPUT_VIA_SERIAL_PORT_ENABLE;
volatile bool system_initialized = false;

Wyświetl plik

@ -36,6 +36,13 @@
// Threshold for time-synchronized modes regarding how far from scheduled transmission time the transmission is still allowed
#define RADIO_TIME_SYNC_THRESHOLD_MS 2000
// Enable NMEA output from GPS via external serial port. This disables use of I²C bus (Si5351 and sensors) because the pins are shared.
#define GPS_NMEA_OUTPUT_VIA_SERIAL_PORT_ENABLE false
#if (GPS_NMEA_OUTPUT_VIA_SERIAL_PORT_ENABLE) && ((RADIO_SI5351_ENABLE) || (SENSOR_BMP280_ENABLE))
#error GPS NMEA output via serial port cannot be enabled simultaneously with the I2C bus.
#endif
/**
* Built-in Si4032 radio chip transmission configuration
*/

Wyświetl plik

@ -1,6 +1,11 @@
#ifndef __CONFIG_INTERNAL_H
#define __CONFIG_INTERNAL_H
#define GPS_SERIAL_PORT_BAUD_RATE 38400
// The external serial port baud rate must be higher than the GPS serial port baud rate (38400)
#define EXTERNAL_SERIAL_PORT_BAUD_RATE 115200
#define RADIO_PAYLOAD_MAX_LENGTH 256
#define RADIO_SYMBOL_DATA_MAX_LENGTH 512
#define RADIO_PAYLOAD_MESSAGE_MAX_LENGTH 128
@ -11,6 +16,7 @@
#include <stdbool.h>
extern bool leds_enabled;
extern bool gps_nmea_output_enabled;
extern bool bmp280_enabled;
extern bool si5351_enabled;

Wyświetl plik

@ -2,11 +2,14 @@
#include "hal/system.h"
#include "hal/usart_gps.h"
#include "hal/usart_ext.h"
#include "hal/delay.h"
#include "ubxg6010.h"
#include "log.h"
#define GPS_INITIAL_BAUD_RATE 9600
typedef struct __attribute__((packed)) {
uint8_t sc1; // 0xB5
uint8_t sc2; // 0x62
@ -204,6 +207,13 @@ typedef struct {
uint16_t timeRef; //Alignment to reference time: 0 = UTC time, 1 = GPS time [- -]
} uBloxCFGRATEPayload;
typedef struct {
uint8_t filter; // Filter flags
uint8_t version; // 0x23 = NMEA version 2.3, 0x21 = NMEA version 2.1
uint8_t numSV; // Maximum Number of SVs to report in NMEA protocol.
uint8_t flags; // Flags: 0x01 = enable compatibility mode, 0x02 = enable considering mode
} uBloxCFGNMEA;
typedef union {
uBloxNAVPVTPayload navpvt;
uBloxCFGPRTPayload cfgprt;
@ -220,6 +230,7 @@ typedef union {
uBloxCFGRSTPayload cfgrst;
uBloxCFGRXMPayload cfgrxm;
uBloxCFGRATEPayload cfgrate;
uBloxCFGNMEA cfgnmea;
} ubloxPacketData;
typedef struct __attribute__((packed)) {
@ -233,6 +244,9 @@ volatile bool gps_initialized = false;
volatile bool ack_received = false;
volatile bool nack_received = false;
volatile static uint8_t sync_ubx = 0;
volatile static uint8_t sync_nmea = 0;
static uBloxChecksum
ubxg6010_calculate_checksum(const uint8_t msgClass, const uint8_t msgId, const uint8_t *message, uint16_t size)
{
@ -458,6 +472,23 @@ uBloxPacket msgcfgnav5 = {
},
};
// Configure NMEA protocol version 2.3
uBloxPacket msgcfgnmea = {
.header = {
0xb5,
0x62,
.messageClass=0x06,
.messageId=0x17,
.payloadSize=sizeof(uBloxCFGNMEA)
},
.data.cfgnmea = {
.filter = 0x00,
.version = 0x23,
.numSV = 24,
.flags = 0x00,
}
};
bool ubxg6010_init()
{
bool success;
@ -466,28 +497,38 @@ bool ubxg6010_init()
gps_initialized = false;
log_info("GPS: Initializing USART with baud rate 38400\n");
usart_gps_init(38400, true);
log_info("GPS: Initializing USART with baud rate %d\n", GPS_SERIAL_PORT_BAUD_RATE);
usart_gps_init(GPS_SERIAL_PORT_BAUD_RATE, true);
delay_ms(100);
log_info("GPS: Resetting GPS chip\n");
ubxg6010_send_packet(&msgcfgrst);
delay_ms(1000);
log_info("GPS: Initializing USART with baud rate 9600\n");
usart_gps_init(9600, true);
log_info("GPS: Initializing USART with baud rate %d\n", GPS_INITIAL_BAUD_RATE);
usart_gps_init(GPS_INITIAL_BAUD_RATE, true);
delay_ms(100);
log_info("GPS: Resetting GPS chip\n");
ubxg6010_send_packet(&msgcfgrst);
delay_ms(1000);
log_info("GPS: Configuring GPS chip serial port for baud rate 38400\n");
if (gps_nmea_output_enabled) {
log_info("GPS: Configuring GPS NMEA output settings\n");
ubxg6010_send_packet(&msgcfgnmea);
delay_ms(100);
}
log_info("GPS: Configuring GPS chip I/O port settings\n");
if (gps_nmea_output_enabled) {
// Enable both UBX and NMEA protocols
msgcfgprt.data.cfgprt.outProtoMask = 0x03;
}
ubxg6010_send_packet(&msgcfgprt);
delay_ms(100);
log_info("GPS: Initializing USART with baud rate 38400\n");
usart_gps_init(38400, true);
log_info("GPS: Initializing USART with baud rate %d\n", GPS_SERIAL_PORT_BAUD_RATE);
usart_gps_init(GPS_SERIAL_PORT_BAUD_RATE, true);
delay_ms(100);
log_info("GPS: Setting GPS chip power mode\n");
@ -690,36 +731,78 @@ static void ubxg6010_handle_packet(uBloxPacket *pkt)
}
}
void ubxg6010_reset_parser()
{
sync_ubx = 0;
sync_nmea = 0;
}
static void ubxg6010_handle_nmea_sentence_start(uint8_t data)
{
if (sync_nmea == 0 && data == '$') {
sync_nmea = 1;
} else if (sync_nmea == 1) {
if (data == 'G') {
sync_nmea = 2;
} else {
sync_nmea = 0;
}
} else if (sync_nmea == 2) {
if (data >= 'A' && data <= 'Z') {
usart_ext_send_byte('$');
usart_ext_send_byte('G');
usart_ext_send_byte(data);
sync_nmea = 3;
} else {
sync_nmea = 0;
}
}
}
static void ubxg6010_handle_nmea_output(uint8_t data)
{
usart_ext_send_byte(data);
if (data == '\r') {
sync_nmea = 3;
} else if (sync_nmea == 3 && data == '\n') {
sync_nmea = 0;
}
}
void ubxg6010_handle_incoming_byte(uint8_t data)
{
static uint8_t sync = 0;
static uint8_t buffer_pos = 0;
static uint8_t incoming_packet_buffer[sizeof(uBloxPacket) + sizeof(uBloxChecksum)];
static uBloxPacket *incoming_packet = (uBloxPacket *) incoming_packet_buffer;
if (!sync) {
if (!sync_ubx && (sync_nmea < 3)) {
if (!buffer_pos && data == 0xB5) {
buffer_pos = 1;
incoming_packet->header.sc1 = data;
} else if (buffer_pos == 1 && data == 0x62) {
sync = 1;
sync_ubx = 1;
buffer_pos = 2;
incoming_packet->header.sc2 = data;
} else {
if (gps_nmea_output_enabled) {
ubxg6010_handle_nmea_sentence_start(data);
}
buffer_pos = 0;
}
} else if (gps_nmea_output_enabled && sync_nmea >= 3) {
ubxg6010_handle_nmea_output(data);
} else {
((uint8_t *) incoming_packet)[buffer_pos] = data;
if ((buffer_pos >= sizeof(uBloxHeader) - 1) &&
(buffer_pos - 1 == (incoming_packet->header.payloadSize + sizeof(uBloxHeader) + sizeof(uBloxChecksum)))) {
ubxg6010_handle_packet((uBloxPacket *) incoming_packet);
buffer_pos = 0;
sync = 0;
sync_ubx = 0;
} else {
buffer_pos++;
if (buffer_pos >= sizeof(uBloxPacket) + sizeof(uBloxChecksum)) {
buffer_pos = 0;
sync = 0;
sync_ubx = 0;
}
}
}

Wyświetl plik

@ -13,4 +13,6 @@ bool ubxg6010_get_current_gps_data(gps_data *data);
void ubxg6010_handle_incoming_byte(uint8_t data);
void ubxg6010_reset_parser();
#endif

Wyświetl plik

@ -2,7 +2,11 @@
#include <stm32f10x_gpio.h>
#include <stm32f10x_usart.h>
void usart_ext_init()
#include "usart_ext.h"
// USART3 is the external serial port, which shares TX/RX pins with the external I²C bus.
void usart_ext_init(uint32_t baud_rate)
{
GPIO_InitTypeDef gpio_init;
@ -23,12 +27,12 @@ void usart_ext_init()
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
USART_InitTypeDef usart_init;
usart_init.USART_BaudRate = 19200; //0x9c4;
usart_init.USART_BaudRate = baud_rate;
usart_init.USART_WordLength = USART_WordLength_8b;
usart_init.USART_StopBits = USART_StopBits_1;
usart_init.USART_Parity = USART_Parity_No;
usart_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart_init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
usart_init.USART_Mode = USART_Mode_Tx; // Enable only transmit for now | USART_Mode_Rx;
USART_Init(USART3, &usart_init);
USART_Cmd(USART3, ENABLE);
@ -40,3 +44,15 @@ void usart_ext_uninit()
USART_DeInit(USART3);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, DISABLE);
}
void usart_ext_enable(bool enabled)
{
USART_Cmd(USART3, enabled ? ENABLE : DISABLE);
}
void usart_ext_send_byte(uint8_t data)
{
while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET) {}
USART_SendData(USART3, data);
while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET) {}
}

Wyświetl plik

@ -0,0 +1,12 @@
#ifndef __USART_EXT_H
#define __USART_EXT_H
#include <stdint.h>
#include <stdbool.h>
void usart_ext_init(uint32_t baud_rate);
void usart_ext_uninit();
void usart_ext_enable(bool enabled);
void usart_ext_send_byte(uint8_t data);
#endif

Wyświetl plik

@ -2,6 +2,7 @@
#include "hal/i2c.h"
#include "hal/spi.h"
#include "hal/usart_gps.h"
#include "hal/usart_ext.h"
#include "hal/delay.h"
#include "hal/datatimer.h"
#include "drivers/ubxg6010/ubxg6010.h"
@ -61,8 +62,13 @@ int main(void)
system_set_green_led(false);
system_set_red_led(true);
log_info("I2C init\n");
i2c_init();
if (gps_nmea_output_enabled) {
log_info("External USART init\n");
usart_ext_init(EXTERNAL_SERIAL_PORT_BAUD_RATE);
} else {
log_info("I2C init\n");
i2c_init();
}
log_info("SPI init\n");
spi_init();

Wyświetl plik

@ -23,7 +23,7 @@
#include "radio_payload_fsq.h"
radio_transmit_entry radio_transmit_schedule[] = {
#if RADIO_SI4032_TX_HORUS_V1_CONTINUOUS == true
#if RADIO_SI4032_TX_HORUS_V1_CONTINUOUS
{
.enabled = RADIO_SI4032_TX_HORUS_V1,
.radio_type = RADIO_TYPE_SI4032,
@ -48,7 +48,7 @@ radio_transmit_entry radio_transmit_schedule[] = {
.payload_encoder = &radio_horus_v1_idle_encoder,
.fsk_encoder_api = &mfsk_fsk_encoder_api,
},
#elif RADIO_SI4032_TX_HORUS_V2_CONTINUOUS == true
#elif RADIO_SI4032_TX_HORUS_V2_CONTINUOUS
{
.enabled = RADIO_SI4032_TX_HORUS_V2,
.radio_type = RADIO_TYPE_SI4032,
@ -122,8 +122,8 @@ radio_transmit_entry radio_transmit_schedule[] = {
.payload_encoder = &radio_horus_v2_payload_encoder,
.fsk_encoder_api = &mfsk_fsk_encoder_api,
},
#if RADIO_SI5351_ENABLE == true
#if RADIO_SI5351_TX_CW == true
#if RADIO_SI5351_ENABLE
#if RADIO_SI5351_TX_CW
{
.enabled = RADIO_SI5351_TX_CW,
.radio_type = RADIO_TYPE_SI5351,
@ -137,7 +137,7 @@ radio_transmit_entry radio_transmit_schedule[] = {
.fsk_encoder_api = &morse_fsk_encoder_api,
},
#endif
#if RADIO_SI5351_TX_HORUS_V1 == true
#if RADIO_SI5351_TX_HORUS_V1
{
.enabled = RADIO_SI5351_TX_HORUS_V1,
.radio_type = RADIO_TYPE_SI5351,
@ -151,7 +151,7 @@ radio_transmit_entry radio_transmit_schedule[] = {
.fsk_encoder_api = &mfsk_fsk_encoder_api,
},
#endif
#if RADIO_SI5351_TX_HORUS_V2 == true
#if RADIO_SI5351_TX_HORUS_V2
{
.enabled = RADIO_SI5351_TX_HORUS_V2,
.radio_type = RADIO_TYPE_SI5351,
@ -165,7 +165,7 @@ radio_transmit_entry radio_transmit_schedule[] = {
.fsk_encoder_api = &mfsk_fsk_encoder_api,
},
#endif
#if RADIO_SI5351_TX_WSPR == true
#if RADIO_SI5351_TX_WSPR
{
.enabled = RADIO_SI5351_TX_WSPR,
.radio_type = RADIO_TYPE_SI5351,
@ -178,7 +178,7 @@ radio_transmit_entry radio_transmit_schedule[] = {
.fsk_encoder_api = &jtencode_fsk_encoder_api,
},
#endif
#if RADIO_SI5351_TX_FT8 == true
#if RADIO_SI5351_TX_FT8
{
.enabled = RADIO_SI5351_TX_FT8,
.radio_type = RADIO_TYPE_SI5351,
@ -191,7 +191,7 @@ radio_transmit_entry radio_transmit_schedule[] = {
.fsk_encoder_api = &jtencode_fsk_encoder_api,
},
#endif
#if RADIO_SI5351_TX_JT9 == true
#if RADIO_SI5351_TX_JT9
{
.enabled = RADIO_SI5351_TX_JT9,
.radio_type = RADIO_TYPE_SI5351,
@ -204,7 +204,7 @@ radio_transmit_entry radio_transmit_schedule[] = {
.fsk_encoder_api = &jtencode_fsk_encoder_api,
},
#endif
#if RADIO_SI5351_TX_JT4 == true
#if RADIO_SI5351_TX_JT4
{
.enabled = RADIO_SI5351_TX_JT4,
.radio_type = RADIO_TYPE_SI5351,
@ -217,7 +217,7 @@ radio_transmit_entry radio_transmit_schedule[] = {
.fsk_encoder_api = &jtencode_fsk_encoder_api,
},
#endif
#if RADIO_SI5351_TX_JT65 == true
#if RADIO_SI5351_TX_JT65
{
.enabled = RADIO_SI5351_TX_JT65,
.radio_type = RADIO_TYPE_SI5351,
@ -230,7 +230,7 @@ radio_transmit_entry radio_transmit_schedule[] = {
.fsk_encoder_api = &jtencode_fsk_encoder_api,
},
#endif
#if RADIO_SI5351_TX_FSQ == true
#if RADIO_SI5351_TX_FSQ
{
.enabled = RADIO_SI5351_TX_FSQ,
.radio_type = RADIO_TYPE_SI5351,
@ -366,8 +366,14 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
log_info("\n");
#endif
// USART interrupts may interfere with transmission timing
bool enable_gps_during_transmit = false;
switch (entry->data_mode) {
case RADIO_DATA_MODE_CW:
// CW timing is not as critical
enable_gps_during_transmit = true;
morse_encoder_new(&entry->fsk_encoder, entry->symbol_rate);
radio_shared_state.radio_current_symbol_rate = entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder);
entry->fsk_encoder_api->get_tones(&entry->fsk_encoder, &radio_shared_state.radio_current_fsk_tone_count,
@ -385,6 +391,9 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
entry->fsk_encoder_api->set_data(&entry->fsk_encoder, radio_current_payload_length, radio_current_payload);
break;
case RADIO_DATA_MODE_HORUS_V1:
// GPS should not disturb the timing of Horus modes
enable_gps_during_transmit = true;
mfsk_encoder_new(&entry->fsk_encoder, MFSK_4, entry->symbol_rate, HORUS_V1_TONE_SPACING_HZ_SI5351 * 100);
radio_shared_state.radio_current_symbol_rate = entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder);
entry->fsk_encoder_api->get_tones(&entry->fsk_encoder, &radio_shared_state.radio_current_fsk_tone_count,
@ -394,6 +403,9 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
entry->fsk_encoder_api->set_data(&entry->fsk_encoder, radio_current_payload_length, radio_current_payload);
break;
case RADIO_DATA_MODE_HORUS_V2:
// GPS should not disturb the timing of Horus modes
enable_gps_during_transmit = true;
mfsk_encoder_new(&entry->fsk_encoder, MFSK_4, entry->symbol_rate, HORUS_V2_TONE_SPACING_HZ_SI5351 * 100);
radio_shared_state.radio_current_symbol_rate = entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder);
entry->fsk_encoder_api->get_tones(&entry->fsk_encoder, &radio_shared_state.radio_current_fsk_tone_count,
@ -411,6 +423,9 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
case RADIO_DATA_MODE_FSQ_3:
case RADIO_DATA_MODE_FSQ_4_5:
case RADIO_DATA_MODE_FSQ_6: {
// Timing of these slow modes is not as critical
enable_gps_during_transmit = true;
char locator[5];
jtencode_mode_type jtencode_mode = radio_jtencode_mode_type_for(entry->data_mode);
@ -436,8 +451,10 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
return false;
}
// USART interrupts may interfere with transmission timing
usart_gps_enable(false);
usart_gps_enable(enable_gps_during_transmit);
if (!enable_gps_during_transmit) {
ubxg6010_reset_parser();
}
switch (entry->radio_type) {
case RADIO_TYPE_SI4032: