diff --git a/tracker/software/config.c b/tracker/software/config.c index 43f407a2..6da2f512 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -122,7 +122,7 @@ const conf_t conf_flash_default = { .aprs = { .thread_conf = { .active = true, - .init_delay = TIME_S2I(20), + .init_delay = TIME_S2I(20) }, .rx = { // The receive identity for APRS .radio_conf = { @@ -146,7 +146,7 @@ const conf_t conf_flash_default = { .path = "WIDE2-1", .symbol = SYM_DIGIPEATER, .beacon = true, - .gps = true, + .gps = false, // A set location if GPS not enabled or unable to acquire lock. .lat = -337331175, // Degress (1e-7) .lon = 1511143478, // Degrees (1e-7) @@ -168,10 +168,11 @@ const conf_t conf_flash_default = { // Power control .keep_cam_switched_on = false, - .gps_on_vbat = 1000, - .gps_off_vbat = 1000, - .gps_onper_vbat = 1000, - .gps_airborne = 90000, // Enable airborne mode when air pressure below this (Pa) + .gps_on_vbat = 3300, // mV + .gps_off_vbat = 2500, // mV + .gps_onper_vbat = 5000, // mV + // GPS model control + .gps_airborne = 90000, // Air pressure (Pa) threshold for airborne model .magic = CONFIG_MAGIC_DEFAULT // Do not remove. This is the activation bit. }; diff --git a/tracker/software/drivers/ublox.c b/tracker/software/drivers/ublox.c index 742f3592..a1f07a5e 100644 --- a/tracker/software/drivers/ublox.c +++ b/tracker/software/drivers/ublox.c @@ -30,7 +30,7 @@ const SerialConfig gps_config = */ void gps_transmit_string(uint8_t *cmd, uint8_t length) { gps_calc_ubx_csum(cmd, length); -#if defined(UBLOX_USE_I2C) +#if UBLOX_USE_I2C == TRUE I2C_writeN(UBLOX_MAX_ADDRESS, cmd, length); #elif defined(UBLOX_USE_UART) sdWrite(&SD5, cmd, length); @@ -42,18 +42,18 @@ void gps_transmit_string(uint8_t *cmd, uint8_t length) { * Returns false is there is no byte available else true */ bool gps_receive_byte(uint8_t *data) { - #if defined(UBLOX_USE_I2C) +#if UBLOX_USE_I2C == TRUE uint16_t len; I2C_read16(UBLOX_MAX_ADDRESS, 0xFD, &len); if(len) { I2C_read8(UBLOX_MAX_ADDRESS, 0xFF, data); return true; } - #elif defined(UBLOX_USE_UART) +#else if((*data = sdGetTimeout(&SD5, TIME_IMMEDIATE)) != MSG_TIMEOUT) { return true; } - #endif +#endif return false; } @@ -117,18 +117,20 @@ uint8_t gps_receive_ack(uint8_t class_id, uint8_t msg_id, uint16_t timeout) { * returns the length of the payload * */ -uint16_t gps_receive_payload(uint8_t class_id, uint8_t msg_id, unsigned char *payload, uint16_t timeout) { +uint16_t gps_receive_payload(uint8_t class_id, uint8_t msg_id, + unsigned char *payload, uint16_t timeout) { uint8_t rx_byte; enum {UBX_A, UBX_B, CLASSID, MSGID, LEN_A, LEN_B, PAYLOAD} state = UBX_A; uint16_t payload_cnt = 0; uint16_t payload_len = 0; - sysinterval_t sTimeout = chVTGetSystemTimeX() + TIME_MS2I(timeout); - while(sTimeout >= chVTGetSystemTimeX()) { + sysinterval_t sNow = chVTGetSystemTime(); + + while(chVTIsSystemTimeWithin(sNow, sNow + TIME_MS2I(timeout))) { // Receive one byte if(!gps_receive_byte(&rx_byte)) { - chThdSleep(TIME_MS2I(10)); + chThdSleep(TIME_MS2I(1)); continue; } @@ -168,7 +170,6 @@ uint16_t gps_receive_payload(uint8_t class_id, uint8_t msg_id, unsigned char *pa state = UBX_A; } } - return 0; } @@ -247,10 +248,10 @@ bool gps_get_fix(gpsFix_t *fix) { fix->alt = (uint16_t)alt_tmp; } /* }*/ - TRACE_INFO("GPS > Polling OK time=%04d-%02d-%02d %02d:%02d:%02d lat=%d.%05d lon=%d.%05d alt=%dm sats=%d fixOK=%d pDOP=%d.%02d", + TRACE_INFO("GPS > Polling OK time=%04d-%02d-%02d %02d:%02d:%02d lat=%d.%05d lon=%d.%05d alt=%dm sats=%d fixOK=%d pDOP=%d.%02d model=%d", fix->time.year, fix->time.month, fix->time.day, fix->time.hour, fix->time.minute, fix->time.second, fix->lat/10000000, (fix->lat > 0 ? 1:-1)*(fix->lat/100)%100000, fix->lon/10000000, (fix->lon > 0 ? 1:-1)*(fix->lon/100)%100000, - fix->alt, fix->num_svs, fix->fixOK, fix->pdop/100, fix->pdop%100 + fix->alt, fix->num_svs, fix->fixOK, fix->pdop/100, fix->pdop%100, gps_model ); return true; @@ -374,7 +375,7 @@ uint8_t gps_set_airborne_model(void) { uint8_t model6[] = { 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, // UBX-CFG-NAV5 0xFF, 0xFF, // parameter bitmask - GPS_MODEL_AIRBORNE1, // dynamic model + GPS_MODEL_AIRBORNE1G, // dynamic model 0x03, // fix mode 0x00, 0x00, 0x00, 0x00, // 2D fix altitude 0x10, 0x27, 0x00, 0x00, // 2D fix altitude variance @@ -473,12 +474,12 @@ bool gps_set_model(bool dynamic) { } /* Default to airborne model. */ - if(gps_model == GPS_MODEL_AIRBORNE1) + if(gps_model == GPS_MODEL_AIRBORNE1G) return true; cntr = 3; while((status = gps_set_airborne_model()) == false && cntr--); if(status) { - gps_model = GPS_MODEL_AIRBORNE1; + gps_model = GPS_MODEL_AIRBORNE1G; //TRACE_INFO("GPS > ... Set airborne model OK"); return true; } @@ -528,6 +529,9 @@ bool GPS_Init() { return true; } +/* + * + */ void GPS_Deinit(void) { // Switch MOSFET diff --git a/tracker/software/drivers/ublox.h b/tracker/software/drivers/ublox.h index 123c4e3b..96cd9131 100644 --- a/tracker/software/drivers/ublox.h +++ b/tracker/software/drivers/ublox.h @@ -12,13 +12,17 @@ #define GPS_MODEL_UNSET -1 #define GPS_MODEL_PORTABLE 0 #define GPS_MODEL_STATIONARY 2 -#define GPS_MODEL_AIRBORNE1 6 +#define GPS_MODEL_PEDESTRIAN 3 +#define GPS_MODEL_AUTOMOTIVE 4 +#define GPS_MODEL_SEA 5 +#define GPS_MODEL_AIRBORNE1G 6 +#define GPS_MODEL_AIRBORNE2G 7 +#define GPS_MODEL_AIRBORNE4G 8 #define UBLOX_MAX_ADDRESS 0x42 // You can either use I2C or UART -//#define UBLOX_USE_UART -#define UBLOX_USE_I2C +#define UBLOX_USE_I2C TRUE #define isGPSLocked(pos) ((pos)->type == 3 && (pos)->num_svs >= 4 && (pos)->fixOK == true) @@ -29,13 +33,13 @@ typedef struct { int32_t lat; // latitude in deg * 10^7, range -90 .. +90 * 10^7 int32_t lon; // longitude in deg * 10^7, range -180 .. +180 * 10^7 int32_t alt; // altitude in m, range 0m, up to ~40000m, clamped - bool fixOK; // Flag that is set to true, when DOP is with the limits + bool fixOK; // Flag that is set to true, when DOP is with the limits uint16_t pdop; // Position DOP } gpsFix_t; uint8_t gps_set_gps_only(void); uint8_t gps_disable_nmea_output(void); -bool gps_set_model(bool dynamic); +bool gps_set_model(bool dynamic); uint8_t gps_set_stationary_model(void); uint8_t gps_set_portable_model(void); uint8_t gps_set_airborne_model(void); @@ -48,5 +52,6 @@ bool GPS_Init(void); void GPS_Deinit(void); uint32_t GPS_get_mcu_frequency(void); bool gps_calc_ubx_csum(uint8_t *mbuf, uint16_t mlen); + #endif diff --git a/tracker/software/drivers/usb/commands.c b/tracker/software/drivers/usb/commands.c index cc97391b..ae78ed9f 100644 --- a/tracker/software/drivers/usb/commands.c +++ b/tracker/software/drivers/usb/commands.c @@ -13,12 +13,12 @@ static uint8_t usb_buffer[16*1024] __attribute__((aligned(32))); // USB image buffer const ShellCommand commands[] = { - {"set_trace_level", usb_cmd_set_trace_level}, +/* {"set_trace_level", usb_cmd_set_trace_level},*/ {"trace", usb_cmd_set_trace_level}, /* Short form alias. */ {"picture", usb_cmd_printPicture}, {"print_log", usb_cmd_printLog}, {"config", usb_cmd_printConfig}, - {"aprs_message", usb_cmd_send_aprs_message}, +/* {"aprs_message", usb_cmd_send_aprs_message},*/ {"msg", usb_cmd_send_aprs_message}, /* Short form alias. */ /* {"test_gps", usb_cmd_set_test_gps},*/ #if SHELL_CMD_MEM_ENABLED == TRUE diff --git a/tracker/software/pkt/diagnostics/pktevt.h b/tracker/software/pkt/diagnostics/pktevt.h index 729bc983..f25dabcd 100644 --- a/tracker/software/pkt/diagnostics/pktevt.h +++ b/tracker/software/pkt/diagnostics/pktevt.h @@ -6,6 +6,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/** + * @file pktevt.h + * @brief Packet event tracing. + * + * @addtogroup pktdiag + * @{ + */ + #ifndef PKT_DIAGNOSTICS_PKTEVT_H_ #define PKT_DIAGNOSTICS_PKTEVT_H_ @@ -24,3 +32,5 @@ void pktDisableEventTrace(void); #endif #endif /* PKT_DIAGNOSTICS_PKTEVT_H_ */ + +/** @} */ diff --git a/tracker/software/protocols/packet/aprs.c b/tracker/software/protocols/packet/aprs.c index 3effafb0..7c0c3ebf 100644 --- a/tracker/software/protocols/packet/aprs.c +++ b/tracker/software/protocols/packet/aprs.c @@ -280,7 +280,7 @@ void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len) * @return encoded packet object pointer * @retval NULL if encoding failed */ -packet_t aprs_encode_position(const char *callsign, +packet_t aprs_encode_stamped_position_and_telemetry(const char *callsign, const char *path, aprs_sym_t symbol, dataPoint_t *dataPoint) { ptime_t time; @@ -320,9 +320,9 @@ packet_t aprs_encode_position(const char *callsign, callsign, APRS_DEVICE_CALLSIGN, path, + time.day, time.hour, - time.minute, - time.second); + time.minute); xmit[len+0] = (symbol >> 8) & 0xFF; xmit[len+1] = y3+33; @@ -424,17 +424,14 @@ packet_t aprs_encode_position_and_telemetry(const char *callsign, uint32_t a1 = a / 91; uint32_t a1r = a % 91; - ptime_t time; - unixTimestamp2Date(&time, dataPoint->gps_time); +/* ptime_t time; + unixTimestamp2Date(&time, dataPoint->gps_time);*/ char xmit[256]; - uint32_t len = chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:@%02d%02d%02dz", + uint32_t len = chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:=", callsign, APRS_DEVICE_CALLSIGN, - path, - time.hour, - time.minute, - time.second); + path); uint8_t gpsFix = dataPoint->gps_state == GPS_LOCKED1 || dataPoint->gps_state == GPS_LOCKED2 @@ -807,11 +804,39 @@ msg_t aprs_send_position_response(aprs_identity_t *id, if(argc != 0) return MSG_ERROR; +/* + * TODO: This should just send a request to a TEL service. + * The TEL service should then transmit config data at a spaced interval. + * The same applies to BCN and POS threads which should also use a TEL service. + */ + // Encode and transmit telemetry config first + for(uint8_t type = 0; type < APRS_NUM_TELEM_GROUPS; type++) { + packet_t packet = aprs_encode_telemetry_configuration( + id->call, + id->path, + APRS_DEVICE_CALLSIGN, + type); + if(packet == NULL) { + TRACE_WARN("BCN > No free packet objects for" + " telemetry config transmission"); + } else { + if(!transmitOnRadio(packet, + id->freq, + 0, + 0, + id->pwr, + id->mod, + id->cca)) { + TRACE_ERROR("BCN > Failed to transmit telemetry config"); + } + } + chThdSleep(TIME_S2I(15)); + } /* TODO: Implement a simple (non base 91) position parameter. */ TRACE_INFO("RX > Message: Position query"); dataPoint_t* dataPoint = getLastDataPoint(); - packet_t pp = aprs_encode_position(id->call, + packet_t pp = aprs_encode_stamped_position_and_telemetry(id->call, id->path, id->symbol, dataPoint); @@ -1246,7 +1271,7 @@ packet_t aprs_encode_telemetry_configuration(const char *originator, "UNIT.V,V,W,degC,Pa,1,1,1", false); #endif case 2: return aprs_encode_message(originator, path, destination, - "EQNS.0,.001,0,0,.001,0,0,.001,-4.096,0,.1,-100,0,12.5,500", false); + "EQNS.0,0.001,0,0,0.001,0,0,0.001,-4.096,0,0.1,-100,0,12.5,500", false); case 3: return aprs_encode_message(originator, path, destination, "BITS.11111111,", false); default: return NULL; diff --git a/tracker/software/protocols/packet/aprs.h b/tracker/software/protocols/packet/aprs.h index 1cb7fa4b..a6af9215 100644 --- a/tracker/software/protocols/packet/aprs.h +++ b/tracker/software/protocols/packet/aprs.h @@ -51,6 +51,8 @@ #define SYM_DIGIPEATER 0x2F23 #define SYM_ANTENNA 0x2F72 +#define APRS_NUM_TELEM_GROUPS 4 + #define APRS_HEARD_LIST_SIZE 20 #define APRS_MAX_MSG_ARGUMENTS 10 @@ -87,7 +89,7 @@ extern bool test_gps_enabled; extern "C" { #endif void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len); - packet_t aprs_encode_position(const char *callsign, + packet_t aprs_encode_stamped_position_and_telemetry(const char *callsign, const char *path, aprs_sym_t symbol, dataPoint_t *dataPoint); packet_t aprs_encode_position_and_telemetry(const char *callsign, diff --git a/tracker/software/threads/collector.c b/tracker/software/threads/collector.c index 77188dde..b0c65975 100644 --- a/tracker/software/threads/collector.c +++ b/tracker/software/threads/collector.c @@ -24,6 +24,7 @@ static dataPoint_t dataPoints[2]; static dataPoint_t* lastDataPoint; static bool threadStarted = false; static uint8_t useGPS = 0; +static uint8_t useTEL = 0; /** * Returns most recent data point which is complete. @@ -209,7 +210,7 @@ void getSensors(dataPoint_t* tp) { bme280_error |= 0x10; } #else - /* Set status to "not fitted". */ + /* Set status to "not fitted" for E1 & E2. */ bme280_error |= 0x28; #endif // Measure various temperature sensors @@ -257,7 +258,8 @@ void setSystemStatus(dataPoint_t* tp) { tp->sys_error |= (pac1720_hasError() & 0x3) << 3; tp->sys_error |= (OV5640_hasError() & 0x7) << 5; - tp->sys_error |= (bme280_error & 0x3F) << 8; + tp->sys_error |= (bme280_error & BME_ALL_STATUS_MASK) + << BME_ALL_STATUS_SHIFT; // Set system time tp->sys_time = TIME_I2S(chVTGetSystemTime()); @@ -394,16 +396,17 @@ THD_FUNCTION(collectorThread, arg) { * RTC valid so set tp & ltp from fixed location data. */ TRACE_INFO("COLL > Using fixed location"); + unixTimestamp2Date(&time, tp->gps_time); tp->gps_alt = conf_sram.aprs.tx.alt; tp->gps_lat = conf_sram.aprs.tx.lat; tp->gps_lon = conf_sram.aprs.tx.lon; tp->gps_state = GPS_FIXED; - +/* ltp->gps_time = tp->gps_time; ltp->gps_alt = tp->gps_alt; ltp->gps_lat = tp->gps_lat; ltp->gps_lon = tp->gps_lon; - ltp->gps_state = GPS_FIXED; + ltp->gps_state = GPS_FIXED;*/ } tp->id = ++id; // Serial ID @@ -446,26 +449,47 @@ THD_FUNCTION(collectorThread, arg) { } } -/* +/** + * Telemetry config (Thread) + */ +THD_FUNCTION(configThread, arg) { + uint8_t *useTEL = arg; + while(true) chThdSleep(TIME_S2I(1)); +} + +/** * */ void init_data_collector() { - if(!threadStarted) { + if(!threadStarted) { - threadStarted = true; + threadStarted = true; - TRACE_INFO("COLL > Startup data collector thread"); - thread_t *th = chThdCreateFromHeap(NULL, - THD_WORKING_AREA_SIZE(10*1024), - "TRA", LOWPRIO, - collectorThread, &useGPS); - if(!th) { - // Print startup error, do not start watchdog for this thread - TRACE_ERROR("COLL > Could not start" - " thread (not enough memory available)"); - } else { - chThdSleep(TIME_MS2I(300)); // Wait a little bit until data collector has initialized first dataset - } - } + TRACE_INFO("COLL > Startup data collector thread"); + thread_t *th = chThdCreateFromHeap(NULL, + THD_WORKING_AREA_SIZE(10*1024), + "COL", LOWPRIO, + collectorThread, &useGPS); + if(!th) { + // Print startup error, do not start watchdog for this thread + TRACE_ERROR("COLL > Could not start" + " thread (not enough memory available)"); + } else { + chThdSleep(TIME_MS2I(300)); // Wait a little bit until data collector has initialized first dataset + } + + TRACE_INFO("CFG > Startup telemetry config thread"); + th = chThdCreateFromHeap(NULL, + THD_WORKING_AREA_SIZE(10*1024), + "CFG", LOWPRIO, + configThread, &useTEL); + if(!th) { + // Print startup error, do not start watchdog for this thread + TRACE_ERROR("CFG > Could not start" + " thread (not enough memory available)"); + } else { + chThdSleep(TIME_MS2I(300)); + } + } } diff --git a/tracker/software/threads/collector.h b/tracker/software/threads/collector.h index c570fd69..0be88409 100644 --- a/tracker/software/threads/collector.h +++ b/tracker/software/threads/collector.h @@ -5,18 +5,22 @@ #include "hal.h" #include "ptime.h" +#define BME_STATUS_BITS 2 #define BME_STATUS_MASK 0x3 #define BME_OK_VALUE 0x0 #define BME_FAIL_VALUE 0x1 -#define BME_NOT_FITTED_VALUE 0x02 +#define BME_NOT_FITTED_VALUE 0x2 -#define BMEI1_STATUS_SHIFT 8 +#define BME_ALL_STATUS_MASK 0x3F +#define BME_ALL_STATUS_SHIFT 8 + +#define BMEI1_STATUS_SHIFT BME_ALL_STATUS_SHIFT #define BMEI1_STATUS_MASK (BME_STATUS_MASK << BMEI1_STATUS_SHIFT) -#define BMEE1_STATUS_SHIFT 10 +#define BMEE1_STATUS_SHIFT BMEI1_STATUS_SHIFT + BME_STATUS_BITS #define BMEE1_STATUS_MASK (BME_STATUS_MASK << BMEE1_STATUS_SHIFT) -#define BMEE2_STATUS_SHIFT 12 +#define BMEE2_STATUS_SHIFT BMEI1_STATUS_SHIFT + BME_STATUS)BITS #define BMEE2_STATUS_MASK (BME_STATUS_MASK << BMEE2_STATUS_SHIFT) typedef enum { @@ -73,8 +77,7 @@ typedef struct { uint16_t reset; uint32_t id; // Serial ID uint32_t gps_time; // GPS time - - uint8_t gpio; // GPIO states + //int8_t gps_model; uint32_t sys_time; // System time (in seconds) uint32_t sys_error; // System error flags @@ -90,6 +93,8 @@ typedef struct { * - 10:11 BMEe1 status (0 = OK, 1 = Fail, 2 = Not fitted) * - 12:13 BMEe2 status (0 = OK, 1 = Fail, 2 = Not fitted) */ + + uint8_t gpio; // GPIO states } dataPoint_t; void waitForNewDataPoint(void); diff --git a/tracker/software/threads/rxtx/beacon.c b/tracker/software/threads/rxtx/beacon.c index 9a7d3cf3..d54e0445 100644 --- a/tracker/software/threads/rxtx/beacon.c +++ b/tracker/software/threads/rxtx/beacon.c @@ -38,14 +38,14 @@ THD_FUNCTION(bcnThread, arg) { dataPoint_t* dataPoint = getLastDataPoint(); if(!p_sleep(&conf->thread_conf.sleep_conf)) { - // Telemetry encoding parameter transmission + // Telemetry encoding parameter transmissions if(conf->tx.tel_enc_cycle != 0 && last_conf_transmission + conf->tx.tel_enc_cycle < chVTGetSystemTime()) { TRACE_INFO("BCN > Transmit telemetry configuration"); // Encode and transmit telemetry config packet - for(uint8_t type = 0; type < 4; type++) { + for(uint8_t type = 0; type < APRS_NUM_TELEM_GROUPS; type++) { packet_t packet = aprs_encode_telemetry_configuration( conf->tx.call, conf->tx.path, @@ -64,8 +64,8 @@ THD_FUNCTION(bcnThread, arg) { conf->tx.radio_conf.cca)) { TRACE_ERROR("BCN > Failed to transmit telemetry config"); } - chThdSleep(TIME_S2I(5)); } + chThdSleep(TIME_S2I(15)); } last_conf_transmission += conf->tx.tel_enc_cycle; } diff --git a/tracker/software/threads/rxtx/position.c b/tracker/software/threads/rxtx/position.c index f6f3f1f3..c6b15bb4 100644 --- a/tracker/software/threads/rxtx/position.c +++ b/tracker/software/threads/rxtx/position.c @@ -49,7 +49,7 @@ THD_FUNCTION(posThread, arg) TRACE_INFO("POS > Transmit telemetry configuration"); // Encode and transmit telemetry config packet - for(uint8_t type = 0; type < 4; type++) { + for(uint8_t type = 0; type < APRS_NUM_TELEM_GROUPS; type++) { packet_t packet = aprs_encode_telemetry_configuration( conf->call, conf->path, @@ -68,8 +68,8 @@ THD_FUNCTION(posThread, arg) conf->radio_conf.cca)) { TRACE_ERROR("POS > Failed to transmit telemetry data"); } - chThdSleep(TIME_S2I(5)); } + chThdSleep(TIME_S2I(15)); } last_conf_transmission += conf->tel_enc_cycle; diff --git a/tracker/software/types.h b/tracker/software/types.h index e5bc234a..396bec07 100644 --- a/tracker/software/types.h +++ b/tracker/software/types.h @@ -127,7 +127,25 @@ typedef struct { char call[AX25_MAX_ADDR_LEN]; char path[16]; aprs_sym_t symbol; - radio_squelch_t rssi; // Squelch for CCA check +} thd_tx_conf_t; + +typedef struct { + radio_tx_conf_t radio_conf; + + // Protocol + char call[AX25_MAX_ADDR_LEN]; + char path[16]; + aprs_sym_t symbol; + bool enabled; +} thd_base_conf_t; + +typedef struct { + radio_tx_conf_t radio_conf; + + // Protocol + char call[AX25_MAX_ADDR_LEN]; + char path[16]; + aprs_sym_t symbol; bool enabled; bool beacon; bool gps; @@ -136,17 +154,16 @@ typedef struct { gps_alt_t alt; sysinterval_t cycle; // Beacon interval (0: continously) sysinterval_t tel_enc_cycle; -} thd_tx_conf_t; +} thd_digi_conf_t; /* APRS configuration. */ typedef struct { thread_conf_t thread_conf; - thd_rx_conf_t rx; - thd_tx_conf_t tx; - thd_tx_conf_t base; // Base station receiving unsolicited sends - bool dig_active; // Digipeater active flag - radio_freq_t freq; // Default APRS frequency if no GPS - + thd_rx_conf_t rx; + thd_digi_conf_t tx; + thd_base_conf_t base; // Base station receiving unsolicited sends + bool dig_active; // Digipeater active flag + radio_freq_t freq; // Default APRS frequency if no GPS } thd_aprs_conf_t; typedef struct {