Add support for APRS weather reports. Fix incorrect atmospheric pressure values with BMP280.

pull/11/head
Mikael Nousiainen 2021-11-16 10:11:54 +02:00
rodzic 47372b9179
commit 81013ecf75
19 zmienionych plików z 203 dodań i 78 usunięć

Wyświetl plik

@ -58,6 +58,7 @@ The main features the RS41ng firmware are:
* Support for multiple transmission modes:
* Standard 1200-baud APRS
* Option to transmit APRS weather reports using readings from an external BMP280 sensor
* [Horus 4FSK v1 and v2 modes](https://github.com/projecthorus/horusdemodlib/wiki) that has improved performance compared to APRS or RTTY
* There is an option to use continuous transmit mode (for either V1 or V2 mode), which helps with receiver frequency synchronization and improves reception.
* Morse code (CW)

Wyświetl plik

@ -40,7 +40,8 @@ bool bmp280_read(int32_t *temperature_celsius_100, uint32_t *pressure_mbar_100,
*temperature_celsius_100 = temperature_raw;
}
if (pressure_mbar_100) {
*pressure_mbar_100 = (uint32_t) (((float) pressure_raw) * 100.0f / 256.0f);
// Pressure unit is Pascal (= mbar * 100) * 256
*pressure_mbar_100 = (uint32_t) (((float) pressure_raw) / 256.0f);
}
if (humidity_percentage_100) {
*humidity_percentage_100 = (uint32_t) (((float) humidity_raw) * 100.0f / 1024.0f);

Wyświetl plik

@ -1,19 +1,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aprs.h"
/**
* TODO: add function to generate weather report
*
* Examples:
* 2019-11-27 04:37:21 EET: OH2NJR>APRS,WIDE1-1,qAR,OH2HCY-2:;434.700-C*111111z6030.65N/02444.84Er434.700MHz T118 -200 R50k OH2RUC Nurmijarvi
* 2019-11-27 04:52:08 EET: OH2NJR>APX210,TCPIP*,qAC,APRSFI-I3:=6030.35N/02443.91E_154/001g002t034r000P000p000b10029
*/
volatile uint16_t aprs_packet_counter = 0;
static void convert_degrees_to_dmh(long x, int8_t *degrees, uint8_t *minutes, uint8_t *h_minutes)
void convert_degrees_to_dmh(long x, int8_t *degrees, uint8_t *minutes, uint8_t *h_minutes)
{
uint8_t sign = (uint8_t) (x > 0 ? 1 : 0);
if (!sign) {
@ -29,47 +20,7 @@ static void convert_degrees_to_dmh(long x, int8_t *degrees, uint8_t *minutes, ui
}
}
size_t aprs_generate_position(uint8_t *payload, size_t length, telemetry_data *data,
char symbol_table, char symbol, bool include_timestamp, char *comment)
void aprs_generate_timestamp(char *timestamp, size_t length, telemetry_data *data)
{
char timestamp[12];
int8_t la_degrees, lo_degrees;
uint8_t la_minutes, la_h_minutes, lo_minutes, lo_h_minutes;
convert_degrees_to_dmh(data->gps.latitude_degrees_1000000 / 10, &la_degrees, &la_minutes, &la_h_minutes);
convert_degrees_to_dmh(data->gps.longitude_degrees_1000000 / 10, &lo_degrees, &lo_minutes, &lo_h_minutes);
int16_t heading_degrees = (int16_t) ((float) data->gps.heading_degrees_100000 / 100000.0f);
int16_t ground_speed_knots = (int16_t) (((float) data->gps.ground_speed_cm_per_second / 100.0f) * 3.6f / 1.852f);
int32_t altitude_feet = (data->gps.altitude_mm / 1000) * 3280 / 1000;
if (include_timestamp) {
snprintf(timestamp, sizeof(timestamp), "/%02d%02d%02dz", data->gps.hours, data->gps.minutes, data->gps.seconds);
} else {
strncpy(timestamp, "!", sizeof(timestamp));
}
aprs_packet_counter++;
return snprintf((char *) payload,
length,
("%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',
symbol_table,
abs(lo_degrees), lo_minutes, lo_h_minutes,
lo_degrees > 0 ? 'E' : 'W',
symbol,
heading_degrees,
ground_speed_knots,
(int) altitude_feet,
aprs_packet_counter,
data->gps.satellites_visible,
(int) data->internal_temperature_celsius_100 / 100,
data->battery_voltage_millivolts,
(int16_t) ((float) data->gps.climb_cm_per_second / 100.0f),
comment
);
snprintf(timestamp, length, "/%02d%02d%02dz", data->gps.hours, data->gps.minutes, data->gps.seconds);
}

Wyświetl plik

@ -7,7 +7,9 @@
#include "gps.h"
#include "telemetry.h"
size_t aprs_generate_position(uint8_t *payload, size_t length, telemetry_data *data,
char symbol_table, char symbol, bool include_timestamp, char *comment);
void convert_degrees_to_dmh(long x, int8_t *degrees, uint8_t *minutes, uint8_t *h_minutes);
void aprs_generate_timestamp(char *timestamp, size_t length, telemetry_data *data);
extern volatile uint16_t aprs_packet_counter;
#endif

Wyświetl plik

@ -0,0 +1,51 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aprs.h"
#include "aprs_position.h"
size_t aprs_generate_position(uint8_t *payload, size_t length, telemetry_data *data,
char symbol_table, char symbol, bool include_timestamp, char *comment)
{
char timestamp[12];
int8_t la_degrees, lo_degrees;
uint8_t la_minutes, la_h_minutes, lo_minutes, lo_h_minutes;
convert_degrees_to_dmh(data->gps.latitude_degrees_1000000 / 10, &la_degrees, &la_minutes, &la_h_minutes);
convert_degrees_to_dmh(data->gps.longitude_degrees_1000000 / 10, &lo_degrees, &lo_minutes, &lo_h_minutes);
int16_t heading_degrees = (int16_t) ((float) data->gps.heading_degrees_100000 / 100000.0f);
int16_t ground_speed_knots = (int16_t) (((float) data->gps.ground_speed_cm_per_second / 100.0f) * 3.6f / 1.852f);
int32_t altitude_feet = (data->gps.altitude_mm / 1000) * 3280 / 1000;
if (include_timestamp) {
aprs_generate_timestamp(timestamp, sizeof(timestamp), data);
} else {
strncpy(timestamp, "!", sizeof(timestamp));
}
aprs_packet_counter++;
return snprintf((char *) payload,
length,
("%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',
symbol_table,
abs(lo_degrees), lo_minutes, lo_h_minutes,
lo_degrees > 0 ? 'E' : 'W',
symbol,
heading_degrees,
ground_speed_knots,
(int) altitude_feet,
aprs_packet_counter,
data->gps.satellites_visible,
(int) data->internal_temperature_celsius_100 / 100,
data->battery_voltage_millivolts,
(int16_t) ((float) data->gps.climb_cm_per_second / 100.0f),
comment
);
}

Wyświetl plik

@ -0,0 +1,13 @@
#ifndef __APRS_POSITION_H
#define __APRS_POSITION_H
#include <stddef.h>
#include <stdint.h>
#include "gps.h"
#include "telemetry.h"
size_t aprs_generate_position(uint8_t *payload, size_t length, telemetry_data *data,
char symbol_table, char symbol, bool include_timestamp, char *comment);
#endif

Wyświetl plik

@ -0,0 +1,51 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aprs.h"
#include "aprs_weather.h"
size_t aprs_generate_weather_report(uint8_t *payload, size_t length, telemetry_data *data,
bool include_timestamp, char *comment)
{
char timestamp[12];
int8_t la_degrees, lo_degrees;
uint8_t la_minutes, la_h_minutes, lo_minutes, lo_h_minutes;
convert_degrees_to_dmh(data->gps.latitude_degrees_1000000 / 10, &la_degrees, &la_minutes, &la_h_minutes);
convert_degrees_to_dmh(data->gps.longitude_degrees_1000000 / 10, &lo_degrees, &lo_minutes, &lo_h_minutes);
if (include_timestamp) {
aprs_generate_timestamp(timestamp, sizeof(timestamp), data);
} else {
strncpy(timestamp, "!", sizeof(timestamp));
}
int32_t temperature_fahrenheits = (data->temperature_celsius_100 * 9 / 5) / 100 + 32;
uint32_t pressure_mbar_10 = data->pressure_mbar_100 / 10;
uint16_t humidity_percentage = data->humidity_percentage_100 / 100;
if (humidity_percentage > 99) {
humidity_percentage = 0; // Zero represents 100%
} else if (humidity_percentage < 1) {
humidity_percentage = 1;
}
aprs_packet_counter++;
return snprintf((char *) payload,
length,
("%s%02d%02d.%02u%c%c%03d%02u.%02u%c%c.../...g...t%03dh%02ub%05u%s"),
timestamp,
abs(la_degrees), la_minutes, la_h_minutes,
la_degrees > 0 ? 'N' : 'S',
'/', // Primary symbol table
abs(lo_degrees), lo_minutes, lo_h_minutes,
lo_degrees > 0 ? 'E' : 'W',
'_', // Weather station symbol
(int) temperature_fahrenheits,
(unsigned int) humidity_percentage,
(unsigned int) pressure_mbar_10,
comment
);
}

Wyświetl plik

@ -0,0 +1,13 @@
#ifndef __APRS_WEATHER_H
#define __APRS_WEATHER_H
#include <stddef.h>
#include <stdint.h>
#include "gps.h"
#include "telemetry.h"
size_t aprs_generate_weather_report(uint8_t *payload, size_t length, telemetry_data *data,
bool include_timestamp, char *comment);
#endif

Wyświetl plik

@ -66,6 +66,7 @@ char *aprs_comment_templates[] = {
// " B$bu $teC $hu% $prmb - " APRS_COMMENT,
// " B$bu $loc12 $hh:$mm:$ss - " APRS_COMMENT,
" $loc12 - " APRS_COMMENT,
// " " APRS_COMMENT,
NULL
};

Wyświetl plik

@ -31,7 +31,7 @@
#define LOCATOR_PAIR_COUNT_FULL 6 // max. 6 (12 characters WWL)
// Delay after transmission for modes that do not use time synchronization. Zero delay allows continuous transmit mode for Horus V1 and V2.
#define RADIO_POST_TRANSMIT_DELAY_MS 0
#define RADIO_POST_TRANSMIT_DELAY_MS 1000
// Threshold for time-synchronized modes regarding how far from scheduled transmission time the transmission is still allowed
#define RADIO_TIME_SYNC_THRESHOLD_MS 2000
@ -54,7 +54,7 @@
// Which modes to transmit using the built-in Si4032 transmitter chip
#define RADIO_SI4032_TX_CW true
#define RADIO_SI4032_TX_APRS true
#define RADIO_SI4032_TX_HORUS_V1 true
#define RADIO_SI4032_TX_HORUS_V1 false
#define RADIO_SI4032_TX_HORUS_V2 true
// Continuous transmit mode can be enabled for *either* Horus V1 or V2, but not both. This disables all other transmission modes.
@ -80,13 +80,13 @@
// Which modes to transmit using an externally connected Si5351 chip in the I²C bus
#define RADIO_SI5351_TX_CW true
#define RADIO_SI5351_TX_HORUS_V1 true
#define RADIO_SI5351_TX_HORUS_V1 false
#define RADIO_SI5351_TX_HORUS_V2 true
#define RADIO_SI5351_TX_JT9 false
#define RADIO_SI5351_TX_JT65 false
#define RADIO_SI5351_TX_JT4 false
#define RADIO_SI5351_TX_WSPR false
#define RADIO_SI5351_TX_FSQ true
#define RADIO_SI5351_TX_FSQ false
#define RADIO_SI5351_TX_FT8 false
// Transmit frequencies for the Si5351 transmitter modes
@ -132,6 +132,8 @@
#define APRS_RELAYS "WIDE1-1,WIDE2-1"
#define APRS_DESTINATION "APZ41N"
#define APRS_DESTINATION_SSID '0'
// Generate an APRS weather report instead of a position report. This will override the APRS symbol with the weather station symbol.
#define APRS_WEATHER_REPORT_ENABLE false
// Schedule transmission every N seconds, counting from beginning of an hour (based on GPS time). Set to zero to disable time sync.
#define APRS_TIME_SYNC_SECONDS 0

Wyświetl plik

@ -15,7 +15,8 @@
#include "radio_si4032.h"
#include "radio_si5351.h"
#include "radio_payload_cw.h"
#include "radio_payload_aprs.h"
#include "radio_payload_aprs_position.h"
#include "radio_payload_aprs_weather.h"
#include "radio_payload_horus_v1.h"
#include "radio_payload_horus_v2.h"
#include "radio_payload_wspr.h"
@ -95,7 +96,11 @@ radio_transmit_entry radio_transmit_schedule[] = {
.frequency = RADIO_SI4032_TX_FREQUENCY_APRS_1200,
.tx_power = RADIO_SI4032_TX_POWER,
.symbol_rate = 1200,
.payload_encoder = &radio_aprs_payload_encoder,
#if APRS_WEATHER_REPORT_ENABLE
.payload_encoder = &radio_aprs_weather_report_payload_encoder,
#else
.payload_encoder = &radio_aprs_position_payload_encoder,
#endif
.fsk_encoder_api = &bell_fsk_encoder_api,
},
{

Wyświetl plik

@ -1,8 +0,0 @@
#ifndef __RADIO_PAYLOAD_APRS_H
#define __RADIO_PAYLOAD_APRS_H
#include "payload.h"
extern payload_encoder radio_aprs_payload_encoder;
#endif

Wyświetl plik

@ -1,13 +1,13 @@
#include <stdint.h>
#include "codecs/ax25/ax25.h"
#include "codecs/aprs/aprs.h"
#include "codecs/aprs/aprs_position.h"
#include "config.h"
#include "telemetry.h"
#include "log.h"
#include "radio_payload_aprs.h"
#include "radio_payload_aprs_position.h"
uint16_t radio_aprs_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
uint16_t radio_aprs_position_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
{
uint8_t aprs_packet[RADIO_PAYLOAD_MAX_LENGTH];
@ -20,6 +20,6 @@ uint16_t radio_aprs_encode(uint8_t *payload, uint16_t length, telemetry_data *te
(char *) aprs_packet, length, payload);
}
payload_encoder radio_aprs_payload_encoder = {
.encode = radio_aprs_encode,
payload_encoder radio_aprs_position_payload_encoder = {
.encode = radio_aprs_position_encode,
};

Wyświetl plik

@ -0,0 +1,8 @@
#ifndef __RADIO_PAYLOAD_APRS_POSITION_H
#define __RADIO_PAYLOAD_APRS_POSITION_H
#include "payload.h"
extern payload_encoder radio_aprs_position_payload_encoder;
#endif

Wyświetl plik

@ -0,0 +1,24 @@
#include <stdint.h>
#include "codecs/ax25/ax25.h"
#include "codecs/aprs/aprs_weather.h"
#include "config.h"
#include "telemetry.h"
#include "log.h"
#include "radio_payload_aprs_weather.h"
uint16_t radio_aprs_weather_report_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message)
{
uint8_t aprs_packet[RADIO_PAYLOAD_MAX_LENGTH];
aprs_generate_weather_report(aprs_packet, sizeof(aprs_packet), telemetry_data,true, message);
log_debug("APRS packet: %s\n", aprs_packet);
return ax25_encode_packet_aprs(APRS_CALLSIGN, APRS_SSID, APRS_DESTINATION, APRS_DESTINATION_SSID, APRS_RELAYS,
(char *) aprs_packet, length, payload);
}
payload_encoder radio_aprs_weather_report_payload_encoder = {
.encode = radio_aprs_weather_report_encode,
};

Wyświetl plik

@ -0,0 +1,8 @@
#ifndef __RADIO_PAYLOAD_APRS_WEATHER_H
#define __RADIO_PAYLOAD_APRS_WEATHER_H
#include "payload.h"
extern payload_encoder radio_aprs_weather_report_payload_encoder;
#endif

Wyświetl plik

@ -2,7 +2,7 @@
#include <stdbool.h>
#include "codecs/bell/bell.h"
#include "codecs/aprs/aprs.h"
#include "codecs/aprs/aprs_position.h"
#include "codecs/ax25/ax25.h"
#include "telemetry.h"
#include "config.h"

Wyświetl plik

@ -4,7 +4,7 @@
#include "codecs/morse/morse.h"
#include "config.h"
int main(void)
int main4(void)
{
fsk_encoder morse;

Wyświetl plik

@ -3,14 +3,14 @@
#include <bsd/string.h>
#include "template.h"
int main3(void)
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.internal_temperature_celsius_100 = -7 * 100;
data.temperature_celsius_100 = 24 * 100;
data.humidity_percentage_100 = 68 * 100;
data.pressure_mbar_100 = 1023 * 100;
@ -24,4 +24,6 @@ int main3(void)
template_replace(dest, sizeof(dest), source, &data);
printf("%s\n", dest);
printf("%03d\n", data.internal_temperature_celsius_100 / 100);
}