From 07cbb83c7fb1e6ea92bddc82f7b1647e029c640b Mon Sep 17 00:00:00 2001 From: CInsights Date: Tue, 17 Apr 2018 18:04:27 +1000 Subject: [PATCH 1/6] Multiple changes. - Implement parser for embedded APRS message commands. - Update board definitions. - Only lock USB for radio id USB active. - Blue LED blinks on packet decode or fast blinks if paused. - Revise ublox CSUM implementation. - Minor TRACE text changes. --- .../software/.settings/language.settings.xml | 2 +- tracker/software/Makefile | 2 +- tracker/software/board/board.h | 2 +- tracker/software/config.c | 14 +- tracker/software/drivers/ov5640.c | 12 +- tracker/software/drivers/ublox.c | 43 +- tracker/software/drivers/usb/commands.c | 8 +- tracker/software/main.c | 6 +- tracker/software/pkt/channels/rxafsk.c | 25 +- tracker/software/pkt/devices/si446x.c | 4 +- tracker/software/pkt/managers/pktservice.h | 4 +- tracker/software/portab.h | 1 + tracker/software/protocols/packet/aprs.c | 989 +++++++++++++----- tracker/software/protocols/packet/aprs.h | 81 +- tracker/software/threads/rxtx/position.c | 21 +- tracker/software/types.h | 4 + 16 files changed, 871 insertions(+), 347 deletions(-) diff --git a/tracker/software/.settings/language.settings.xml b/tracker/software/.settings/language.settings.xml index 232008d6..8fed0175 100644 --- a/tracker/software/.settings/language.settings.xml +++ b/tracker/software/.settings/language.settings.xml @@ -5,7 +5,7 @@ - + diff --git a/tracker/software/Makefile b/tracker/software/Makefile index 5e51bcdd..802b2e3e 100644 --- a/tracker/software/Makefile +++ b/tracker/software/Makefile @@ -253,7 +253,7 @@ CPPWARN = -Wall -Wextra -Wundef # List all user C define here, like -D_DEBUG=1 UDEFS = -D_GNU_SOURCE -DARM_MATH_CM4 -DSHELL_CMD_TEST_ENABLED=0 \ - -DSHELL_CMD_EXIT_ENABLED=1 -DUSB_TRACE_LEVEL=4 \ + -DSHELL_CMD_EXIT_ENABLED=1 -DUSB_TRACE_LEVEL=5 \ -DSHELL_CMD_MEM_ENABLED=0 # -DDISABLE_HW_WATCHDOG=1 diff --git a/tracker/software/board/board.h b/tracker/software/board/board.h index 7db0621c..be41256c 100644 --- a/tracker/software/board/board.h +++ b/tracker/software/board/board.h @@ -297,7 +297,7 @@ // USB #define LINE_USB_ID PAL_LINE(GPIOA, 10U) -#define LINE_USB_VUSB PAL_LINE(GPIOA, 9U) +#define LINE_USB_VUSB PAL_LINE(GPIOA, 9U) #define LINE_USB_DM PAL_LINE(GPIOA, 11U) #define LINE_USB_DP PAL_LINE(GPIOA, 12U) diff --git a/tracker/software/config.c b/tracker/software/config.c index b59d241c..3abe594a 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -13,7 +13,7 @@ const conf_t conf_flash_default = { .thread_conf = { .active = true, .cycle = TIME_S2I(60*30), - .init_delay = TIME_S2I(10) + .init_delay = TIME_S2I(30) }, .radio_conf = { .pwr = 0x7F, @@ -56,9 +56,9 @@ const conf_t conf_flash_default = { .img_pri = { .thread_conf = { .active = true, - .cycle = TIME_S2I(60*30), - .init_delay = TIME_S2I(60*1), - .send_spacing = TIME_S2I(10) + .cycle = TIME_S2I(60*5), + .init_delay = TIME_S2I(60*5), + .send_spacing = TIME_S2I(5) }, .radio_conf = { .pwr = 0x7F, @@ -141,7 +141,11 @@ const conf_t conf_flash_default = { }, .call = "VK2GJ-5", .path = "WIDE2-1", - .symbol = SYM_DIGIPEATER + .symbol = SYM_DIGIPEATER, + .fixed = true, + .lat = -337331175, + .lon = 1511143478, + .alt = 144 }, .base = { // The base station parameters - how and where tracker originated messages are sent .enabled = true, diff --git a/tracker/software/drivers/ov5640.c b/tracker/software/drivers/ov5640.c index 93d32b7d..7436f232 100644 --- a/tracker/software/drivers/ov5640.c +++ b/tracker/software/drivers/ov5640.c @@ -978,24 +978,26 @@ void vsync_cb(void *arg) { * Other drivers using resources that can cause DMA competition are locked. */ msg_t OV5640_LockResourcesForCapture(void) { - I2C_Lock(); msg_t msg = pktAcquireRadio(PKT_RADIO_1, TIME_INFINITE); if(msg != MSG_OK) { return msg; } + I2C_Lock(); pktPauseReception(PKT_RADIO_1); - //chMtxLock(&trace_mtx); + /* Hold TRACE output on USB. */ + if(isUSBactive()) + chMtxLock(&trace_mtx); return MSG_OK; - /* FIXME: USB has to be locked? */ } /* * Unlock competing drivers. */ void OV5640_UnlockResourcesForCapture(void) { - /* FIXME: USB has to be unlocked? */ - //chMtxUnlock(&trace_mtx); + /* Re-enable TRACE output on USB. */ + if(isUSBactive()) + chMtxUnlock(&trace_mtx); I2C_Unlock(); pktResumeReception(PKT_RADIO_1); pktReleaseRadio(PKT_RADIO_1); diff --git a/tracker/software/drivers/ublox.c b/tracker/software/drivers/ublox.c index 4ec672d3..53b14d2c 100644 --- a/tracker/software/drivers/ublox.c +++ b/tracker/software/drivers/ublox.c @@ -26,21 +26,20 @@ const SerialConfig gps_config = /** * Transmits a string of bytes to the GPS */ -void gps_transmit_string(uint8_t *cmd, uint8_t length) -{ - #if defined(UBLOX_USE_I2C) - I2C_writeN(UBLOX_MAX_ADDRESS, cmd, length); - #elif defined(UBLOX_USE_UART) - sdWrite(&SD5, cmd, length); - #endif +void gps_transmit_string(uint8_t *cmd, uint8_t length) { + gps_calc_ubx_csum(cmd, length); +#if defined(UBLOX_USE_I2C) + I2C_writeN(UBLOX_MAX_ADDRESS, cmd, length); +#elif defined(UBLOX_USE_UART) + sdWrite(&SD5, cmd, length); +#endif } /** * Receives a single byte from the GPS and assigns to supplied pointer. * Returns false is there is no byte available else true */ -bool gps_receive_byte(uint8_t *data) -{ +bool gps_receive_byte(uint8_t *data) { #if defined(UBLOX_USE_I2C) uint16_t len; I2C_read16(UBLOX_MAX_ADDRESS, 0xFD, &len); @@ -190,7 +189,7 @@ bool gps_get_fix(gpsFix_t *fix) { static uint8_t navstatus[32]; // Transmit request - uint8_t navpvt_req[] = {0xB5, 0x62, 0x01, 0x07, 0x00, 0x00, 0x08, 0x19}; + uint8_t navpvt_req[] = {0xB5, 0x62, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00}; gps_transmit_string(navpvt_req, sizeof(navpvt_req)); if(!gps_receive_payload(0x01, 0x07, navpvt, 3000)) { // Receive request @@ -198,7 +197,7 @@ bool gps_get_fix(gpsFix_t *fix) { return false; } - uint8_t navstatus_req[] = {0xB5, 0x62, 0x01, 0x03, 0x00, 0x00, 0x04, 0x0D}; + uint8_t navstatus_req[] = {0xB5, 0x62, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00}; gps_transmit_string(navstatus_req, sizeof(navstatus_req)); if(!gps_receive_payload(0x01, 0x03, navstatus, 3000)) { // Receive request @@ -250,13 +249,22 @@ bool gps_get_fix(gpsFix_t *fix) { fix->time.second = navpvt[10]; fix->lat = (int32_t) ( - (uint32_t)(navpvt[28]) + ((uint32_t)(navpvt[29]) << 8) + ((uint32_t)(navpvt[30]) << 16) + ((uint32_t)(navpvt[31]) << 24) + (uint32_t)(navpvt[28]) + + ((uint32_t)(navpvt[29]) << 8) + + ((uint32_t)(navpvt[30]) << 16) + + ((uint32_t)(navpvt[31]) << 24) ); fix->lon = (int32_t) ( - (uint32_t)(navpvt[24]) + ((uint32_t)(navpvt[25]) << 8) + ((uint32_t)(navpvt[26]) << 16) + ((uint32_t)(navpvt[27]) << 24) + (uint32_t)(navpvt[24]) + + ((uint32_t)(navpvt[25]) << 8) + + ((uint32_t)(navpvt[26]) << 16) + + ((uint32_t)(navpvt[27]) << 24) ); int32_t alt_tmp = (((int32_t) - ((uint32_t)(navpvt[36]) + ((uint32_t)(navpvt[37]) << 8) + ((uint32_t)(navpvt[38]) << 16) + ((uint32_t)(navpvt[39]) << 24)) + ((uint32_t)(navpvt[36]) + + ((uint32_t)(navpvt[37]) << 8) + + ((uint32_t)(navpvt[38]) << 16) + + ((uint32_t)(navpvt[39]) << 24)) ) / 1000); if (alt_tmp <= 0) { fix->alt = 1; @@ -298,7 +306,6 @@ uint8_t gps_disable_nmea_output(void) { 0x00, 0x00 // CRC place holders }; - gps_calc_ubx_csum(nonmea, sizeof(nonmea)); gps_transmit_string(nonmea, sizeof(nonmea)); return gps_receive_ack(0x06, 0x00, 1000); } @@ -338,7 +345,6 @@ uint8_t gps_set_airborne_model(void) { 0x00, 0x00 // CRC place holders }; - gps_calc_ubx_csum(model6, sizeof(model6)); gps_transmit_string(model6, sizeof(model6)); return gps_receive_ack(0x06, 0x24, 1000); } @@ -369,7 +375,6 @@ uint8_t gps_set_power_save(void) { 0x00, 0x00 // CRC place holders }; - gps_calc_ubx_csum(powersave, sizeof(powersave)); gps_transmit_string(powersave, sizeof(powersave)); return gps_receive_ack(0x06, 0x3B, 1000); } @@ -386,7 +391,6 @@ uint8_t gps_power_save(int on) { 0x00, 0x00 // CRC place holders }; - gps_calc_ubx_csum(recvmgmt, sizeof(recvmgmt)); gps_transmit_string(recvmgmt, sizeof(recvmgmt)); return gps_receive_ack(0x06, 0x11, 1000); } @@ -448,14 +452,15 @@ void GPS_Deinit(void) /* * Calculate checksum and inserts into buffer. + * Calling function must allocate space in message buff for csum. * */ bool gps_calc_ubx_csum(uint8_t *mbuf, uint16_t mlen) { uint16_t i; uint8_t ck_a = 0, ck_b = 0; + /* Counting sync bytes there must be at least one byte to checksum. */ if(mlen < 5) - /* Counting sync bytes there must be at least one byte to checksum. */ return false; for (i = 2; i < mlen - 2; i++) { diff --git a/tracker/software/drivers/usb/commands.c b/tracker/software/drivers/usb/commands.c index c91a3b81..7e91e433 100644 --- a/tracker/software/drivers/usb/commands.c +++ b/tracker/software/drivers/usb/commands.c @@ -241,10 +241,10 @@ void usb_cmd_send_aprs_message(BaseSequentialStream *chp, int argc, char *argv[] chprintf(chp, "Message: %s\r\n", m); - /* Send with ack request (last arg false). */ + /* Send with ack request (last arg true). */ packet_t packet = aprs_encode_message(conf_sram.aprs.tx.call, conf_sram.aprs.tx.path, - argv[0], m, false); + argv[0], m, true); if(packet == NULL) { TRACE_WARN("CMD > No free packet objects"); return; @@ -258,9 +258,5 @@ void usb_cmd_send_aprs_message(BaseSequentialStream *chp, int argc, char *argv[] conf_sram.aprs.tx.radio_conf.rssi); chprintf(chp, "Message sent!\r\n"); -/* - (void)argc; - (void)argv; - chprintf(chp, "TODO: Not implemented\r\n");*/ } diff --git a/tracker/software/main.c b/tracker/software/main.c index 56a87c79..6f234acb 100644 --- a/tracker/software/main.c +++ b/tracker/software/main.c @@ -18,12 +18,10 @@ int main(void) { // Init debugging (Serial debug port, LEDs) DEBUG_INIT(); - // This won't actually display since USB isn't initialized yet. - TRACE_INFO("MAIN > Startup"); /* * Setup buffers in CCM if available. - * Setup IO device arbitration. + * Setup packet primary data. */ bool pkt = pktSystemInit(); @@ -43,6 +41,8 @@ int main(void) { startUSB(); #endif + TRACE_INFO("MAIN > Startup"); + // Startup threads start_essential_threads(); // Startup required modules (tracking manager, watchdog) start_user_threads(); // Startup optional modules (eg. POSITION, LOG, ...) diff --git a/tracker/software/pkt/channels/rxafsk.c b/tracker/software/pkt/channels/rxafsk.c index 6935e067..9063b3a0 100644 --- a/tracker/software/pkt/channels/rxafsk.c +++ b/tracker/software/pkt/channels/rxafsk.c @@ -642,11 +642,11 @@ THD_FUNCTION(pktAFSKDecoder, arg) { /* Activity LED blink rate scaling variable. */ uint16_t led_count = 0; -#define DECODER_WAIT_TIME 200U /* 200mS. */ -#define DECODER_IDLE_TIME 2000U /* 2000uS. */ +#define DECODER_WAIT_TIME 100U /* 100mS. */ +//#define DECODER_IDLE_TIME 2000U /* 2000uS. */ #define DECODER_POLL_TIME 10U /* 10mS. */ -#define DECODER_LED_RATE_POLL 100U /* 1000uS. */ -#define DECODER_ACTIVE_TIMEOUT 5U /* 5mS. */ +//#define DECODER_LED_RATE_POLL 100U /* 1000uS. */ +//#define DECODER_ACTIVE_TIMEOUT 5U /* 5mS. */ #define DECODER_SUSPEND_TIME 2000U /* 2000uS. */ #define DECODER_LED_RATE_SUSPEND 250U /* Blink at 250mS during suspend. */ @@ -693,6 +693,7 @@ THD_FUNCTION(pktAFSKDecoder, arg) { /* Something went wrong if we arrive here. */ chSysHalt("ThdExit"); } + /* Toggle decoder LED in wait state. */ pktWriteDecoderLED(PAL_TOGGLE); continue; } @@ -723,12 +724,15 @@ THD_FUNCTION(pktAFSKDecoder, arg) { TIME_MS2I(DECODER_POLL_TIME)); if(fifo_msg != MSG_OK) { - if(++led_count >= DECODER_LED_RATE_POLL) { - /* Toggle decoder LED. */ +/* if(++led_count >= DECODER_LED_RATE_POLL) { + Toggle decoder LED. pktWriteDecoderLED(PAL_TOGGLE); led_count = 0; - } - /* No FIFO object posted so loop again. */ + }*/ + /* + * No FIFO object posted so loop. + * Go back through IDLE and check for STOP event. + */ myDriver->decoder_state = DECODER_IDLE; break; } @@ -796,8 +800,9 @@ THD_FUNCTION(pktAFSKDecoder, arg) { byte_packed_pwm_t data; size_t n = iqReadTimeout(myQueue, data.bytes, sizeof(packed_pwm_counts_t), - TIME_MS2I(DECODER_ACTIVE_TIMEOUT)); - /* TODO: Timeout to be calculated from SYMBOL time x (8?). */ + chTimeUS2I(833 * 8) + /*TIME_MS2I(DECODER_ACTIVE_TIMEOUT)*/); + /* Timeout calculated as SYMBOL time x 8. */ if(n == sizeof(packed_pwm_counts_t)) { array_min_pwm_counts_t radio; diff --git a/tracker/software/pkt/devices/si446x.c b/tracker/software/pkt/devices/si446x.c index 8f8935bf..5ac39eca 100644 --- a/tracker/software/pkt/devices/si446x.c +++ b/tracker/software/pkt/devices/si446x.c @@ -589,7 +589,7 @@ static bool Si446x_transmit(radio_unit_t radio, } /* Try to get clear channel. */ - TRACE_INFO( "SI > Wait maximum of %.1f seconds for clear channel on" + TRACE_INFO( "SI > Run CCA for %.1f seconds on" " %d.%03d MHz", (float32_t)(TIME_I2MS(cca_timeout) / 1000), op_freq/1000000, (op_freq%1000000)/1000); @@ -601,7 +601,7 @@ static bool Si446x_transmit(radio_unit_t radio, chThdSleep(TIME_MS2I(1)); } /* Clear channel timing. */ - TRACE_INFO( "SI > CCA time = %d milliseconds", + TRACE_INFO( "SI > CCA completed in %d milliseconds", chTimeI2MS(chVTTimeElapsedSinceX(t0))); } diff --git a/tracker/software/pkt/managers/pktservice.h b/tracker/software/pkt/managers/pktservice.h index d848e71b..615ed0f6 100644 --- a/tracker/software/pkt/managers/pktservice.h +++ b/tracker/software/pkt/managers/pktservice.h @@ -21,8 +21,8 @@ #define PKT_SEND_BUFFER_SEM_NAME "pbsem" -#define PKT_CALLBACK_WA_SIZE 8192 -#define PKT_TERMINATOR_WA_SIZE 1024 +#define PKT_CALLBACK_WA_SIZE (1024 * 10) +#define PKT_TERMINATOR_WA_SIZE (1024 * 1) /*===========================================================================*/ /* Module data structures and types. */ diff --git a/tracker/software/portab.h b/tracker/software/portab.h index d15c49fe..fa35526a 100644 --- a/tracker/software/portab.h +++ b/tracker/software/portab.h @@ -67,6 +67,7 @@ #endif //#define LINE_PWM_MIRROR PAL_LINE(GPIOA, 8U) +#define LINE_GPIO_PIN PAL_LINE(GPIOA, 8U) /** * ICU related definitions. diff --git a/tracker/software/protocols/packet/aprs.c b/tracker/software/protocols/packet/aprs.c index fb824770..40479f0f 100644 --- a/tracker/software/protocols/packet/aprs.c +++ b/tracker/software/protocols/packet/aprs.c @@ -39,7 +39,7 @@ static uint16_t msg_id; char alias_re[] = "WIDE[4-7]-[1-7]|CITYD"; char wide_re[] = "WIDE[1-7]-[1-7]"; enum preempt_e preempt = PREEMPT_OFF; -static heard_t heard_list[20]; +static heard_t heard_list[APRS_HEARD_LIST_SIZE]; static bool dedupe_initialized; const conf_command_t command_list[] = { @@ -172,6 +172,81 @@ const conf_command_t command_list[] = { {TYPE_NULL} }; +/* + * Table of commands that can be embedded in a message. + */ +const APRSCommand aprs_commands[] = { + {"?aprsd", aprs_send_aprsd_message}, + {"?aprsh", aprs_send_aprsh_message}, + {"?aprsp", aprs_send_position_beacon}, + {"?gpio", aprs_handle_gpio_command}, + {"?gps", aprs_handle_gps_command}, + {"?reset", aprs_execute_system_reset}, + {"?save", aprs_execute_config_save}, + {"?img", aprs_execute_img_command}, + {"?config", aprs_execute_config_command}, + {NULL, NULL} +}; + +/* + * Parse command arguments from aprs message. + */ +static char *aprs_parse_arguments(char *str, char **saveptr) { + char *p; + + if (str != NULL) + *saveptr = str; + + p = *saveptr; + if (!p) { + return NULL; + } + + /* Skipping white space.*/ + p += strspn(p, " \t"); + + if (*p == '"') { + /* If an argument starts with a double quote then its delimiter is another + quote.*/ + p++; + *saveptr = strpbrk(p, "\""); + } + else { + /* The delimiter is white space.*/ + *saveptr = strpbrk(p, " \t"); + } + + /* Replacing the delimiter with a zero.*/ + if (*saveptr != NULL) { + *(*saveptr)++ = '\0'; + } + + return *p != '\0' ? p : NULL; +} + +/* + * Execute a command found in an aprs message. + * Return result of command. + * False means either command failed or does not exist. + */ +static msg_t aprs_cmd_exec(const APRSCommand *acp, + char *name, + aprs_identity_t *id, + int argc, + char *argv[]) { + + while (acp->ac_name != NULL) { + if (strcmp(acp->ac_name, name) == 0) { + return acp->ac_function(id, argc, argv); + } + acp++; + } + return MSG_TIMEOUT; +} + +/* + * + */ void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len) { // Decode packet @@ -193,19 +268,27 @@ void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len) } /** - * Transmit APRS position packet. The comments are filled with: - * - Static comment (can be set in config.h) - * - Battery voltage in mV - * - Solar voltage in mW (if tracker is solar-enabled) - * - Temperature in Celcius - * - Air pressure in Pascal - * - Number of satellites being used - * - Number of cycles where GPS has been lost (if applicable in cycle) + * @brief Transmit APRS position packet. + * @notes The comments are filled with: + * @notes - Static comment (can be set in config.h) + * @notes - Battery voltage in mV + * @notes - Solar voltage in mW (if tracker is solar-enabled) + * @notes - Temperature in Celcius + * @notes - Air pressure in Pascal + * @notes - Number of satellites being used + * @notes - Number of cycles where GPS has been lost (if applicable in cycle) + * + * @param[in] callsign originator calls sign + * @param[in] path path to use + * @param[in] symbol symbol for originator + * @param[in] dataPoint position data object + * + * @return encoded packet object pointer + * @retval NULL if encoding failed */ packet_t aprs_encode_position(const char *callsign, const char *path, uint16_t symbol, - dataPoint_t *dataPoint) -{ + dataPoint_t *dataPoint) { // Latitude uint32_t y = 380926 * (90 - dataPoint->gps_lat/10000000.0); uint32_t y3 = y / 753571; @@ -236,7 +319,7 @@ packet_t aprs_encode_position(const char *callsign, char xmit[256]; uint32_t len = chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:!", - callsign, APRS_DEST_CALLSIGN, path); + callsign, APRS_DEVICE_CALLSIGN, path); xmit[len+0] = (symbol >> 8) & 0xFF; xmit[len+1] = y3+33; @@ -289,39 +372,61 @@ packet_t aprs_encode_data_packet(const char *callsign, const char *path, { char xmit[256]; chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:{{%c%s", callsign, - APRS_DEST_CALLSIGN, path, packetType, data); + APRS_DEVICE_CALLSIGN, path, packetType, data); return ax25_from_text(xmit, true); } /** - * Transmit message packet + * @brief Transmit message packet + * + * @param[in] originator call sign of originator of this message + * @param[in path path for message + * @param[in] recipient call sign of recipient + * @param[in text text of the message + * @param[in] ack true if message acknowledgment requested */ -packet_t aprs_encode_message(const char *callsign, const char *path, - const char *receiver, const char *text, - const bool noCounter) { +packet_t aprs_encode_message(const char *originator, const char *path, + const char *recipient, const char *text, + const bool ack) { char xmit[256]; if((strlen(text) > AX25_MAX_APRS_MSG_LEN) || (strpbrk(text, "|~{") != NULL)) /* Invalid message. */ return NULL; - if(noCounter) - chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s", callsign, - APRS_DEST_CALLSIGN, path, receiver, text); + if(!ack) + chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s", + originator, + APRS_DEVICE_CALLSIGN, + path, + recipient, + text); else - chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s{%d", callsign, - APRS_DEST_CALLSIGN, path, receiver, text, ++msg_id); + chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s{%d", + originator, + APRS_DEVICE_CALLSIGN, + path, + recipient, + text, + ++msg_id); return ax25_from_text(xmit, true); } -packet_t aprs_encode_query_answer_aprsd(const char *callsign, - const char *path, - const char *receiver) -{ +/* + * @brief Compose an APRSD message + * @notes Used by position service. + * + * @param[in] originator originator of this message + * @param[in] path path to use + * @param[in] recipient identity that requested the message + */ +packet_t aprs_compose_aprsd_message(const char *originator, + const char *path, + const char *recipient) { char buf[256] = "Directs="; - uint32_t out = 8; - for(uint8_t i=0; i<20; i++) { + uint32_t out = strlen(buf); + for(uint8_t i = 0; i < APRS_HEARD_LIST_SIZE; i++) { if(heard_list[i].time && heard_list[i].time + TIME_S2I(600) >= chVTGetSystemTime() && heard_list[i].time <= chVTGetSystemTime()) @@ -330,267 +435,606 @@ packet_t aprs_encode_query_answer_aprsd(const char *callsign, } buf[out-1] = 0; // Remove last space - return aprs_encode_message(callsign, path, receiver, buf, true); + return aprs_encode_message(originator, path, recipient, buf, false); } +/* + * @brief Encode and send an APRSD message + * + * @param[in] id aprs node identity + * @param[in] argc number of parameters + * @param[in] argv array of pointers to parameter strings + * + * @return result of command + * @retval MSG_OK if the command completed. + * @retval MSG_ERROR if there was an error. + */ +msg_t aprs_send_aprsd_message(aprs_identity_t *id, + int argc, char *argv[]) { + (void)argc; + (void)argv; + + packet_t pp = aprs_compose_aprsd_message(id->call, id->path, id->src); + if(pp == NULL) { + TRACE_WARN("RX > No free packet objects or badly formed message"); + return MSG_ERROR; + } + if(!transmitOnRadio(pp, + id->freq, + 0, + 0, + id->pwr, + id->mod, + id->rssi)) { + TRACE_ERROR("RX > Transmit of APRSD failed"); + return MSG_ERROR; + } + return MSG_OK; +} + +/* + * @brief Encode and send an APRSH message + * + * @param[in] id aprs node identity + * @param[in] argc number of parameters + * @param[in] argv array of pointers to parameter strings + * + * @return result of command + * @retval MSG_OK if the command completed. + * @retval MSG_ERROR if there was an error. + */ +msg_t aprs_send_aprsh_message(aprs_identity_t *id, + int argc, char *argv[]) { + if(argc != 1) + return MSG_ERROR; + char buf[AX25_MAX_APRS_MSG_LEN + 1]; + uint32_t out = 0; + strupr(argv[0]); + for(uint8_t i = 0; i < APRS_HEARD_LIST_SIZE; i++) { + if(heard_list[i].time && (strncmp(heard_list[i].call, argv[0], + strlen(argv[0])) == 0)) { + /* Convert time to human readable form. */ + time_secs_t diff = chTimeI2S(chVTTimeElapsedSinceX(heard_list[i].time)); + out = chsnprintf(buf, sizeof(buf), + "%s heard %02i:%02i ago", + heard_list[i].call, diff/60, diff % 60); + break; + } + if(out == 0) { + out = chsnprintf(buf, sizeof(buf), + "%s not heard", heard_list[i].call); + } + } + packet_t pp = aprs_encode_message(id->call, id->path, id->src, buf, false); + if(pp == NULL) { + TRACE_WARN("RX > No free packet objects or badly formed message"); + return MSG_ERROR; + } + if(!transmitOnRadio(pp, + id->freq, + 0, + 0, + id->pwr, + id->mod, + id->rssi)) { + TRACE_ERROR("RX > Transmit of APRSH failed"); + return MSG_ERROR; + } + return MSG_OK; +} + +/* + * @brief Handle GPIO set/clear/query + * + * @param[in] id aprs node identity + * @param[in] argc number of parameters + * @param[in] argv array of pointers to parameter strings + * + * @return result of command + * @retval MSG_OK if the command completed. + * @retval MSG_ERROR if there was an error. + */ +msg_t aprs_handle_gpio_command(aprs_identity_t *id, + int argc, char *argv[]) { + if(argc != 1) + return MSG_ERROR; + + if(!strcmp(argv[0], "pa8:1")) { + TRACE_INFO("RX > Message: GPIO query PA8 HIGH"); + palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL); + palSetPad(GPIOA, 8); + return MSG_OK; + } + + if(!strcmp(argv[0], "pa8:0")) { + TRACE_INFO("RX > Message: GPIO query PA8 LOW"); + palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL); + palClearPad(GPIOA, 8); + return MSG_OK; + } + + if(!strcmp(argv[0], "pa8:?")) { + char buf[AX25_MAX_APRS_MSG_LEN + 1]; + /* TODO: Need to read mode and if not output then report as "input" etc. */ + chsnprintf(buf, sizeof(buf), + "PA8 is %s ", + (palReadPad(GPIOA, 8) == PAL_HIGH) ? "HIGH" : "LOW"); + + packet_t pp = aprs_encode_message(id->call, id->path, id->src, buf, false); + if(pp == NULL) { + TRACE_WARN("RX > No free packet objects or badly formed message"); + return MSG_ERROR; + } + if(!transmitOnRadio(pp, + id->freq, + 0, + 0, + id->pwr, + id->mod, + id->rssi)) { + TRACE_ERROR("RX > Transmit of GPIO status failed"); + return MSG_ERROR; + } + return MSG_OK; + } + return MSG_ERROR; +} + +/* + * @brief Handle GPS command + * + * @param[in] id aprs node identity + * @param[in] argc number of parameters + * @param[in] argv array of pointers to parameter strings + * + * @return result of command + * @retval MSG_OK if the command completed. + * @retval MSG_ERROR if there was an error. + */ +msg_t aprs_handle_gps_command(aprs_identity_t *id, + int argc, char *argv[]) { + if(argc != 1) + return MSG_ERROR; + + if(!strcmp(argv[0], "fixed")) { + TRACE_INFO("RX > Message: GPS set to fixed location"); + test_gps_enabled = true; + return MSG_OK; + } + + if(!strcmp(argv[0], "normal")) { + TRACE_INFO("RX > Message: GPS set to normal operation"); + test_gps_enabled = false; + return MSG_OK; + } + + if(!strcmp(argv[0], "status")) { + char buf[AX25_MAX_APRS_MSG_LEN + 1]; + /* TODO: Need to read mode and if not output then report as "input" etc. */ + chsnprintf(buf, sizeof(buf), + "GPS is %s", + test_gps_enabled ? "fixed" : "normal"); + + packet_t pp = aprs_encode_message(id->call, id->path, id->src, buf, false); + if(pp == NULL) { + TRACE_WARN("RX > No free packet objects or badly formed message"); + return MSG_ERROR; + } + if(!transmitOnRadio(pp, + id->freq, + 0, + 0, + id->pwr, + id->mod, + id->rssi)) { + TRACE_ERROR("RX > Transmit of GPS status failed"); + return MSG_ERROR; + } + return MSG_OK; + } + return MSG_ERROR; +} + +/** +* @brief Request for position beacon to be sent +* +* @param[in] id aprs node identity +* @param[in] argc number of parameters +* @param[in] argv array of pointers to parameter strings +* +* @return result of command +* @retval MSG_OK if the command completed. +* @retval MSG_ERROR if there was an error. +*/ +msg_t aprs_send_position_beacon(aprs_identity_t *id, + int argc, char *argv[]) { + (void)argv; + + if(argc != 0) + return MSG_ERROR; + + TRACE_INFO("RX > Message: Position query"); + dataPoint_t* dataPoint = getLastDataPoint(); + packet_t pp = aprs_encode_position(id->call, + id->path, + id->symbol, + dataPoint); + if(pp == NULL) { + TRACE_WARN("RX > No free packet objects or badly formed message"); + return MSG_ERROR; + } + if(!transmitOnRadio(pp, + id->freq, + 0, + 0, + id->pwr, + id->mod, + id->rssi)) { + TRACE_ERROR("RX > Transmit of APRSD failed"); + return MSG_ERROR; + } + return MSG_OK; +} + +/** +* @brief Request for system reset +* +* @param[in] id aprs node identity +* @param[in] argc number of parameters +* @param[in] argv array of pointers to parameter strings +* +* @return result of command +* @retval MSG_ERROR if there was an error. +*/ +msg_t aprs_execute_system_reset(aprs_identity_t *id, + int argc, char *argv[]) { + (void)argv; + + if(argc != 0) + return MSG_ERROR; + + TRACE_INFO("RX > Message: System Reset"); + char buf[16]; + chsnprintf(buf, sizeof(buf), "ack%s", id->num); + packet_t pp = aprs_encode_message(id->call, + id->path, + id->src, buf, false); + if(pp == NULL) { + TRACE_WARN("RX > No free packet objects"); + return MSG_ERROR; + } + transmitOnRadio(pp, + id->freq, + 0, + 0, + id->pwr, + id->mod, + id->rssi); + + chThdSleep(TIME_S2I(10)); + + NVIC_SystemReset(); + /* We don't arrive here. */ + return MSG_OK; +} + +/* + * @brief Handle config command + * + * @param[in] id aprs node identity + * @param[in] argc number of parameters + * @param[in] argv array of pointers to parameter strings + * + * @return result of command + * @retval MSG_OK if the command completed. + * @retval MSG_ERROR if there was an error. + */ +msg_t aprs_execute_config_command(aprs_identity_t *id, + int argc, char *argv[]) { + (void)id; + + if(argc != 2) + return MSG_ERROR; + + for(uint8_t i=0; command_list[i].type != TYPE_NULL; i++) { + if(!strncmp(argv[1], command_list[i].name, + strlen(command_list[i].name))) { + + /* Parameter being changed is in argv[0], new value is in argv[1]. */ + TRACE_INFO("RX > Message: Configuration Command"); + TRACE_INFO("RX > %s => %s", argv[1], argv[1]); + + if(command_list[i].type == TYPE_INT + && command_list[i].size == 1) { + *((uint8_t*)command_list[i].ptr) = atoi(argv[1]); + } else if(command_list[i].type == TYPE_INT + && command_list[i].size == 2) { + *((uint16_t*)command_list[i].ptr) = atoi(argv[1]); + } else if(command_list[i].type == TYPE_INT + && command_list[i].size == 4) { + *((uint32_t*)command_list[i].ptr) = atoi(argv[1]); + } else if(command_list[i].type == TYPE_TIME) { + *((sysinterval_t*)command_list[i].ptr) = + TIME_MS2I(atoi(argv[2])); + } else if(command_list[i].type == TYPE_STR) { + strncpy((char*)command_list[i].ptr, argv[1], + sizeof(command_list[i].size)-1); + } + return MSG_OK; + } /* Next parameter. */ + } /* Parameter not found. */ + return MSG_ERROR; +} + +/** +* @brief Request configuration save to flash +* +* @param[in] id aprs node identity +* @param[in] argc number of parameters +* @param[in] argv array of pointers to parameter strings +* +* @return result of command +* @retval MSG_ERROR if there was an error. +*/ +msg_t aprs_execute_config_save(aprs_identity_t *id, + int argc, char *argv[]) { + (void)id; + (void)argc; + (void)argv; + + TRACE_INFO("RX > Message: Config Save"); + conf_sram.magic = CONFIG_MAGIC_UPDATED; + flashSectorBegin(flashSectorAt(0x08060000)); + flashErase(0x08060000, 0x20000); + flashWrite(0x08060000, (char*)&conf_sram, sizeof(conf_t)); + flashSectorEnd(flashSectorAt(0x08060000)); + return MSG_OK; +} + +/* + * @brief Handle Image command + * + * @param[in] id aprs node identity + * @param[in] argc number of parameters + * @param[in] argv array of pointers to parameter strings + * + * @return result of command + * @retval MSG_OK if the command completed. + * @retval MSG_ERROR if there was an error. + */ +msg_t aprs_execute_img_command(aprs_identity_t *id, + int argc, char *argv[]) { + (void)id; + if(argc < 2) + return MSG_ERROR; + + if(!strcmp(argv[0], "reject") && argc == 2) { + if(!strcmp(argv[1], "pri")) { + reject_pri = true; + TRACE_INFO("RX > Message: Image reject pri"); + return MSG_OK; + } + if(!strcmp(argv[1], "sec")) { + reject_sec = true; + TRACE_INFO("RX > Message: Image reject sec"); + return MSG_OK; + } + return MSG_ERROR; + } + + if(!strcmp(argv[0], "repeat")) { + TRACE_INFO("RX > Message: Image packet repeat request"); + + /* Start at arg 2. */ + int c = 2; + while(c <= argc) { + uint32_t req = strtol(argv[c++], NULL, 16); + for(uint8_t i = 0; i < 16; i++) { + /* Find an empty repeat slot. */ + if(!packetRepeats[i].n_done) { + packetRepeats[i].image_id = (req >> 16) & 0xFF; + packetRepeats[i].packet_id = req & 0xFFFF; + packetRepeats[i].n_done = true; + + TRACE_INFO("RX > ... Image %3d Packet %3d", + packetRepeats[i].image_id, + packetRepeats[i].packet_id); + break; + } /* Not an empty slot. */ + } /* No more slots. */ + } /* No more image IDs. */ + return MSG_OK; + } + /* Unknown parameter. */ + return MSG_ERROR; +} + +/* + * @brief Decode APRS content and check for message + * + * @param[in] pp an APRS packet object + * + * @return result of command + * @retval true if not a message or not addressed to any node on this device. + * in that case the APRS content can be digipeated. + * @retval false if this was a message for a node on this device. + * in that case the APRS content should not be digipeated. + */ static bool aprs_decode_message(packet_t pp) { - // Get Info field - char src[127]; - unsigned char *pinfo; - if(ax25_get_info(pp, &pinfo) == 0) - return false; - ax25_format_addrs(pp, src, sizeof(src)); + // Get Info field + char src[127]; + unsigned char *pinfo; + if(ax25_get_info(pp, &pinfo) == 0) + return false; - // Decode destination callsign - char dest[AX25_MAX_ADDR_LEN]; - uint8_t i=0; + ax25_format_addrs(pp, src, sizeof(src)); - while(i < sizeof(dest)-1) { - if(pinfo[i+1] == ':' || pinfo[i+1] == ' ') { - dest[i++] = 0; - break; - } - dest[i] = pinfo[i+1]; - i++; - } + /* Decode destination call sign. */ + char dest[AX25_MAX_ADDR_LEN]; + uint8_t i = 0; - // Decode source callsign - for(uint32_t i=0; i < sizeof(src); i++) { - if(src[i] == '>') { - src[i] = 0; - break; - } - } + while(i < sizeof(dest) - 1) { + if(pinfo[i+1] == ':' || pinfo[i+1] == ' ') { + dest[i++] = 0; + break; + } + dest[i] = pinfo[i+1]; + i++; + } + /* Convert destination call sign to upper case. */ + strupr(dest); - /* Check if this message is meant for us. */ - bool pos_pri = !strcmp(conf_sram.pos_pri.call, dest) - && (conf_sram.pos_pri.aprs_msg) - && (conf_sram.pos_pri.thread_conf.active); - bool pos_sec = !strcmp(conf_sram.pos_sec.call, dest) - && (conf_sram.pos_sec.aprs_msg) - && (conf_sram.pos_sec.thread_conf.active); - bool aprs_rx = !strcmp(conf_sram.aprs.rx.call, dest) - && (conf_sram.aprs.thread_conf.active); - bool aprs_tx = !strcmp(conf_sram.aprs.tx.call, dest) - && (conf_sram.aprs.thread_conf.active) - && (conf_sram.aprs.dig_active); + /* Decode source call sign. */ + for(uint32_t i = 0; i < sizeof(src); i++) { + if(src[i] == '>') { + src[i] = 0; + break; + } + } + /* Convert source call sign to upper case. */ + strupr(src); - if((pinfo[10] == ':') && (pos_pri || pos_sec || aprs_rx || aprs_tx)) { - char msg_id_rx[8]; - memset(msg_id_rx, 0, sizeof(msg_id_rx)); + /* + * Setup default responding node identity. + * Default identity id set to tx. + */ - // Cut off control chars - for(uint16_t i = 11; pinfo[i] != 0 && i < AX25_MAX_APRS_MSG_LEN + 11; i++) { - /* FIXME: Trim trailing spaces before {. */ - if(pinfo[i] == '{') { - // Copy ACK ID - memcpy(msg_id_rx, &pinfo[i+1], sizeof(msg_id_rx)-1); - // Cut off non-printable chars - for(uint8_t j=0; j 126) { - msg_id_rx[j] = 0; - break; - } - } - pinfo[i] = 0; // Mark end of message - } - if(pinfo[i] == '\r' || pinfo[i] == '\n') { - pinfo[i] = 0; - } - } + aprs_identity_t identity = {0}; - char *command = strlwr((char*)&pinfo[11]); + strcpy(identity.src, src); + strcpy(identity.call, conf_sram.aprs.tx.call); + /* TODO: define a length for path. */ + strcpy(identity.path, conf_sram.aprs.tx.path); + identity.symbol = conf_sram.aprs.tx.symbol; + identity.freq = conf_sram.aprs.tx.radio_conf.freq; + identity.pwr = conf_sram.aprs.tx.radio_conf.pwr; + identity.mod = conf_sram.aprs.tx.radio_conf.mod; + identity.rssi = conf_sram.aprs.tx.radio_conf.rssi; - // Trace - TRACE_INFO("RX > Received message from %s (ID=%s): %s [%s]", - src, msg_id_rx, &pinfo[11], command); + /* Check which nodes are enabled to accept aprs messages. */ + bool pos_pri = !strcmp(conf_sram.pos_pri.call, dest) + && (conf_sram.pos_pri.aprs_msg) + && (conf_sram.pos_pri.thread_conf.active); - // Do control actions - if(!strcmp(command, "?gpio pa8:1")) { // Switch on pin + if(pos_pri) { + strcpy(identity.call, conf_sram.pos_pri.call); + strcpy(identity.path, conf_sram.pos_pri.path); + identity.symbol = conf_sram.pos_pri.symbol; + } - TRACE_INFO("RX > Message: GPIO query PA8 HIGH"); - palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL); - palSetPad(GPIOA, 8); + bool pos_sec = !strcmp(conf_sram.pos_sec.call, dest) + && (conf_sram.pos_sec.aprs_msg) + && (conf_sram.pos_sec.thread_conf.active); + if(pos_sec) { + strcpy(identity.call, conf_sram.pos_sec.call); + strcpy(identity.path, conf_sram.pos_sec.path); + identity.symbol = conf_sram.pos_sec.symbol; + } - } else if(!strcmp(command, "?gpio pa8:0")) { // Switch off pin + bool aprs_rx = !strcmp(conf_sram.aprs.rx.call, dest) + && (conf_sram.aprs.thread_conf.active); + if(aprs_rx) { + /* Parameters come from tx. */ + } - TRACE_INFO("RX > Message: GPIO query PA8 LOW"); - palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL); - palClearPad(GPIOA, 8); + bool aprs_tx = !strcmp(conf_sram.aprs.tx.call, dest) + && (conf_sram.aprs.thread_conf.active) + && (conf_sram.aprs.dig_active); + /* Default already set for tx. */ - } else if(!strcmp(command, "?gps fixed")) { // Use fixed gps coordinates + /* Check if this is message and address is one of the nodes on this device. */ + if(!((pinfo[10] == ':') && (pos_pri || pos_sec || aprs_rx || aprs_tx))) { + /* + * Not a command or not addressed to one of the active nodes on this device. + * Flag that message should be digipeated. + */ + return true; + } - TRACE_INFO("RX > Message: GPS fixed location"); - test_gps_enabled = true; + /* Proceed with command analysis. */ + char msg_id_rx[8] = {0}; + //memset(msg_id_rx, 0, sizeof(msg_id_rx)); - } else if(!strcmp(command, "?gps normal")) { // Use fixed gps coordinates + // Cut off control chars + for(uint16_t i = 11; pinfo[i] != 0 + && i < (AX25_MAX_APRS_MSG_LEN + 11); i++) { + /* FIXME: Trim trailing spaces before {. */ + if(pinfo[i] == '{') { + // Copy ACK ID + memcpy(msg_id_rx, &pinfo[i+1], sizeof(msg_id_rx)-1); + // Cut off non-printable chars + for(uint8_t j=0; j 126) { + msg_id_rx[j] = 0; + break; + } + } + pinfo[i] = 0; // Mark end of message + } + if(pinfo[i] == '\r' || pinfo[i] == '\n') { + pinfo[i] = 0; + } + } - TRACE_INFO("RX > Message: GPS acquire location"); - test_gps_enabled = false; + strcpy(identity.num, msg_id_rx); - } else if(!strcmp(command, "?aprsp")) { // Transmit position - /* - * There is no reply sent to the src (ack can be sent). - * Just a position sent from the APRS TX identity. - * The identity could be the same as one of the POS identities. - */ - TRACE_INFO("RX > Message: Position query"); - dataPoint_t* dataPoint = getLastDataPoint(); - packet_t pp = aprs_encode_position(conf_sram.aprs.tx.call, - conf_sram.aprs.tx.path, - conf_sram.aprs.tx.symbol, - dataPoint); - if(pp == NULL) { - TRACE_WARN("RX > No free packet objects"); - return false; - } - transmitOnRadio(pp, - conf_sram.aprs.tx.radio_conf.freq, - 0, - 0, - conf_sram.aprs.tx.radio_conf.pwr, - conf_sram.aprs.tx.radio_conf.mod, - conf_sram.aprs.tx.radio_conf.rssi); + /* Convert command string to lower case. */ + char *astrng = strlwr((char*)&pinfo[11]); - } else if(!strcmp(command, "?aprsd")) { // Transmit position + // Trace + TRACE_INFO("RX > Received message from %s (ID=%s): %s [%s]", + src, msg_id_rx, &pinfo[11], astrng); - TRACE_INFO("RX > Message: Directs query"); - packet_t pp = - aprs_encode_query_answer_aprsd(conf_sram.aprs.tx.call, - conf_sram.aprs.tx.path, src); - if(pp == NULL) { - TRACE_WARN("RX > No free packet objects or badly formed message"); - return false; - } - transmitOnRadio(pp, - conf_sram.aprs.tx.radio_conf.freq, - 0, - 0, - conf_sram.aprs.tx.radio_conf.pwr, - conf_sram.aprs.tx.radio_conf.mod, - conf_sram.aprs.tx.radio_conf.rssi); + /* Parse arguments. */ + char *lp, *cmd, *tokp; + char *args[APRS_MAX_MSG_ARGUMENTS]; + lp = aprs_parse_arguments(astrng, &tokp); + /* The command itself. */ + cmd = lp; + int n = 0; + while ((lp = aprs_parse_arguments(NULL, &tokp)) != NULL) { + if (n >= APRS_MAX_MSG_ARGUMENTS) { + TRACE_INFO("RX > Too many APRS command arguments"); + cmd = NULL; + break; + } + args[n++] = lp; + } - } else if(!strcmp(command, "?reset")) { // Transmit position + /* Parse and execute command. */ + msg_t msg = aprs_cmd_exec(aprs_commands, cmd, &identity, n, args); - TRACE_INFO("RX > Message: System Reset"); - char buf[16]; - chsnprintf(buf, sizeof(buf), "ack%s", msg_id_rx); - packet_t pp = aprs_encode_message(conf_sram.aprs.tx.call, - conf_sram.aprs.tx.path, - src, buf, true); - if(pp == NULL) { - TRACE_WARN("RX > No free packet objects"); - return false; - } - transmitOnRadio(pp, - conf_sram.aprs.tx.radio_conf.freq, - 0, - 0, - conf_sram.aprs.tx.radio_conf.pwr, - conf_sram.aprs.tx.radio_conf.mod, - conf_sram.aprs.tx.radio_conf.rssi); - chThdSleep(TIME_S2I(5)); // Give some time to send the message + if(msg == MSG_TIMEOUT) { + TRACE_INFO("RX > Command not found by parser"); + } - NVIC_SystemReset(); + if(msg_id_rx[0]) { + /* Incoming message ID exists so an ACK or REJ has to be sent. */ + char buf[16]; + chsnprintf(buf, sizeof(buf), "%s%s", + (msg == MSG_OK) ? "ack" : "rej", msg_id_rx); - } else if(!strcmp(command, "?save")) { // Transmit position - - TRACE_INFO("RX > Message: Save"); - conf_sram.magic = CONFIG_MAGIC_UPDATED; - flashSectorBegin(flashSectorAt(0x08060000)); - flashErase(0x08060000, 0x20000); - flashWrite(0x08060000, (char*)&conf_sram, sizeof(conf_t)); - flashSectorEnd(flashSectorAt(0x08060000)); - - } else if(!strcmp(command, "?img reject pri")) { // Reject image - - reject_pri = true; - - } else if(!strcmp(command, "?img reject sec")) { // Reject image - - reject_sec = true; - - } else if(!strncmp(command, "?img ", 5)) { // Repeat packets - - TRACE_INFO("RX > Message: Image packet repeat request"); - - char *pt; - pt = strtok(&command[5], " "); - while(pt != NULL) { - uint32_t req = strtol(pt, NULL, 16); - - for(uint8_t i=0; i<16; i++) { - if(!packetRepeats[i].n_done) { - packetRepeats[i].image_id = (req >> 16) & 0xFF; - packetRepeats[i].packet_id = req & 0xFFFF; - packetRepeats[i].n_done = true; - - TRACE_INFO("RX > ... Image %3d Packet %3d", - packetRepeats[i].image_id, - packetRepeats[i].packet_id); - break; - } - } - - pt = strtok(NULL, " "); - } - - } else if(!strncmp(command, "?conf ", 6)) { // Modify configuration - - for(uint8_t i=0; command_list[i].type != TYPE_NULL; i++) - { - if(!strncmp(&command[6], command_list[i].name, - strlen(command_list[i].name))) { - - char *value = &command[strlen(command_list[i].name) + 6]; - TRACE_INFO("RX > Message: Configuration Command"); - TRACE_INFO("RX > %s => %s", &command[6], value); - - if(command_list[i].type == TYPE_INT - && command_list[i].size == 1) { - *((uint8_t*)command_list[i].ptr) = atoi(value); - } else if(command_list[i].type == TYPE_INT - && command_list[i].size == 2) { - *((uint16_t*)command_list[i].ptr) = atoi(value); - } else if(command_list[i].type == TYPE_INT - && command_list[i].size == 4) { - *((uint32_t*)command_list[i].ptr) = atoi(value); - } else if(command_list[i].type == TYPE_TIME) { - *((sysinterval_t*)command_list[i].ptr) = - TIME_MS2I(atoi(value)); - } else if(command_list[i].type == TYPE_STR) { - strncpy((char*)command_list[i].ptr, value, - sizeof(command_list[i].size)-1); - } - } - } - - } else { - TRACE_INFO("RX > No command found in message"); - } - - if(msg_id_rx[0]) { // Message ID has been sent which has to be acknowledged - char buf[16]; - chsnprintf(buf, sizeof(buf), "ack%s", msg_id_rx); - - packet_t pp = aprs_encode_message(conf_sram.aprs.tx.call, - conf_sram.aprs.tx.path, - src, buf, true); - if(pp == NULL) { - TRACE_WARN("RX > No free packet objects"); - return false; - } - transmitOnRadio(pp, - conf_sram.aprs.tx.radio_conf.freq, - 0, - 0, - conf_sram.aprs.tx.radio_conf.pwr, - conf_sram.aprs.tx.radio_conf.mod, - conf_sram.aprs.tx.radio_conf.rssi); - } - return false; // Mark that message should not be digipeated - } - return true; // Mark that message has to be digipeated + /* + * Use the receiving node identity as sender. + * Don't request acknowledgment. + */ + packet_t pp = aprs_encode_message(identity.call, + identity.path, + identity.src, buf, false); + if(pp == NULL) { + TRACE_WARN("RX > No free packet objects"); + return false; + } + transmitOnRadio(pp, + identity.freq, + 0, + 0, + identity.pwr, + identity.mod, + identity.rssi); + } + /* Flag that the APRS content should not be digipeated. */ + return false; } /* @@ -624,18 +1068,17 @@ static void aprs_digipeat(packet_t pp) { * Transmit APRS telemetry configuration */ packet_t aprs_encode_telemetry_configuration(const char *callsign, - const char *path, uint8_t type) -{ - switch(type) - { + const char *path, + uint8_t type) { + switch(type) { case 0: return aprs_encode_message(callsign, path, callsign, - "PARM.Vbat,Vsol,Pbat,Temperature,Airpressure", true); + "PARM.Vbat,Vsol,Pbat,Temperature,Airpressure", false); case 1: return aprs_encode_message(callsign, path, callsign, - "UNIT.V,V,W,degC,Pa", true); + "UNIT.V,V,W,degC,Pa", false); case 2: return aprs_encode_message(callsign, path, callsign, - "EQNS.0,.001,0,0,.001,0,0,.001,-4.096,0,.1,-100,0,12.5,500", true); + "EQNS.0,.001,0,0,.001,0,0,.001,-4.096,0,.1,-100,0,12.5,500", false); case 3: return aprs_encode_message(callsign, path, callsign, - "BITS.11111111,", true); + "BITS.11111111,", false); default: return NULL; } } @@ -656,8 +1099,8 @@ void aprs_decode_packet(packet_t pp) { sysinterval_t first_time = 0xFFFFFFFF; // Timestamp of oldest heard list entry uint8_t first_id = 0; // ID of oldest heard list entry - for(uint8_t i=0; i<=20; i++) { - if(i < 20) { + for(uint8_t i=0; i <= APRS_HEARD_LIST_SIZE; i++) { + if(i < APRS_HEARD_LIST_SIZE) { // Search for callsign in list if(!strcmp(heard_list[i].call, call)) { // Callsign found in list heard_list[i].time = chVTGetSystemTime(); // Update time the callsign was last heard diff --git a/tracker/software/protocols/packet/aprs.h b/tracker/software/protocols/packet/aprs.h index 7d1a07e9..57e62886 100644 --- a/tracker/software/protocols/packet/aprs.h +++ b/tracker/software/protocols/packet/aprs.h @@ -40,8 +40,8 @@ #define ORIGIN_OTHER_TRACKER 0x6 #define ORIGIN_DIGIPEATER_CONVERSION 0x7 -#define APRS_DEST_CALLSIGN "APECAN" // APExxx = Pecan device -#define APRS_DEST_SSID 0 +#define APRS_DEVICE_CALLSIGN "APECAN" // APExxx = Pecan device +//#define APRS_DEST_SSID 0 #define SYM_BALLOON 0x2F4F #define SYM_SMALLAIRCRAFT 0x2F27 @@ -50,17 +50,74 @@ #define SYM_SHIP 0x2F73 #define SYM_DIGIPEATER 0x2F23 +#define APRS_HEARD_LIST_SIZE 20 + +#define APRS_MAX_MSG_ARGUMENTS 10 + +typedef struct APRSIdentity { + char num[8]; + char src[AX25_MAX_ADDR_LEN]; + char call[AX25_MAX_ADDR_LEN]; + char path[16]; + uint16_t symbol; + uint32_t freq; + uint8_t pwr; + mod_t mod; + uint8_t rssi; +} aprs_identity_t; + +/** + * @brief Command handler function type. + */ +typedef msg_t (*aprscmd_t)(aprs_identity_t *id, int argc, char *argv[]); + +/** + * @brief APRS command entry type. + */ +typedef struct { + const char *ac_name; /**< @brief Command name. */ + aprscmd_t ac_function; /**< @brief Command function. */ +} APRSCommand; + +/* Temporary. Will be deprecated when fixed station feature is implemented. */ extern bool test_gps_enabled; -void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len); - -packet_t aprs_encode_position(const char *callsign, const char *path, uint16_t symbol, dataPoint_t *dataPoint); -packet_t aprs_encode_telemetry_configuration(const char *callsign, const char *path, uint8_t type); -packet_t aprs_encode_message(const char *callsign, const char *path, const char *receiver, const char *text, const bool noCounter); -packet_t aprs_encode_data_packet(const char *callsign, const char *path, char packetType, uint8_t *data); -packet_t aprs_encode_query_answer_aprsd(const char *callsign, const char *path, const char *receiver); - -void aprs_decode_packet(packet_t pp); - +#ifdef __cplusplus +extern "C" { +#endif + void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len); + packet_t aprs_encode_position(const char *callsign, const char *path, + uint16_t symbol, dataPoint_t *dataPoint); + packet_t aprs_encode_telemetry_configuration(const char *callsign, + const char *path, uint8_t type); + packet_t aprs_encode_message(const char *callsign, const char *path, + const char *receiver, const char *text, + const bool ack); + packet_t aprs_encode_data_packet(const char *callsign, const char *path, + char packetType, uint8_t *data); + packet_t aprs_compose_aprsd_message(const char *callsign, const char *path, + const char *receiver); + void aprs_decode_packet(packet_t pp); + msg_t aprs_send_position_beacon(aprs_identity_t *id, + int argc, char *argv[]); + msg_t aprs_send_aprsd_message(aprs_identity_t *id, + int argc, char *argv[]); + msg_t aprs_send_aprsh_message(aprs_identity_t *id, + int argc, char *argv[]); + msg_t aprs_handle_gpio_command(aprs_identity_t *id, + int argc, char *argv[]); + msg_t aprs_handle_gps_command(aprs_identity_t *id, + int argc, char *argv[]); + msg_t aprs_execute_config_command(aprs_identity_t *id, + int argc, char *argv[]); + msg_t aprs_execute_config_save(aprs_identity_t *id, + int argc, char *argv[]); + msg_t aprs_execute_img_command(aprs_identity_t *id, + int argc, char *argv[]); + msg_t aprs_execute_system_reset(aprs_identity_t *id, + int argc, char *argv[]); +#ifdef __cplusplus +} +#endif #endif diff --git a/tracker/software/threads/rxtx/position.c b/tracker/software/threads/rxtx/position.c index aed5221e..1bdf138e 100644 --- a/tracker/software/threads/rxtx/position.c +++ b/tracker/software/threads/rxtx/position.c @@ -61,17 +61,23 @@ THD_FUNCTION(posThread, arg) chThdSleep(TIME_S2I(5)); } - // Encode/Transmit APRSD packet /* - * This is a tracker originated message (not a reply to an incoming). - * The message will be sent to the base station if set. + * Encode/Transmit APRSD packet. + * This is a tracker originated message (not a reply to a request). + * The message will be sent to the base station set in path. */ if(conf_sram.aprs.base.enabled) { - packet = aprs_encode_query_answer_aprsd(conf->call, /* from */ - conf_sram.aprs.base.path, /* via */ - conf_sram.aprs.base.call); /* to */ + /* + * Send message from this device. + * Use call sign and path as specified in base config. + * There is no acknowledgment requested. + */ + packet = aprs_compose_aprsd_message( + APRS_DEVICE_CALLSIGN, + conf_sram.aprs.base.path, + conf_sram.aprs.base.call); if(packet == NULL) { - TRACE_WARN("POS > No free packet objects for " + TRACE_WARN("POS > No free packet objects " "or badly formed APRSD message"); } else { if(!transmitOnRadio(packet, @@ -87,6 +93,7 @@ THD_FUNCTION(posThread, arg) chThdSleep(TIME_S2I(5)); } } else { + /* TODO: Implement a fallback destination if no base station set? */ TRACE_INFO("POS > APRSD data not sent - no base station specified"); } diff --git a/tracker/software/types.h b/tracker/software/types.h index f85c1938..1c931f46 100644 --- a/tracker/software/types.h +++ b/tracker/software/types.h @@ -128,6 +128,10 @@ typedef struct { uint16_t symbol; uint8_t rssi; // Squelch for CCA check bool enabled; + bool fixed; + int32_t lat; + int32_t lon; + int32_t alt; } thd_tx_conf_t; From 1f3c1fa07afb58b276bcd128677c7c6ea6476569 Mon Sep 17 00:00:00 2001 From: CInsights Date: Tue, 17 Apr 2018 19:33:36 +1000 Subject: [PATCH 2/6] Small fix to ?aprsh command. --- tracker/software/config.c | 12 ++++++------ tracker/software/pkt/channels/rxafsk.c | 8 +++++++- tracker/software/protocols/packet/aprs.c | 18 +++++++++++++----- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/tracker/software/config.c b/tracker/software/config.c index 3abe594a..e04ad840 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -55,7 +55,7 @@ const conf_t conf_flash_default = { // Primary image transmission thread .img_pri = { .thread_conf = { - .active = true, + .active = false, .cycle = TIME_S2I(60*5), .init_delay = TIME_S2I(60*5), .send_spacing = TIME_S2I(5) @@ -79,10 +79,10 @@ const conf_t conf_flash_default = { // Secondary image transmission thread .img_sec = { .thread_conf = { - .active = false, - .cycle = TIME_S2I(600), - .init_delay = TIME_S2I(20), - .send_spacing = TIME_MS2I(100) + .active = true, + .cycle = TIME_S2I(300), + .init_delay = TIME_S2I(60), + .send_spacing = TIME_S2I(30) }, .radio_conf = { .pwr = 0x7F, @@ -94,7 +94,7 @@ const conf_t conf_flash_default = { .call = "VK2GJ-15", .path = "", - .res = RES_VGA, + .res = RES_QVGA, .quality = 4, .buf_size = 64*1024 }, diff --git a/tracker/software/pkt/channels/rxafsk.c b/tracker/software/pkt/channels/rxafsk.c index 9063b3a0..d781d381 100644 --- a/tracker/software/pkt/channels/rxafsk.c +++ b/tracker/software/pkt/channels/rxafsk.c @@ -906,7 +906,13 @@ THD_FUNCTION(pktAFSKDecoder, arg) { radio_cca_fifo_t *myFIFO = myDriver->active_demod_object; if(myFIFO != NULL) { - /* Wait for queue object to be released by PWM. */ + /* + * Wait for queue object to be released by PWM. + * Normally this is the case. + * If can be a forced release by semaphore reset. + * TODO: This may happen if the watchdog system forces reset. + * TBD. + */ (void)chBSemWait(&myFIFO->sem); #if USE_HEAP_PWM_BUFFER == TRUE diff --git a/tracker/software/protocols/packet/aprs.c b/tracker/software/protocols/packet/aprs.c index 40479f0f..e1e0d6fa 100644 --- a/tracker/software/protocols/packet/aprs.c +++ b/tracker/software/protocols/packet/aprs.c @@ -189,7 +189,10 @@ const APRSCommand aprs_commands[] = { }; /* - * Parse command arguments from aprs message. + * @brief parse arguments from a command string. + * + * @return pointer to next element in string. + * @retval NULL if end. */ static char *aprs_parse_arguments(char *str, char **saveptr) { char *p; @@ -225,9 +228,14 @@ static char *aprs_parse_arguments(char *str, char **saveptr) { } /* - * Execute a command found in an aprs message. - * Return result of command. - * False means either command failed or does not exist. + * @brief Execute a command in an APRS message. + * @notes Known commands are in APRS command table. + * @notes Commands themselves return only MSG_OK or MSG_ERROR. + * + * @return result of command. + * @retval MSG_OK if the command completed. + * @retval MSG_ERROR if there was an error in command execution. + * @retval MSG_TIMEOUT if the command was not found in known commands. */ static msg_t aprs_cmd_exec(const APRSCommand *acp, char *name, @@ -502,7 +510,7 @@ msg_t aprs_send_aprsh_message(aprs_identity_t *id, } if(out == 0) { out = chsnprintf(buf, sizeof(buf), - "%s not heard", heard_list[i].call); + "%s not heard", argv[0]); } } packet_t pp = aprs_encode_message(id->call, id->path, id->src, buf, false); From f3f99aaaf408d8afde608e8c26155b5ab264e470 Mon Sep 17 00:00:00 2001 From: CInsights Date: Tue, 17 Apr 2018 19:41:41 +1000 Subject: [PATCH 3/6] Fix ?aprsd command output when there are no DIRECTS. --- tracker/software/protocols/packet/aprs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tracker/software/protocols/packet/aprs.c b/tracker/software/protocols/packet/aprs.c index e1e0d6fa..a6d768e3 100644 --- a/tracker/software/protocols/packet/aprs.c +++ b/tracker/software/protocols/packet/aprs.c @@ -434,6 +434,7 @@ packet_t aprs_compose_aprsd_message(const char *originator, const char *recipient) { char buf[256] = "Directs="; uint32_t out = strlen(buf); + uint32_t empty = out; for(uint8_t i = 0; i < APRS_HEARD_LIST_SIZE; i++) { if(heard_list[i].time && heard_list[i].time + TIME_S2I(600) >= chVTGetSystemTime() @@ -441,7 +442,11 @@ packet_t aprs_compose_aprsd_message(const char *originator, out += chsnprintf(&buf[out], sizeof(buf)-out, "%s ", heard_list[i].call); } - buf[out-1] = 0; // Remove last space + if(out == empty) { + out += chsnprintf(&buf[out], sizeof(buf)-out, "[none]"); + } else { + buf[out-1] = 0; // Remove last space + } return aprs_encode_message(originator, path, recipient, buf, false); } From 36b6617356cceea675f092cda82bf0d2e8eba9f2 Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Thu, 19 Apr 2018 01:49:16 +0200 Subject: [PATCH 4/6] Fixed autoreload bug at image webpage, fixed timerange bug --- decoder/html/Image.class.php | 12 ++++++------ decoder/html/Tracker.class.php | 9 ++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/decoder/html/Image.class.php b/decoder/html/Image.class.php index d74fc0e9..36a05f10 100644 --- a/decoder/html/Image.class.php +++ b/decoder/html/Image.class.php @@ -3,15 +3,15 @@ class Image { function __construct($sqlResult) { - $this->id = $sqlResult['id']; + $this->id = (int)$sqlResult['id']; $this->call = $sqlResult['call']; - $this->time_first = $sqlResult['time_first']; - $this->time_last = $sqlResult['time_last']; + $this->time_first = (int)$sqlResult['time_first']; + $this->time_last = (int)$sqlResult['time_last']; - $this->imageID = $sqlResult['imageID']; - $this->packetID = $sqlResult['packetID']; - $this->count = $sqlResult['count']; + $this->imageID = (int)$sqlResult['imageID']; + $this->packetID = (int)$sqlResult['packetID']; + $this->count = (int)$sqlResult['count']; } } diff --git a/decoder/html/Tracker.class.php b/decoder/html/Tracker.class.php index 0d4cb189..e302705e 100644 --- a/decoder/html/Tracker.class.php +++ b/decoder/html/Tracker.class.php @@ -38,8 +38,8 @@ class Tracker { if($from > $to) return array(); // Error $from is larger than $to - if($from - $to > 64281600) - $from = $from + 64281600; // Max. 744 days (2 non leap years + 14 weeks) + if($to - $from > 64281600) + $from = $to - 64281600; // Max. 744 days (2 non leap years + 14 weeks) $query = Database::getInstance()->query(" SELECT t.`id`,`call`,MIN(`rxtime`) as `time_first`,MAX(`rxtime`) as `time_last`, @@ -74,8 +74,8 @@ class Tracker { if($from > $to) return array(); // Error $from is larger than $to - if($from - $to > 64281600) - $from = $from + 64281600; // Max. 744 days (2 non leap years + 14 weeks) + if($to - $from > 64281600) + $from = $to - 64281600; // Max. 744 days (2 non leap years + 14 weeks) $query = Database::getInstance()->query(" SELECT *, @@ -99,7 +99,6 @@ class Tracker { ORDER BY `ordertime` ASC "); - $datasets = array(); while($row = $query->fetch_assoc()) { $datasets[] = new Telemetry($row); } From 59d210024009b46f42f1eabdcc3b9a965a17c92a Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Thu, 19 Apr 2018 02:45:38 +0200 Subject: [PATCH 5/6] Fixed reload bug, added new chart --- decoder/html/Tracker.class.php | 9 +++-- decoder/html/telemetry.php | 63 +++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/decoder/html/Tracker.class.php b/decoder/html/Tracker.class.php index e302705e..63611706 100644 --- a/decoder/html/Tracker.class.php +++ b/decoder/html/Tracker.class.php @@ -16,14 +16,12 @@ class Tracker { $query = Database::getInstance()->query(" SELECT * FROM ( - SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'pos' as `type` FROM `position` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' AND `org` = 'pos' + (SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'pos' as `type` FROM `position` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' AND `org` = 'pos' ORDER BY `rxtime` DESC LIMIT 1) UNION ALL - SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'img' as `type` FROM `image` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' + (SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'img' as `type` FROM `image` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' ORDER BY `rxtime` DESC LIMIT 1) UNION ALL - SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'log' as `type` FROM `position` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' AND `org` = 'log' + (SELECT UNIX_TIMESTAMP() - `rxtime` as `lasttime`,'log' as `type` FROM `position` WHERE `call` = '" . Database::getInstance()->escape_string($this->call) . "' AND `org` = 'log' ORDER BY `rxtime` DESC LIMIT 1) ) AS d - GROUP BY `type` - ORDER BY `lasttime` DESC "); while($row = $query->fetch_assoc()) @@ -99,6 +97,7 @@ class Tracker { ORDER BY `ordertime` ASC "); + $datasets = array(); while($row = $query->fetch_assoc()) { $datasets[] = new Telemetry($row); } diff --git a/decoder/html/telemetry.php b/decoder/html/telemetry.php index e98dcd9b..c17e59f6 100644 --- a/decoder/html/telemetry.php +++ b/decoder/html/telemetry.php @@ -216,14 +216,29 @@ function loadRecentData() { dataTemp.addRow([null,null,null,null,null,null]); dataGPS.addRow([null,null,null,null]); dataLight.addRow([null,null]); + dataAlt.addRow([null,null,null,null,null]); } dataBattery.addRow([time, data['adc_vbat'], data['pac_vbat'], data['pac_pbat']/10]); dataSolar.addRow([time, data['adc_vsol'], data['pac_vsol'], data['pac_psol']/10]); - dataTemp.addRow([time, data['sen_i1_temp']/100, data['sen_e1_temp']/100, data['sen_e2_temp']/100, data['stm32_temp']/100, data['si4464_temp']/100]); + dataTemp.addRow([ + time, + data['sen_i1_temp'] && data['sen_i1_temp'] > -10000 && data['sen_i1_temp'] < 10000 ? data['sen_i1_temp']/100 : null, + data['sen_e1_temp'] && data['sen_e1_temp'] > -10000 && data['sen_e1_temp'] < 10000 ? data['sen_e1_temp']/100 : null, + data['sen_e2_temp'] && data['sen_e2_temp'] > -10000 && data['sen_e2_temp'] < 10000 ? data['sen_e2_temp']/100 : null, + data['stm32_temp'] && data['stm32_temp'] > -10000 && data['stm32_temp'] < 10000 ? data['stm32_temp']/100 : null, + data['si4464_temp'] && data['si4464_temp'] > -10000 && data['si4464_temp'] < 10000 ? data['si4464_temp']/100 : null + ]); dataGPS.addRow([time, data['gps_sats'], data['gps_ttff'], data['gps_pdop']/20]); dataLight.addRow([time, data['light_intensity']]); + dataAlt.addRow([ + time, + data['gps_alt'], + data['sen_i1_press'] && data['sen_i1_press'] < 1100000 ? data['sen_i1_press']/10 : null, + data['sen_e1_press'] && data['sen_e1_press'] < 1100000 ? data['sen_e1_press']/10 : null, + data['sen_e2_press'] && data['sen_e2_press'] < 1100000 ? data['sen_e2_press']/10 : null + ]); last = time; }); @@ -237,6 +252,7 @@ function loadRecentData() { dataTemp.removeRow(c); dataGPS.removeRow(c); dataLight.removeRow(c); + dataAlt.removeRow(c); } } @@ -246,6 +262,7 @@ function loadRecentData() { tempChart.draw(dataTemp, tempOptions); gpsChart.draw(dataGPS, gpsOptions); lightChart.draw(dataLight, lightOptions); + altChart.draw(dataAlt, altOptions); lastChartUpdate = json['time']; } @@ -335,6 +352,30 @@ var gpsOptions = { }; // Chart 5 +var altChart; +var dataAlt; +var altOptions = { + explorer: scroll, + width: 1285, + height: 300, + series: { + 0: {targetAxisIndex: 0}, + 1: {targetAxisIndex: 1}, + 2: {targetAxisIndex: 1}, + 3: {targetAxisIndex: 1} + }, + vAxes: { + 0: {title: 'Altitude'}, + 1: {title: 'Airpressure'}, + }, + legend: { + position: 'top' + }, + hAxis: xAxis, + chartArea: area +}; + +// Chart 6 var lightChart; var dataLight; var lightOptions = { @@ -390,6 +431,15 @@ function drawChart() { gpsChart = new google.visualization.LineChart(document.getElementById('gpsDiv')); // Chart 5 + dataAlt = new google.visualization.DataTable(); + dataAlt.addColumn('date', 'Time'); + dataAlt.addColumn('number', "GPS_ALT"); + dataAlt.addColumn('number', "PRESS_BME_I1"); + dataAlt.addColumn('number', "PRESS_BME_E1"); + dataAlt.addColumn('number', "PRESS_BME_E2"); + altChart = new google.visualization.LineChart(document.getElementById('altDiv')); + + // Chart 6 dataLight = new google.visualization.DataTable(); dataLight.addColumn('date', 'Time'); dataLight.addColumn('number', "LIGHT"); @@ -448,6 +498,14 @@ include "sidebar.inc.php"; + + + DIR: + + + + + IMG: @@ -600,6 +658,9 @@ include "sidebar.inc.php";
+ +
+
From 40042dc7c00398bdda8d5fa7b4b9358a71ef401d Mon Sep 17 00:00:00 2001 From: CInsights Date: Fri, 20 Apr 2018 00:12:25 +1000 Subject: [PATCH 6/6] Multiple updates - Trace out and shell. To get shell type any key. Trace is suspended. - Fixed bug where AX25 receive FIFO was left locked. - Improved trace message for AX25 packets with bad CRC. - Additions to config.c in preparation for adding fixed location beacon. --- tracker/hardware/10b/.gitignore | 1 + .../software/ChibiOS/os/various/shell/shell.c | 2 +- tracker/software/config.c | 45 +- tracker/software/config.h | 5 +- tracker/software/drivers/usb/usb.c | 149 +++- tracker/software/drivers/usb/usb.h | 11 +- tracker/software/drivers/usb/usbcfg.c | 688 +++++++++--------- tracker/software/main.c | 23 +- tracker/software/pkt/channels/rxafsk.c | 5 +- tracker/software/pkt/channels/rxpwm.c | 2 +- tracker/software/pkt/managers/pktservice.c | 1 - tracker/software/pkt/managers/pktservice.h | 17 + tracker/software/pkt/pktconf.h | 10 +- .../software/pkt/protocols/aprs2/ax25_pad.c | 88 ++- .../software/pkt/protocols/aprs2/ax25_pad.h | 7 +- tracker/software/portab.c | 5 + tracker/software/portab.h | 6 +- tracker/software/protocols/packet/aprs.c | 82 +-- tracker/software/protocols/packet/aprs.h | 3 +- tracker/software/threads/collector.c | 43 +- tracker/software/threads/rxtx/image.c | 19 +- tracker/software/threads/rxtx/radio.c | 5 +- 22 files changed, 674 insertions(+), 543 deletions(-) create mode 100644 tracker/hardware/10b/.gitignore diff --git a/tracker/hardware/10b/.gitignore b/tracker/hardware/10b/.gitignore new file mode 100644 index 00000000..46f586da --- /dev/null +++ b/tracker/hardware/10b/.gitignore @@ -0,0 +1 @@ +/pecanpico10-rescue.lib diff --git a/tracker/software/ChibiOS/os/various/shell/shell.c b/tracker/software/ChibiOS/os/various/shell/shell.c index 418b1300..9b382115 100644 --- a/tracker/software/ChibiOS/os/various/shell/shell.c +++ b/tracker/software/ChibiOS/os/various/shell/shell.c @@ -354,7 +354,7 @@ THD_FUNCTION(shellThread, p) { chprintf(chp, SHELL_NEWLINE_STR); chprintf(chp, "ChibiOS/RT Shell" SHELL_NEWLINE_STR); - while (true) { + while (!chThdShouldTerminateX()) { chprintf(chp, SHELL_PROMPT_STR); if (shellGetLine(scfg, line, sizeof(line), shp)) { #if (SHELL_CMD_EXIT_ENABLED == TRUE) && !defined(_CHIBIOS_NIL_) diff --git a/tracker/software/config.c b/tracker/software/config.c index e04ad840..8e34cecf 100644 --- a/tracker/software/config.c +++ b/tracker/software/config.c @@ -8,10 +8,10 @@ conf_t conf_sram; const conf_t conf_flash_default = { - // Primary position transmission thread + // Primary position node .pos_pri = { .thread_conf = { - .active = true, + .active = false, .cycle = TIME_S2I(60*30), .init_delay = TIME_S2I(30) }, @@ -21,16 +21,16 @@ const conf_t conf_flash_default = { .mod = MOD_AFSK, .rssi = 0x4F, }, - + // Node identity .call = "VK2GJ-12", .path = "WIDE2-1", - .symbol = SYM_DIGIPEATER, + .symbol = SYM_ANTENNA, .aprs_msg = true, .tel_enc_cycle = TIME_S2I(10800) }, - // Secondary position transmission thread + // Secondary position node .pos_sec = { .thread_conf = { .active = false, @@ -43,7 +43,7 @@ const conf_t conf_flash_default = { .mod = MOD_AFSK, .rssi = 0x4F }, - + // Node identity .call = "DL7AD-14", .path = "WIDE1-1", .symbol = SYM_BALLOON, @@ -52,11 +52,11 @@ const conf_t conf_flash_default = { .tel_enc_cycle = TIME_S2I(10800) }, - // Primary image transmission thread + // Primary image node .img_pri = { .thread_conf = { .active = false, - .cycle = TIME_S2I(60*5), + .cycle = TIME_S2I(60*10), .init_delay = TIME_S2I(60*5), .send_spacing = TIME_S2I(5) }, @@ -67,21 +67,21 @@ const conf_t conf_flash_default = { .rssi = 0x4F, .redundantTx = false }, - + // Node identity .call = "VK2GJ-15", .path = "", .res = RES_VGA, .quality = 4, - .buf_size = 64*1024 + .buf_size = 40*1024 }, - // Secondary image transmission thread + // Secondary image node .img_sec = { .thread_conf = { - .active = true, - .cycle = TIME_S2I(300), - .init_delay = TIME_S2I(60), + .active = false, + .cycle = TIME_S2I(60*5), + .init_delay = TIME_S2I(60*1), .send_spacing = TIME_S2I(30) }, .radio_conf = { @@ -90,16 +90,16 @@ const conf_t conf_flash_default = { .mod = MOD_AFSK, .rssi = 0x4F }, - - .call = "VK2GJ-15", + // Node identity + .call = "VK2GJ-14", .path = "", .res = RES_QVGA, .quality = 4, - .buf_size = 64*1024 + .buf_size = 15*1024 }, - // Log transmission thread + // Log node .log = { .thread_conf = { .active = false, @@ -112,13 +112,13 @@ const conf_t conf_flash_default = { .mod = MOD_AFSK, .rssi = 0x4F }, - + // Node identity .call = "VK2GJ-13", .path = "WIDE1-1", .density = 10 }, - // APRS system control + // APRS node .aprs = { .thread_conf = { .active = true, @@ -130,6 +130,7 @@ const conf_t conf_flash_default = { .mod = MOD_AFSK, .rssi = 0x3F }, + // Node rx identity .call = "VK2GJ-4" }, .tx = { // The transmit identity for digipeat transmit and messages responses @@ -139,6 +140,7 @@ const conf_t conf_flash_default = { .mod = MOD_AFSK, .rssi = 0x4F }, + // Node tx identity .call = "VK2GJ-5", .path = "WIDE2-1", .symbol = SYM_DIGIPEATER, @@ -147,7 +149,8 @@ const conf_t conf_flash_default = { .lon = 1511143478, .alt = 144 }, - .base = { // The base station parameters - how and where tracker originated messages are sent + .base = { + // The base station identity - how and where tracker originated messages are sent .enabled = true, .call = "VK2GJ-7", .path = "WIDE2-1", diff --git a/tracker/software/config.h b/tracker/software/config.h index 22660d11..e266727b 100644 --- a/tracker/software/config.h +++ b/tracker/software/config.h @@ -8,7 +8,7 @@ * 3V, because USB would not work at 1.8V. Note that the transmission power is increased * too when operating at 3V. This option will also run the STM32 at 48MHz (AHB) permanently * because USB needs that speed, otherwise it is running at 6MHz which saves a lot of power. */ -#define ENABLE_EXTERNAL_I2C TRUE /* The external port can be used for bit bang I2C. */ +#define ENABLE_EXTERNAL_I2C FALSE /* The external port can be used for bit bang I2C. */ #include "types.h" @@ -18,5 +18,4 @@ extern conf_t conf_sram; extern const conf_t conf_flash_default; -#endif - +#endif /* __CONFIG_H__ */ diff --git a/tracker/software/drivers/usb/usb.c b/tracker/software/drivers/usb/usb.c index 4cf206f5..8a269b45 100644 --- a/tracker/software/drivers/usb/usb.c +++ b/tracker/software/drivers/usb/usb.c @@ -6,60 +6,137 @@ #include "pktconf.h" static thread_t *shelltp; -static bool usb_initialized; +sdu_term_t sdu_chn_state; -event_listener_t shell_el; +event_listener_t sdu1_el; static const ShellConfig shell_cfg = { (BaseSequentialStream*)&SDU1, commands }; +/* + * + */ void startUSB(void) { - if(usb_initialized) - return; // Avoid duplicate initialization + usbObjectInit(&USBD1); - /* Initialize USB. */ - sduObjectInit(&SDU1); + usbStart(&USBD1, &usbcfg); /* Currently does nothing. */ - usbDisconnectBus(serusbcfg.usbp); + usbDisconnectBus(&USBD1); + chThdSleep(TIME_MS2I(100)); - usbStart(serusbcfg.usbp, &usbcfg); - /* Currently does nothing. */ - usbConnectBus(serusbcfg.usbp); + usbConnectBus(&USBD1); - sduStart(&SDU1, &serusbcfg); - - // Initialize shell - shelltp = NULL; - shellInit(); - - usb_initialized = true; + sdu_chn_state = TERM_SDU_INIT; } -void manageShell(void) { - if(shelltp == NULL && isUSBactive()) { - - shelltp = chThdCreateFromHeap(NULL, - THD_WORKING_AREA_SIZE(4*1024), - "shell", NORMALPRIO + 1, - shellThread, - (void*)&shell_cfg); - - - chEvtRegister(&shell_terminated, &shell_el, USB_SHELL_EVT); - } - chEvtWaitAnyTimeout(EVENT_MASK(USB_SHELL_EVT), TIME_S2I(1)); - if(chThdTerminatedX(shelltp)) { - chThdWait(shelltp); - shelltp = NULL; - chEvtUnregister(&shell_terminated, &shell_el); - } +/* + * + */ +void startSDU(void) { + if(sdu_chn_state != TERM_SDU_INIT) + return; + sduObjectInit(&SDU1); + chEvtRegister(chnGetEventSource(&SDU1), &sdu1_el, USB_SDU1_EVT); + sduStart(&SDU1, &serusbcfg); + sdu_chn_state = TERM_SDU_IDLE; } +/** + * @brief Manage trace output and shell on Serial Over USB. + * @notes TRACE output is sent to USB serial. + * @notes TRACE output is suspended when any key is pressed on terminal. + * @notes A new shell is invoked and remains active until logout. + * @notes TRACE output is then resotored. + * + * @api + */ +void manageTraceAndShell(void) { + + if(chEvtGetAndClearEvents(EVENT_MASK(USB_SDU1_EVT)) == 0) + return; + + BaseSequentialStream *chp = (BaseSequentialStream *)&SDU1; + + eventflags_t evtf = chEvtGetAndClearFlags(&sdu1_el); + + switch(sdu_chn_state) { + case TERM_SDU_INIT: + return; + + case TERM_SDU_IDLE: { + if(evtf == 0) + return; + if(evtf & CHN_CONNECTED) { + sdu_chn_state = TERM_SDU_OUT; + chprintf(chp, "\r\n*** Trace output enabled ***\r\n"); + break; + } + break; + } /* End case TERM_SDU_IDLE */ + + case TERM_SDU_OUT: { + if(evtf & CHN_DISCONNECTED) { + sdu_chn_state = TERM_SDU_IDLE; + return; + } + if(evtf & CHN_INPUT_AVAILABLE) { + /* Flush the input queue. */ + while(chnGetTimeout((SerialUSBDriver *)chp, TIME_MS2I(100)) != STM_TIMEOUT); + chprintf(chp, "\r\n*** Trace suspended - type ^D or use the " + "'exit' command to resume trace ***\r\n"); + shellInit(); + shelltp = chThdCreateFromHeap(NULL, + THD_WORKING_AREA_SIZE(4*1024), + "shell", NORMALPRIO + 1, + shellThread, + (void*)&shell_cfg); + if(shelltp == NULL) { + chprintf(chp, "\r\n*** Failed to open shell ***\r\n"); + break; + } + sdu_chn_state = TERM_SDU_SHELL; + } + break; + } /* End case TERM_SDU_OUT */ + + case TERM_SDU_SHELL: { + /* USB disconnect. */ + if(evtf & CHN_DISCONNECTED) { + chThdTerminate(shelltp); + sdu_chn_state = TERM_SDU_EXIT; + break; + } + /* Was shell terminated from CLI? */ + if(chThdTerminatedX(shelltp)) { + chThdWait(shelltp); + shelltp = NULL; + sdu_chn_state = TERM_SDU_OUT; + chprintf(chp, "\r\n*** Trace resumed by user ***\r\n"); + } + break; + } /* End case TERM_SDU_SHELL */ + + case TERM_SDU_EXIT: { + chThdWait(shelltp); + shelltp = NULL; + sdu_chn_state = TERM_SDU_IDLE; + break; + } /* End case TERM_SDU_EXIT */ + + default: + break; + } /* End switch. */ +} + +/* + * + */ bool isSDUAvailable(void) { - return usb_initialized; + /* Return channel connection status of SDU. */ + return (bool)(sdu_chn_state == TERM_SDU_OUT); } diff --git a/tracker/software/drivers/usb/usb.h b/tracker/software/drivers/usb/usb.h index 52f1c183..194d0194 100644 --- a/tracker/software/drivers/usb/usb.h +++ b/tracker/software/drivers/usb/usb.h @@ -4,10 +4,19 @@ #include "ch.h" #include "hal.h" +typedef enum sduTermStates { + TERM_SDU_INIT = 0, + TERM_SDU_IDLE, + TERM_SDU_OUT, + TERM_SDU_SHELL, + TERM_SDU_EXIT +} sdu_term_t; + #define isUSBactive() (SDU1.config->usbp->state == USB_ACTIVE) void startUSB(void); -void manageShell(void); +void startSDU(void); +void manageTraceAndShell(void); bool isSDUAvailable(void); #endif diff --git a/tracker/software/drivers/usb/usbcfg.c b/tracker/software/drivers/usb/usbcfg.c index f2f2d237..0ebf18de 100644 --- a/tracker/software/drivers/usb/usbcfg.c +++ b/tracker/software/drivers/usb/usbcfg.c @@ -1,344 +1,344 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include "hal.h" -#include "config.h" - -/* Virtual serial port over USB.*/ -SerialUSBDriver SDU1; - -/* - * Endpoints to be used for USBD1. - */ -#define USBD1_DATA_REQUEST_EP 1 -#define USBD1_DATA_AVAILABLE_EP 1 -#define USBD1_INTERRUPT_REQUEST_EP 2 - -/* - * USB Device Descriptor. - */ -static const uint8_t vcom_device_descriptor_data[18] = { - USB_DESC_DEVICE (0x0110, /* bcdUSB (1.1). */ - 0x02, /* bDeviceClass (CDC). */ - 0x00, /* bDeviceSubClass. */ - 0x00, /* bDeviceProtocol. */ - 0x40, /* bMaxPacketSize. */ - 0x0483, /* idVendor (ST). */ - 0x5740, /* idProduct. */ - 0x0200, /* bcdDevice. */ - 1, /* iManufacturer. */ - 2, /* iProduct. */ - 3, /* iSerialNumber. */ - 1) /* bNumConfigurations. */ -}; - -/* - * Device Descriptor wrapper. - */ -static const USBDescriptor vcom_device_descriptor = { - sizeof vcom_device_descriptor_data, - vcom_device_descriptor_data -}; - -/* Configuration Descriptor tree for a CDC.*/ -static const uint8_t vcom_configuration_descriptor_data[67] = { - /* Configuration Descriptor.*/ - USB_DESC_CONFIGURATION(67, /* wTotalLength. */ - 0x02, /* bNumInterfaces. */ - 0x01, /* bConfigurationValue. */ - 0, /* iConfiguration. */ - 0xC0, /* bmAttributes (self powered). */ - 50), /* bMaxPower (100mA). */ - /* Interface Descriptor.*/ - USB_DESC_INTERFACE (0x00, /* bInterfaceNumber. */ - 0x00, /* bAlternateSetting. */ - 0x01, /* bNumEndpoints. */ - 0x02, /* bInterfaceClass (Communications - Interface Class, CDC section - 4.2). */ - 0x02, /* bInterfaceSubClass (Abstract - Control Model, CDC section 4.3). */ - 0x01, /* bInterfaceProtocol (AT commands, - CDC section 4.4). */ - 0), /* iInterface. */ - /* Header Functional Descriptor (CDC section 5.2.3).*/ - USB_DESC_BYTE (5), /* bLength. */ - USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */ - USB_DESC_BYTE (0x00), /* bDescriptorSubtype (Header - Functional Descriptor. */ - USB_DESC_BCD (0x0110), /* bcdCDC. */ - /* Call Management Functional Descriptor. */ - USB_DESC_BYTE (5), /* bFunctionLength. */ - USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */ - USB_DESC_BYTE (0x01), /* bDescriptorSubtype (Call Management - Functional Descriptor). */ - USB_DESC_BYTE (0x00), /* bmCapabilities (D0+D1). */ - USB_DESC_BYTE (0x01), /* bDataInterface. */ - /* ACM Functional Descriptor.*/ - USB_DESC_BYTE (4), /* bFunctionLength. */ - USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */ - USB_DESC_BYTE (0x02), /* bDescriptorSubtype (Abstract - Control Management Descriptor). */ - USB_DESC_BYTE (0x02), /* bmCapabilities. */ - /* Union Functional Descriptor.*/ - USB_DESC_BYTE (5), /* bFunctionLength. */ - USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */ - USB_DESC_BYTE (0x06), /* bDescriptorSubtype (Union - Functional Descriptor). */ - USB_DESC_BYTE (0x00), /* bMasterInterface (Communication - Class Interface). */ - USB_DESC_BYTE (0x01), /* bSlaveInterface0 (Data Class - Interface). */ - /* Endpoint 2 Descriptor.*/ - USB_DESC_ENDPOINT (USBD1_INTERRUPT_REQUEST_EP|0x80, - 0x03, /* bmAttributes (Interrupt). */ - 0x0008, /* wMaxPacketSize. */ - 0xFF), /* bInterval. */ - /* Interface Descriptor.*/ - USB_DESC_INTERFACE (0x01, /* bInterfaceNumber. */ - 0x00, /* bAlternateSetting. */ - 0x02, /* bNumEndpoints. */ - 0x0A, /* bInterfaceClass (Data Class - Interface, CDC section 4.5). */ - 0x00, /* bInterfaceSubClass (CDC section - 4.6). */ - 0x00, /* bInterfaceProtocol (CDC section - 4.7). */ - 0x00), /* iInterface. */ - /* Endpoint 3 Descriptor.*/ - USB_DESC_ENDPOINT (USBD1_DATA_AVAILABLE_EP, /* bEndpointAddress.*/ - 0x02, /* bmAttributes (Bulk). */ - 0x0040, /* wMaxPacketSize. */ - 0x00), /* bInterval. */ - /* Endpoint 1 Descriptor.*/ - USB_DESC_ENDPOINT (USBD1_DATA_REQUEST_EP|0x80, /* bEndpointAddress.*/ - 0x02, /* bmAttributes (Bulk). */ - 0x0040, /* wMaxPacketSize. */ - 0x00) /* bInterval. */ -}; - -/* - * Configuration Descriptor wrapper. - */ -static const USBDescriptor vcom_configuration_descriptor = { - sizeof vcom_configuration_descriptor_data, - vcom_configuration_descriptor_data -}; - -/* - * U.S. English language identifier. - */ -static const uint8_t vcom_string0[] = { - USB_DESC_BYTE(4), /* bLength. */ - USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ - USB_DESC_WORD(0x0409) /* wLANGID (U.S. English). */ -}; - -/* - * Vendor string. - */ -static const uint8_t vcom_string1[] = { - USB_DESC_BYTE(38), /* bLength. */ - USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ - 'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0, - 'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0, - 'c', 0, 's', 0 -}; - -/* - * Device Description string. - */ -static const uint8_t vcom_string2[] = { - USB_DESC_BYTE(56), /* bLength. */ - USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ - 'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0, - 'R', 0, 'T', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0, - 'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0, ' ', 0, 'P', 0, - 'o', 0, 'r', 0, 't', 0 -}; - -/* - * Serial Number string. - */ -static const uint8_t vcom_string3[] = { - USB_DESC_BYTE(8), /* bLength. */ - USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ - '0' + CH_KERNEL_MAJOR, 0, - '0' + CH_KERNEL_MINOR, 0, - '0' + CH_KERNEL_PATCH, 0 -}; - -/* - * Strings wrappers array. - */ -static const USBDescriptor vcom_strings[] = { - {sizeof vcom_string0, vcom_string0}, - {sizeof vcom_string1, vcom_string1}, - {sizeof vcom_string2, vcom_string2}, - {sizeof vcom_string3, vcom_string3} -}; - -/* - * Handles the GET_DESCRIPTOR callback. All required descriptors must be - * handled here. - */ -static const USBDescriptor *get_descriptor(USBDriver *usbp, - uint8_t dtype, - uint8_t dindex, - uint16_t lang) { - - (void)usbp; - (void)lang; - switch (dtype) { - case USB_DESCRIPTOR_DEVICE: - return &vcom_device_descriptor; - case USB_DESCRIPTOR_CONFIGURATION: - return &vcom_configuration_descriptor; - case USB_DESCRIPTOR_STRING: - if (dindex < 4) - return &vcom_strings[dindex]; - } - return NULL; -} - -/** - * @brief IN EP1 state. - */ -static USBInEndpointState ep1instate; - -/** - * @brief OUT EP1 state. - */ -static USBOutEndpointState ep1outstate; - -/** - * @brief EP1 initialization structure (both IN and OUT). - */ -static const USBEndpointConfig ep1config = { - USB_EP_MODE_TYPE_BULK, - NULL, - sduDataTransmitted, - sduDataReceived, - 0x0040, - 0x0040, - &ep1instate, - &ep1outstate, - 2, - NULL -}; - -/** - * @brief IN EP2 state. - */ -static USBInEndpointState ep2instate; - -/** - * @brief EP2 initialization structure (IN only). - */ -static const USBEndpointConfig ep2config = { - USB_EP_MODE_TYPE_INTR, - NULL, - sduInterruptTransmitted, - NULL, - 0x0010, - 0x0000, - &ep2instate, - NULL, - 1, - NULL -}; - -/* - * Handles the USB driver global events. - */ -static void usb_event(USBDriver *usbp, usbevent_t event) { - extern SerialUSBDriver SDU1; - - switch (event) { - case USB_EVENT_ADDRESS: - return; - case USB_EVENT_CONFIGURED: - chSysLockFromISR(); - - /* Enables the endpoints specified into the configuration. - Note, this callback is invoked from an ISR so I-Class functions - must be used.*/ - usbInitEndpointI(usbp, USBD1_DATA_REQUEST_EP, &ep1config); - usbInitEndpointI(usbp, USBD1_INTERRUPT_REQUEST_EP, &ep2config); - - /* Resetting the state of the CDC subsystem.*/ - sduConfigureHookI(&SDU1); - - chSysUnlockFromISR(); - return; - case USB_EVENT_RESET: - /* Falls into.*/ - case USB_EVENT_UNCONFIGURED: - /* Falls into.*/ - case USB_EVENT_SUSPEND: - chSysLockFromISR(); - - /* Disconnection event on suspend.*/ - sduSuspendHookI(&SDU1); - - chSysUnlockFromISR(); - return; - case USB_EVENT_WAKEUP: - chSysLockFromISR(); - - /* Disconnection event on suspend.*/ - sduWakeupHookI(&SDU1); - - chSysUnlockFromISR(); - return; - case USB_EVENT_STALLED: - return; - } - return; -} - -/* - * Handles the USB driver global events. - */ -static void sof_handler(USBDriver *usbp) { - - (void)usbp; - - osalSysLockFromISR(); - sduSOFHookI(&SDU1); - osalSysUnlockFromISR(); -} - -/* - * USB driver configuration. - */ -const USBConfig usbcfg = { - usb_event, - get_descriptor, - sduRequestsHook, - sof_handler -}; - -/* - * Serial over USB driver configuration. - */ -const SerialUSBConfig serusbcfg = { - &USBD1, - USBD1_DATA_REQUEST_EP, - USBD1_DATA_AVAILABLE_EP, - USBD1_INTERRUPT_REQUEST_EP -}; - +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "hal.h" +#include "config.h" + +/* Virtual serial port over USB.*/ +SerialUSBDriver SDU1; + +/* + * Endpoints to be used for USBD1. + */ +#define USBD1_DATA_REQUEST_EP 1 +#define USBD1_DATA_AVAILABLE_EP 1 +#define USBD1_INTERRUPT_REQUEST_EP 2 + +/* + * USB Device Descriptor. + */ +static const uint8_t vcom_device_descriptor_data[18] = { + USB_DESC_DEVICE (0x0110, /* bcdUSB (1.1). */ + 0x02, /* bDeviceClass (CDC). */ + 0x00, /* bDeviceSubClass. */ + 0x00, /* bDeviceProtocol. */ + 0x40, /* bMaxPacketSize. */ + 0x0483, /* idVendor (ST). */ + 0x5740, /* idProduct. */ + 0x0200, /* bcdDevice. */ + 1, /* iManufacturer. */ + 2, /* iProduct. */ + 3, /* iSerialNumber. */ + 1) /* bNumConfigurations. */ +}; + +/* + * Device Descriptor wrapper. + */ +static const USBDescriptor vcom_device_descriptor = { + sizeof vcom_device_descriptor_data, + vcom_device_descriptor_data +}; + +/* Configuration Descriptor tree for a CDC.*/ +static const uint8_t vcom_configuration_descriptor_data[67] = { + /* Configuration Descriptor.*/ + USB_DESC_CONFIGURATION(67, /* wTotalLength. */ + 0x02, /* bNumInterfaces. */ + 0x01, /* bConfigurationValue. */ + 0, /* iConfiguration. */ + 0xC0, /* bmAttributes (self powered). */ + 50), /* bMaxPower (100mA). */ + /* Interface Descriptor.*/ + USB_DESC_INTERFACE (0x00, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x01, /* bNumEndpoints. */ + 0x02, /* bInterfaceClass (Communications + Interface Class, CDC section + 4.2). */ + 0x02, /* bInterfaceSubClass (Abstract + Control Model, CDC section 4.3). */ + 0x01, /* bInterfaceProtocol (AT commands, + CDC section 4.4). */ + 0), /* iInterface. */ + /* Header Functional Descriptor (CDC section 5.2.3).*/ + USB_DESC_BYTE (5), /* bLength. */ + USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */ + USB_DESC_BYTE (0x00), /* bDescriptorSubtype (Header + Functional Descriptor. */ + USB_DESC_BCD (0x0110), /* bcdCDC. */ + /* Call Management Functional Descriptor. */ + USB_DESC_BYTE (5), /* bFunctionLength. */ + USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */ + USB_DESC_BYTE (0x01), /* bDescriptorSubtype (Call Management + Functional Descriptor). */ + USB_DESC_BYTE (0x00), /* bmCapabilities (D0+D1). */ + USB_DESC_BYTE (0x01), /* bDataInterface. */ + /* ACM Functional Descriptor.*/ + USB_DESC_BYTE (4), /* bFunctionLength. */ + USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */ + USB_DESC_BYTE (0x02), /* bDescriptorSubtype (Abstract + Control Management Descriptor). */ + USB_DESC_BYTE (0x02), /* bmCapabilities. */ + /* Union Functional Descriptor.*/ + USB_DESC_BYTE (5), /* bFunctionLength. */ + USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */ + USB_DESC_BYTE (0x06), /* bDescriptorSubtype (Union + Functional Descriptor). */ + USB_DESC_BYTE (0x00), /* bMasterInterface (Communication + Class Interface). */ + USB_DESC_BYTE (0x01), /* bSlaveInterface0 (Data Class + Interface). */ + /* Endpoint 2 Descriptor.*/ + USB_DESC_ENDPOINT (USBD1_INTERRUPT_REQUEST_EP|0x80, + 0x03, /* bmAttributes (Interrupt). */ + 0x0008, /* wMaxPacketSize. */ + 0xFF), /* bInterval. */ + /* Interface Descriptor.*/ + USB_DESC_INTERFACE (0x01, /* bInterfaceNumber. */ + 0x00, /* bAlternateSetting. */ + 0x02, /* bNumEndpoints. */ + 0x0A, /* bInterfaceClass (Data Class + Interface, CDC section 4.5). */ + 0x00, /* bInterfaceSubClass (CDC section + 4.6). */ + 0x00, /* bInterfaceProtocol (CDC section + 4.7). */ + 0x00), /* iInterface. */ + /* Endpoint 3 Descriptor.*/ + USB_DESC_ENDPOINT (USBD1_DATA_AVAILABLE_EP, /* bEndpointAddress.*/ + 0x02, /* bmAttributes (Bulk). */ + 0x0040, /* wMaxPacketSize. */ + 0x00), /* bInterval. */ + /* Endpoint 1 Descriptor.*/ + USB_DESC_ENDPOINT (USBD1_DATA_REQUEST_EP|0x80, /* bEndpointAddress.*/ + 0x02, /* bmAttributes (Bulk). */ + 0x0040, /* wMaxPacketSize. */ + 0x00) /* bInterval. */ +}; + +/* + * Configuration Descriptor wrapper. + */ +static const USBDescriptor vcom_configuration_descriptor = { + sizeof vcom_configuration_descriptor_data, + vcom_configuration_descriptor_data +}; + +/* + * U.S. English language identifier. + */ +static const uint8_t vcom_string0[] = { + USB_DESC_BYTE(4), /* bLength. */ + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ + USB_DESC_WORD(0x0409) /* wLANGID (U.S. English). */ +}; + +/* + * Vendor string. + */ +static const uint8_t vcom_string1[] = { + USB_DESC_BYTE(38), /* bLength. */ + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ + 'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0, + 'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0, + 'c', 0, 's', 0 +}; + +/* + * Device Description string. + */ +static const uint8_t vcom_string2[] = { + USB_DESC_BYTE(56), /* bLength. */ + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ + 'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0, + 'R', 0, 'T', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0, + 'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0, ' ', 0, 'P', 0, + 'o', 0, 'r', 0, 't', 0 +}; + +/* + * Serial Number string. + */ +static const uint8_t vcom_string3[] = { + USB_DESC_BYTE(8), /* bLength. */ + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ + '0' + CH_KERNEL_MAJOR, 0, + '0' + CH_KERNEL_MINOR, 0, + '0' + CH_KERNEL_PATCH, 0 +}; + +/* + * Strings wrappers array. + */ +static const USBDescriptor vcom_strings[] = { + {sizeof vcom_string0, vcom_string0}, + {sizeof vcom_string1, vcom_string1}, + {sizeof vcom_string2, vcom_string2}, + {sizeof vcom_string3, vcom_string3} +}; + +/* + * Handles the GET_DESCRIPTOR callback. All required descriptors must be + * handled here. + */ +static const USBDescriptor *get_descriptor(USBDriver *usbp, + uint8_t dtype, + uint8_t dindex, + uint16_t lang) { + + (void)usbp; + (void)lang; + switch (dtype) { + case USB_DESCRIPTOR_DEVICE: + return &vcom_device_descriptor; + case USB_DESCRIPTOR_CONFIGURATION: + return &vcom_configuration_descriptor; + case USB_DESCRIPTOR_STRING: + if (dindex < 4) + return &vcom_strings[dindex]; + } + return NULL; +} + +/** + * @brief IN EP1 state. + */ +static USBInEndpointState ep1instate; + +/** + * @brief OUT EP1 state. + */ +static USBOutEndpointState ep1outstate; + +/** + * @brief EP1 initialization structure (both IN and OUT). + */ +static const USBEndpointConfig ep1config = { + USB_EP_MODE_TYPE_BULK, + NULL, + sduDataTransmitted, + sduDataReceived, + 0x0040, + 0x0040, + &ep1instate, + &ep1outstate, + 2, + NULL +}; + +/** + * @brief IN EP2 state. + */ +static USBInEndpointState ep2instate; + +/** + * @brief EP2 initialization structure (IN only). + */ +static const USBEndpointConfig ep2config = { + USB_EP_MODE_TYPE_INTR, + NULL, + sduInterruptTransmitted, + NULL, + 0x0010, + 0x0000, + &ep2instate, + NULL, + 1, + NULL +}; + +/* + * Handles the USB driver global events. + */ +static void usb_event(USBDriver *usbp, usbevent_t event) { + extern SerialUSBDriver SDU1; + + switch (event) { + case USB_EVENT_ADDRESS: + return; + case USB_EVENT_CONFIGURED: + chSysLockFromISR(); + + /* Enables the endpoints specified into the configuration. + Note, this callback is invoked from an ISR so I-Class functions + must be used.*/ + usbInitEndpointI(usbp, USBD1_DATA_REQUEST_EP, &ep1config); + usbInitEndpointI(usbp, USBD1_INTERRUPT_REQUEST_EP, &ep2config); + + /* Resetting the state of the CDC subsystem.*/ + sduConfigureHookI(&SDU1); + + chSysUnlockFromISR(); + return; + case USB_EVENT_RESET: + /* Falls into.*/ + case USB_EVENT_UNCONFIGURED: + /* Falls into.*/ + case USB_EVENT_SUSPEND: + chSysLockFromISR(); + + /* Disconnection event on suspend.*/ + sduSuspendHookI(&SDU1); + + chSysUnlockFromISR(); + return; + case USB_EVENT_WAKEUP: + chSysLockFromISR(); + + /* Wake up event.*/ + sduWakeupHookI(&SDU1); + + chSysUnlockFromISR(); + return; + case USB_EVENT_STALLED: + return; + } + return; +} + +/* + * Handles the USB driver global events. + */ +static void sof_handler(USBDriver *usbp) { + + (void)usbp; + + osalSysLockFromISR(); + sduSOFHookI(&SDU1); + osalSysUnlockFromISR(); +} + +/* + * USB driver configuration. + */ +const USBConfig usbcfg = { + usb_event, + get_descriptor, + sduRequestsHook, + sof_handler +}; + +/* + * Serial over USB driver configuration. + */ +const SerialUSBConfig serusbcfg = { + &USBD1, + USBD1_DATA_REQUEST_EP, + USBD1_DATA_AVAILABLE_EP, + USBD1_INTERRUPT_REQUEST_EP +}; + diff --git a/tracker/software/main.c b/tracker/software/main.c index 6f234acb..9f986de3 100644 --- a/tracker/software/main.c +++ b/tracker/software/main.c @@ -4,7 +4,6 @@ #include "debug.h" #include "threads.h" -#include "padc.h" /** * Main routine is starting up system, runs the software watchdog (module monitoring), controls LEDs @@ -27,6 +26,9 @@ int main(void) { chDbgAssert(pkt == true, "failed to init packet system"); + /* Start Serial Over USB. */ + startSDU(); + /* Start serial channels if selected. */ pktSerialStart(); @@ -37,10 +39,6 @@ int main(void) { pktEnableEventTrace(); } - #if ACTIVATE_USB - startUSB(); - #endif - TRACE_INFO("MAIN > Startup"); // Startup threads @@ -48,15 +46,12 @@ int main(void) { start_user_threads(); // Startup optional modules (eg. POSITION, LOG, ...) while(true) { - #if ACTIVATE_USB - if(isUSBactive()) { - manageShell(); - pktTraceEvents(); - continue; - } - #endif /* ACTIVATE_USB */ - /* Wait in a loop if nothing to do. */ - chThdSleep(TIME_S2I(1)); + #if ACTIVATE_USB + manageTraceAndShell(); + pktTraceEvents(); + #endif /* ACTIVATE_USB */ + /* Wait in a loop if nothing to do. */ + chThdSleep(TIME_MS2I(200)); } } diff --git a/tracker/software/pkt/channels/rxafsk.c b/tracker/software/pkt/channels/rxafsk.c index d781d381..7b99bc4e 100644 --- a/tracker/software/pkt/channels/rxafsk.c +++ b/tracker/software/pkt/channels/rxafsk.c @@ -761,8 +761,11 @@ THD_FUNCTION(pktAFSKDecoder, arg) { TIME_MS2I(100)); if(myPktBuffer == NULL) { + /* Decrease ref count on AX25 FIFO and stop PWM. */ + chFactoryReleaseObjectsFIFO(pkt_fifo); pktAddEventFlags(myHandler, EVT_AX25_NO_BUFFER); - myDriver->active_demod_object->status |= EVT_AX25_NO_BUFFER; + myDriver->active_demod_object->status |= + EVT_AX25_NO_BUFFER | EVT_PWM_QUEUE_LOCK; myDriver->decoder_state = DECODER_ERROR; break; } diff --git a/tracker/software/pkt/channels/rxpwm.c b/tracker/software/pkt/channels/rxpwm.c index b2f94883..66efd720 100644 --- a/tracker/software/pkt/channels/rxpwm.c +++ b/tracker/software/pkt/channels/rxpwm.c @@ -206,7 +206,7 @@ void pktClosePWMChannelI(ICUDriver *myICU, eventflags_t evt, pwm_code_t reason) icuDisableNotificationsI(myICU); if(myDemod->active_radio_object != NULL) { myDemod->active_radio_object->status |= (EVT_PWM_QUEUE_LOCK | evt); - pktAddEventFlagsI(myHandler, (EVT_PWM_QUEUE_LOCK | evt)); + pktAddEventFlagsI(myHandler, evt); input_queue_t *myQueue = &myDemod->active_radio_object->radio_pwm_queue; /* End of data flag. */ #if USE_12_BIT_PWM == TRUE diff --git a/tracker/software/pkt/managers/pktservice.c b/tracker/software/pkt/managers/pktservice.c index fbd46557..0d77c7c6 100644 --- a/tracker/software/pkt/managers/pktservice.c +++ b/tracker/software/pkt/managers/pktservice.c @@ -661,7 +661,6 @@ eventflags_t pktDispatchReceivedBuffer(pkt_data_object_t *pkt_buffer) { /* Increase outstanding callback count. */ handler->cb_count++; } - } return flags; } diff --git a/tracker/software/pkt/managers/pktservice.h b/tracker/software/pkt/managers/pktservice.h index 615ed0f6..d9eedfb7 100644 --- a/tracker/software/pkt/managers/pktservice.h +++ b/tracker/software/pkt/managers/pktservice.h @@ -477,6 +477,23 @@ static inline bool pktIsBufferValidAX25Frame(pkt_data_object_t *object) { && (frame_size >= PKT_MIN_FRAME)); } +/** + * @brief Gets status of frame. + * @note This returns validity (size) and CRC result. + * @details This function is called from thread level. + * + * @param[in] object pointer to a @p objects FIFO. + * + * @return The operation status. + * @retval true if the frame is valid and has good CRC. + * @retval false if the frame is valid and has bad CRC. + * + * @api + */ +static inline bool pktGetAX25FrameStatus(pkt_data_object_t *object) { + chDbgAssert(object != NULL, "no pointer to packet object buffer"); + return !(object->status & (EVT_PKT_INVALID_FRAME | EVT_AX25_CRC_ERROR)); +} /** * @brief Gets service object associated with radio. diff --git a/tracker/software/pkt/pktconf.h b/tracker/software/pkt/pktconf.h index 4016bb1b..bcb0ca5c 100644 --- a/tracker/software/pkt/pktconf.h +++ b/tracker/software/pkt/pktconf.h @@ -53,7 +53,7 @@ #define EVT_PRIORITY_BASE 0 /* - * Decoder global system events. + * Decoder global system event masks. * The packet channel object holds the global events. * Events are broadcast to any listeners. */ @@ -98,7 +98,8 @@ #define EVT_PKT_CBK_MGR_FAIL EVENT_MASK(EVT_PRIORITY_BASE + 31) -/* Decoder thread events (sent from initiator to decoder). */ +/* Decoder thread event IDs (sent from initiator to decoder). */ +/*TODO: These needs to be values and NOT bit shifted masks. */ #define DEC_COMMAND_START EVENT_MASK(EVT_PRIORITY_BASE + 0) #define DEC_COMMAND_STOP EVENT_MASK(EVT_PRIORITY_BASE + 1) @@ -107,8 +108,9 @@ #define DEC_SUSPEND_EXIT EVENT_MASK(EVT_PRIORITY_BASE + 4) -/* Reserved system thread events (in user threads level). */ -#define USB_SHELL_EVT EVENT_MASK(EVT_PRIORITY_BASE + 0) +/* Reserved system event broadcast IDs (set mask in user threads level). */ +#define USB_SHELL_EVT EVT_PRIORITY_BASE + 0 +#define USB_SDU1_EVT EVT_PRIORITY_BASE + 16 /* Response thread events (from decoder to initiator). */ #define DEC_OPEN_EXEC EVENT_MASK(EVT_PRIORITY_BASE + 15) diff --git a/tracker/software/pkt/protocols/aprs2/ax25_pad.c b/tracker/software/pkt/protocols/aprs2/ax25_pad.c index 6c58d40d..fae22e7e 100644 --- a/tracker/software/pkt/protocols/aprs2/ax25_pad.c +++ b/tracker/software/pkt/protocols/aprs2/ax25_pad.c @@ -1180,43 +1180,54 @@ void ax25_remove_addr (packet_t this_p, int n) * *------------------------------------------------------------------------------*/ -int ax25_get_num_addr (packet_t this_p) -{ - //unsigned char *pf; - int a; - int addr_bytes; +int ax25_get_num_addr (packet_t this_p) { + int a; + //int addr_bytes; - if(this_p->magic1 != MAGIC || this_p->magic2 != MAGIC) { - TRACE_ERROR("PKT > Buffer overflow"); - return 0; - } + if(this_p->magic1 != MAGIC || this_p->magic2 != MAGIC) { + TRACE_ERROR("PKT > Buffer overflow"); + return 0; + } -/* Use cached value if already set. */ + /* Use cached value if already set. */ - if (this_p->num_addr >= 0) { - return (this_p->num_addr); - } + if (this_p->num_addr >= 0) { + return (this_p->num_addr); + } -/* Otherwise, determine the number of addresses. */ + /* + * Otherwise, determine the number of addresses. + * Start with assumption of zero. + */ - this_p->num_addr = 0; /* Number of addresses extracted. */ - - addr_bytes = 0; - for (a = 0; a < this_p->frame_len && addr_bytes == 0; a++) { - if (this_p->frame_data[a] & SSID_LAST_MASK) { - addr_bytes = a + 1; - } - } + this_p->num_addr = 0; - if (addr_bytes % 7 == 0) { - int addrs = addr_bytes / 7; - if (addrs >= AX25_MIN_ADDRS && addrs <= AX25_MAX_ADDRS) { - this_p->num_addr = addrs; - } - } - - return (this_p->num_addr); + /* Check that address characters are valid. */ + + for(a = 0; + a < this_p->frame_len && a < (AX25_MAX_ADDRS * AX25_ADDR_LEN); + a++) { + /* + * Check the call sign characters with isgraph + * Could be more strict and accept upper case alpha & numeric only. + */ + if(a % 7 != 6) { + if(isgraph(this_p->frame_data[a] >> 1)) + continue; + } + if((this_p->frame_data[a] & SSID_LAST_MASK)) + break; + } /* End for. */ + + /* Check if last happened on an address boundary. */ + if (++a % 7 == 0) { + int addrs = a / 7; + if (addrs >= AX25_MIN_ADDRS && addrs <= AX25_MAX_ADDRS) { + this_p->num_addr = addrs; + } + } + return (this_p->num_addr); } @@ -1401,7 +1412,7 @@ int ax25_get_ssid (packet_t this_p, int n) } if (n >= 0 && n < this_p->num_addr) { - return ((this_p->frame_data[n*7+6] & SSID_SSID_MASK) >> SSID_SSID_SHIFT); + return ((this_p->frame_data[n * AX25_ADDR_LEN + 6] & SSID_SSID_MASK) >> SSID_SSID_SHIFT); } else { TRACE_ERROR ("Internal error: ax25_get_ssid(%d), num_addr=%d", n, this_p->num_addr); @@ -1437,7 +1448,8 @@ void ax25_set_ssid (packet_t this_p, int n, int ssid) if (n >= 0 && n < this_p->num_addr) { - this_p->frame_data[n*7+6] = (this_p->frame_data[n*7+6] & ~ SSID_SSID_MASK) | + this_p->frame_data[n * AX25_ADDR_LEN + 6] = + (this_p->frame_data[n * AX25_ADDR_LEN + 6] & ~ SSID_SSID_MASK) | ((ssid << SSID_SSID_SHIFT) & SSID_SSID_MASK) ; } else { @@ -1476,7 +1488,7 @@ int ax25_get_h (packet_t this_p, int n) } if (n >= 0 && n < this_p->num_addr) { - return ((this_p->frame_data[n*7+6] & SSID_H_MASK) >> SSID_H_SHIFT); + return ((this_p->frame_data[n * AX25_ADDR_LEN + 6] & SSID_H_MASK) >> SSID_H_SHIFT); } else { TRACE_ERROR ("PKT > Internal error: ax25_get_h(%d), num_addr=%d", n, this_p->num_addr); @@ -1511,7 +1523,7 @@ void ax25_set_h (packet_t this_p, int n) } if (n >= 0 && n < this_p->num_addr) { - this_p->frame_data[n*7+6] |= SSID_H_MASK; + this_p->frame_data[n * AX25_ADDR_LEN + 6] |= SSID_H_MASK; } else { TRACE_ERROR ("PKT > Internal error: ax25_set_hd(%d), num_addr=%d", n, this_p->num_addr); @@ -1921,15 +1933,13 @@ void ax25_format_addrs (packet_t this_p, char *result, int8_t size) } *result = '\0'; - /* New in 0.9. */ - /* Don't get upset if no addresses. */ - /* This will allow packets that do not comply to AX.25 format. */ + /* There must be at least two addresses. */ - if (this_p->num_addr == 0) { + if (this_p->num_addr < 2) { return; } - /* TODO: Make a safe strcat function. */ + /* TODO: Refactor this to use a single loop and safe write to buffer. */ ax25_get_addr_with_ssid (this_p, AX25_SOURCE, stemp); if(size - (strlen(stemp) + 1) < 2) diff --git a/tracker/software/pkt/protocols/aprs2/ax25_pad.h b/tracker/software/pkt/protocols/aprs2/ax25_pad.h index 79223614..a8e95999 100644 --- a/tracker/software/pkt/protocols/aprs2/ax25_pad.h +++ b/tracker/software/pkt/protocols/aprs2/ax25_pad.h @@ -37,6 +37,9 @@ /* to be safe. */ #define AX25_MAX_ADDR_LEN 12 +/* The length of an address field in an AX25 header. */ +#define AX25_ADDR_LEN 7 + /* Previously 1 when considering only APRS. */ #define AX25_MIN_INFO_LEN 0U @@ -61,9 +64,9 @@ /* The more general case. */ /* An AX.25 frame can have a control byte and no protocol. */ -#define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 ) +#define AX25_MIN_PACKET_LEN ( 2 * AX25_ADDR_LEN + 1 ) -#define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + 3 + AX25_MAX_INFO_LEN) +#define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * AX25_ADDR_LEN + 2 + 3 + AX25_MAX_INFO_LEN) #define AX25_UI_FRAME 3 /* Control field value. */ diff --git a/tracker/software/portab.c b/tracker/software/portab.c index e8719894..71c5ad76 100644 --- a/tracker/software/portab.c +++ b/tracker/software/portab.c @@ -18,6 +18,7 @@ #include "hal.h" #include "chprintf.h" #include "portab.h" +#include "usb.h" #include /*===========================================================================*/ @@ -158,6 +159,10 @@ void sysConfigureCoreIO(void) { palSetLineMode(LINE_I2C_SCL, PAL_MODE_ALTERNATE(4) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_OTYPE_OPENDRAIN); // SCL + + #if ACTIVATE_USB + startUSB(); + #endif } /** @} */ diff --git a/tracker/software/portab.h b/tracker/software/portab.h index fa35526a..73c08e2d 100644 --- a/tracker/software/portab.h +++ b/tracker/software/portab.h @@ -21,7 +21,7 @@ #define USE_SPI_ATTACHED_RADIO TRUE -#define DUMP_PACKET_TO_SERIAL TRUE +#define DUMP_PACKET_TO_SERIAL FALSE /* * TODO: Need to use radio unit ID to set assigned GPIO & SPI. @@ -67,7 +67,7 @@ #endif //#define LINE_PWM_MIRROR PAL_LINE(GPIOA, 8U) -#define LINE_GPIO_PIN PAL_LINE(GPIOA, 8U) +#define LINE_GPIO_PIN PAL_LINE(GPIOA, 8U) /** * ICU related definitions. @@ -124,7 +124,7 @@ extern "C" { void pktSetLineModeICU(void); void pktSerialStart(void); void dbgWrite(uint8_t level, uint8_t *buf, uint32_t len); - int dbgPrintf(uint8_t level, const char *format, ...); + int dbgPrintf(uint8_t level, const char *format, ...); void pktWrite(uint8_t *buf, uint32_t len); void pktPowerUpRadio(radio_unit_t radio); void pktPowerDownRadio(radio_unit_t radio); diff --git a/tracker/software/protocols/packet/aprs.c b/tracker/software/protocols/packet/aprs.c index a6d768e3..c0260398 100644 --- a/tracker/software/protocols/packet/aprs.c +++ b/tracker/software/protocols/packet/aprs.c @@ -45,7 +45,6 @@ static bool dedupe_initialized; const conf_command_t command_list[] = { {TYPE_INT, "pos_pri.active", sizeof(conf_sram.pos_pri.thread_conf.active), &conf_sram.pos_pri.thread_conf.active }, {TYPE_TIME, "pos_pri.init_delay", sizeof(conf_sram.pos_pri.thread_conf.init_delay), &conf_sram.pos_pri.thread_conf.init_delay }, -/* {TYPE_TIME, "pos_pri.send_spacing", sizeof(conf_sram.pos_pri.thread_conf.send_spacing), &conf_sram.pos_pri.thread_conf.send_spacing },*/ {TYPE_INT, "pos_pri.sleep_conf.type", sizeof(conf_sram.pos_pri.thread_conf.sleep_conf.type), &conf_sram.pos_pri.thread_conf.sleep_conf.type }, {TYPE_INT, "pos_pri.sleep_conf.vbat_thres", sizeof(conf_sram.pos_pri.thread_conf.sleep_conf.vbat_thres), &conf_sram.pos_pri.thread_conf.sleep_conf.vbat_thres}, {TYPE_INT, "pos_pri.sleep_conf.vsol_thres", sizeof(conf_sram.pos_pri.thread_conf.sleep_conf.vsol_thres), &conf_sram.pos_pri.thread_conf.sleep_conf.vsol_thres}, @@ -54,8 +53,6 @@ const conf_command_t command_list[] = { {TYPE_INT, "pos_pri.freq", sizeof(conf_sram.pos_pri.radio_conf.freq), &conf_sram.pos_pri.radio_conf.freq }, {TYPE_INT, "pos_pri.mod", sizeof(conf_sram.pos_pri.radio_conf.mod), &conf_sram.pos_pri.radio_conf.mod }, {TYPE_INT, "pos_pri.rssi", sizeof(conf_sram.pos_pri.radio_conf.rssi), &conf_sram.pos_pri.radio_conf.rssi }, -/* {TYPE_INT, "pos_pri.speed", sizeof(conf_sram.pos_pri.radio_conf.speed), &conf_sram.pos_pri.radio_conf.speed },*/ -/* {TYPE_INT, "pos_pri.redundantTx", sizeof(conf_sram.pos_pri.radio_conf.redundantTx), &conf_sram.pos_pri.radio_conf.redundantTx },*/ {TYPE_STR, "pos_pri.call", sizeof(conf_sram.pos_pri.call), &conf_sram.pos_pri.call }, {TYPE_STR, "pos_pri.path", sizeof(conf_sram.pos_pri.path), &conf_sram.pos_pri.path }, {TYPE_INT, "pos_pri.symbol", sizeof(conf_sram.pos_pri.symbol), &conf_sram.pos_pri.symbol }, @@ -64,7 +61,6 @@ const conf_command_t command_list[] = { {TYPE_INT, "pos_sec.active", sizeof(conf_sram.pos_sec.thread_conf.active), &conf_sram.pos_sec.thread_conf.active }, {TYPE_TIME, "pos_sec.init_delay", sizeof(conf_sram.pos_sec.thread_conf.init_delay), &conf_sram.pos_sec.thread_conf.init_delay }, -/* {TYPE_TIME, "pos_sec.send_spacing", sizeof(conf_sram.pos_sec.thread_conf.send_spacing), &conf_sram.pos_sec.thread_conf.send_spacing },*/ {TYPE_INT, "pos_sec.sleep_conf.type", sizeof(conf_sram.pos_sec.thread_conf.sleep_conf.type), &conf_sram.pos_sec.thread_conf.sleep_conf.type }, {TYPE_INT, "pos_sec.sleep_conf.vbat_thres", sizeof(conf_sram.pos_sec.thread_conf.sleep_conf.vbat_thres), &conf_sram.pos_sec.thread_conf.sleep_conf.vbat_thres}, {TYPE_INT, "pos_sec.sleep_conf.vsol_thres", sizeof(conf_sram.pos_sec.thread_conf.sleep_conf.vsol_thres), &conf_sram.pos_sec.thread_conf.sleep_conf.vsol_thres}, @@ -73,8 +69,6 @@ const conf_command_t command_list[] = { {TYPE_INT, "pos_sec.freq", sizeof(conf_sram.pos_sec.radio_conf.freq), &conf_sram.pos_sec.radio_conf.freq }, {TYPE_INT, "pos_sec.mod", sizeof(conf_sram.pos_sec.radio_conf.mod), &conf_sram.pos_sec.radio_conf.mod }, {TYPE_INT, "pos_sec.rssi", sizeof(conf_sram.pos_sec.radio_conf.rssi), &conf_sram.pos_sec.radio_conf.rssi }, -/* {TYPE_INT, "pos_sec.speed", sizeof(conf_sram.pos_sec.radio_conf.speed), &conf_sram.pos_sec.radio_conf.speed },*/ -/* {TYPE_INT, "pos_sec.redundantTx", sizeof(conf_sram.pos_sec.radio_conf.redundantTx), &conf_sram.pos_sec.radio_conf.redundantTx },*/ {TYPE_STR, "pos_sec.call", sizeof(conf_sram.pos_sec.call), &conf_sram.pos_sec.call }, {TYPE_STR, "pos_sec.path", sizeof(conf_sram.pos_sec.path), &conf_sram.pos_sec.path }, {TYPE_INT, "pos_sec.symbol", sizeof(conf_sram.pos_sec.symbol), &conf_sram.pos_sec.symbol }, @@ -138,13 +132,7 @@ const conf_command_t command_list[] = { {TYPE_INT, "aprs.active", sizeof(conf_sram.aprs.thread_conf.active), &conf_sram.aprs.thread_conf.active }, {TYPE_TIME, "aprs.init_delay", sizeof(conf_sram.aprs.thread_conf.init_delay), &conf_sram.aprs.thread_conf.init_delay }, - /* - {TYPE_TIME, "aprs.packet_spacing", sizeof(conf_sram.aprs.rx.thread_conf.packet_spacing), &conf_sram.aprs.rx.thread_conf.packet_spacing }, - {TYPE_INT, "aprs.sleep_conf.type", sizeof(conf_sram.aprs.rx.thread_conf.sleep_conf.type), &conf_sram.aprs.rx.thread_conf.sleep_conf.type }, - {TYPE_INT, "aprs.sleep_conf.vbat_thres", sizeof(conf_sram.aprs.rx.thread_conf.sleep_conf.vbat_thres), &conf_sram.aprs.rx.thread_conf.sleep_conf.vbat_thres}, - {TYPE_INT, "aprs.sleep_conf.vsol_thres", sizeof(conf_sram.aprs.rx.thread_conf.sleep_conf.vsol_thres), &conf_sram.aprs.rx.thread_conf.sleep_conf.vsol_thres}, - {TYPE_TIME, "aprs.rx.cycle", sizeof(conf_sram.aprs.rx.thread_conf.cycle), &conf_sram.aprs.rx.thread_conf.cycle }, - */ + {TYPE_INT, "aprs.rx.freq", sizeof(conf_sram.aprs.rx.radio_conf.freq), &conf_sram.aprs.rx.radio_conf.freq }, {TYPE_INT, "aprs.rx.mod", sizeof(conf_sram.aprs.rx.radio_conf.mod), &conf_sram.aprs.rx.radio_conf.mod }, {TYPE_INT, "aprs.rx.speed", sizeof(conf_sram.aprs.rx.radio_conf.speed), &conf_sram.aprs.tx.radio_conf.speed }, @@ -179,7 +167,7 @@ const APRSCommand aprs_commands[] = { {"?aprsd", aprs_send_aprsd_message}, {"?aprsh", aprs_send_aprsh_message}, {"?aprsp", aprs_send_position_beacon}, - {"?gpio", aprs_handle_gpio_command}, + {"?gpio", aprs_execute_gpio_command}, {"?gps", aprs_handle_gps_command}, {"?reset", aprs_execute_system_reset}, {"?save", aprs_execute_config_save}, @@ -188,7 +176,7 @@ const APRSCommand aprs_commands[] = { {NULL, NULL} }; -/* +/** * @brief parse arguments from a command string. * * @return pointer to next element in string. @@ -227,7 +215,7 @@ static char *aprs_parse_arguments(char *str, char **saveptr) { return *p != '\0' ? p : NULL; } -/* +/** * @brief Execute a command in an APRS message. * @notes Known commands are in APRS command table. * @notes Commands themselves return only MSG_OK or MSG_ERROR. @@ -252,7 +240,7 @@ static msg_t aprs_cmd_exec(const APRSCommand *acp, return MSG_TIMEOUT; } -/* +/** * */ void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len) @@ -286,7 +274,7 @@ void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len) * @notes - Number of satellites being used * @notes - Number of cycles where GPS has been lost (if applicable in cycle) * - * @param[in] callsign originator calls sign + * @param[in] callsign origination call sign * @param[in] path path to use * @param[in] symbol symbol for originator * @param[in] dataPoint position data object @@ -547,7 +535,7 @@ msg_t aprs_send_aprsh_message(aprs_identity_t *id, * @retval MSG_OK if the command completed. * @retval MSG_ERROR if there was an error. */ -msg_t aprs_handle_gpio_command(aprs_identity_t *id, +msg_t aprs_execute_gpio_command(aprs_identity_t *id, int argc, char *argv[]) { if(argc != 1) return MSG_ERROR; @@ -1018,14 +1006,15 @@ static bool aprs_decode_message(packet_t pp) { msg_t msg = aprs_cmd_exec(aprs_commands, cmd, &identity, n, args); if(msg == MSG_TIMEOUT) { - TRACE_INFO("RX > Command not found by parser"); + TRACE_INFO("RX > No command found in message"); } if(msg_id_rx[0]) { /* Incoming message ID exists so an ACK or REJ has to be sent. */ char buf[16]; chsnprintf(buf, sizeof(buf), "%s%s", - (msg == MSG_OK) ? "ack" : "rej", msg_id_rx); + (msg == MSG_OK || msg == MSG_TIMEOUT) ? + "ack" : "rej", msg_id_rx); /* * Use the receiving node identity as sender. @@ -1050,31 +1039,33 @@ static bool aprs_decode_message(packet_t pp) { return false; } -/* +/** * Transmit failure will release the packet memory. */ static void aprs_digipeat(packet_t pp) { - if(!dedupe_initialized) { - dedupe_init(TIME_S2I(10)); - dedupe_initialized = true; - } + if(!dedupe_initialized) { + dedupe_init(TIME_S2I(10)); + dedupe_initialized = true; + } - if(!dedupe_check(pp, 0)) { // Last identical packet older than 10 seconds - packet_t result = digipeat_match(0, pp, conf_sram.aprs.rx.call, - conf_sram.aprs.tx.call, alias_re, - wide_re, 0, preempt, NULL); - if(result != NULL) { // Should be digipeated - dedupe_remember(result, 0); - /* If transmit fails the packet is freed. */ - transmitOnRadio(result, - conf_sram.aprs.tx.radio_conf.freq, - 0, - 0, - conf_sram.aprs.tx.radio_conf.pwr, - conf_sram.aprs.tx.radio_conf.mod, - conf_sram.aprs.tx.radio_conf.rssi); - } - } + if(!dedupe_check(pp, 0)) { // Last identical packet older than 10 seconds + packet_t result = digipeat_match(0, pp, conf_sram.aprs.rx.call, + conf_sram.aprs.tx.call, alias_re, + wide_re, 0, preempt, NULL); + if(result != NULL) { // Should be digipeated + dedupe_remember(result, 0); + /* If transmit fails the packet buffer is released. */ + if(!transmitOnRadio(result, + conf_sram.aprs.tx.radio_conf.freq, + 0, + 0, + conf_sram.aprs.tx.radio_conf.pwr, + conf_sram.aprs.tx.radio_conf.mod, + conf_sram.aprs.tx.radio_conf.rssi)) { + TRACE_INFO("RX > Failed to digipeat packet"); + } /* TX failed. */ + } /* Should be digipteated. */ + } /* Duplicate check. */ } /** @@ -1106,7 +1097,8 @@ void aprs_decode_packet(packet_t pp) { do { v++; ax25_get_addr_with_ssid(pp, ax25_get_heard(pp)-v, call); - } while(ax25_get_heard(pp) - v >= AX25_SOURCE && (!strncmp("WIDE", call, 4) || !strncmp("TRACE", call, 5))); + } while(((ax25_get_heard(pp) - v) >= AX25_SOURCE) + && (!strncmp("WIDE", call, 4) || !strncmp("TRACE", call, 5))); // Fill/Update direct list sysinterval_t first_time = 0xFFFFFFFF; // Timestamp of oldest heard list entry @@ -1142,7 +1134,9 @@ void aprs_decode_packet(packet_t pp) { * Execute any command found in the message. * If not then digipeat it. */ - if(pinfo[0] == ':') digipeat = aprs_decode_message(pp); // ax25_get_dti(pp) + if(pinfo[0] == ':') { + digipeat = aprs_decode_message(pp); // ax25_get_dti(pp) + } // Digipeat packet if(conf_sram.aprs.dig_active && digipeat) { diff --git a/tracker/software/protocols/packet/aprs.h b/tracker/software/protocols/packet/aprs.h index 57e62886..059666e1 100644 --- a/tracker/software/protocols/packet/aprs.h +++ b/tracker/software/protocols/packet/aprs.h @@ -49,6 +49,7 @@ #define SYM_CAR 0x2F3E #define SYM_SHIP 0x2F73 #define SYM_DIGIPEATER 0x2F23 +#define SYM_ANTENNA 0x2F72 #define APRS_HEARD_LIST_SIZE 20 @@ -104,7 +105,7 @@ extern "C" { int argc, char *argv[]); msg_t aprs_send_aprsh_message(aprs_identity_t *id, int argc, char *argv[]); - msg_t aprs_handle_gpio_command(aprs_identity_t *id, + msg_t aprs_execute_gpio_command(aprs_identity_t *id, int argc, char *argv[]); msg_t aprs_handle_gps_command(aprs_identity_t *id, int argc, char *argv[]); diff --git a/tracker/software/threads/collector.c b/tracker/software/threads/collector.c index 0dc6d8e9..3438d697 100644 --- a/tracker/software/threads/collector.c +++ b/tracker/software/threads/collector.c @@ -27,16 +27,14 @@ static bool threadStarted = false; /** * Returns most recent data point which is complete. */ -dataPoint_t* getLastDataPoint(void) -{ +dataPoint_t* getLastDataPoint(void) { return lastDataPoint; } /* * */ -void waitForNewDataPoint(void) -{ +void waitForNewDataPoint(void) { uint32_t old_id = getLastDataPoint()->id; while(old_id == getLastDataPoint()->id) chThdSleep(TIME_S2I(1)); @@ -45,8 +43,8 @@ void waitForNewDataPoint(void) /* * */ -static void aquirePosition(dataPoint_t* tp, dataPoint_t* ltp, sysinterval_t timeout) -{ +static void aquirePosition(dataPoint_t* tp, dataPoint_t* ltp, + sysinterval_t timeout) { sysinterval_t start = chVTGetSystemTime(); gpsFix_t gpsFix; @@ -62,7 +60,7 @@ static void aquirePosition(dataPoint_t* tp, dataPoint_t* ltp, sysinterval_t time bool status = GPS_Init(); if(status) { - // Search for lock as long enough power is available + // Search for lock as long as enough power is available do { batt = stm32_get_vbat(); gps_get_fix(&gpsFix); @@ -185,7 +183,7 @@ static void getSensors(dataPoint_t* tp) tp->sen_e1_press = 0; tp->sen_e1_hum = 0; tp->sen_e1_temp = 0; - bme280_error |= 0x2; + bme280_error |= 0x4; } // External BME280 Sensor 2 @@ -199,8 +197,11 @@ static void getSensors(dataPoint_t* tp) tp->sen_e2_press = 0; tp->sen_e2_hum = 0; tp->sen_e2_temp = 0; - bme280_error |= 0x4; + bme280_error |= 0x10; } +#else + /* Set status to "not installed". */ + bme280_error |= 0x28; #endif // Measure various temperature sensors tp->stm32_temp = stm32_get_temp(); @@ -214,7 +215,19 @@ static void getSensors(dataPoint_t* tp) * */ static void setSystemStatus(dataPoint_t* tp) { - // Set system errors + + /* + * Set system errors. + * + * Bit usage: + * - 0:1 I2C status + * - 2:2 GPS status + * - 3:4 pac1720 status + * - 5:7 OV5640 status + * - 8:9 BMEi1 status (0 = OK, 1 = Fail, 2 = Not fitted) + * - 9:10 BMEe1 status (0 = OK, 1 = Fail, 2 = Not fitted) + * - 10:11 BMEe2 status (0 = OK, 1 = Fail, 2 = Not fitted) + */ tp->sys_error = 0; tp->sys_error |= (I2C_hasError() & 0x1) << 0; @@ -222,7 +235,7 @@ static void setSystemStatus(dataPoint_t* tp) { tp->sys_error |= (pac1720_hasError() & 0x3) << 3; tp->sys_error |= (OV5640_hasError() & 0x7) << 5; - tp->sys_error |= (bme280_error & 0x7) << 8; + tp->sys_error |= (bme280_error & 0x3F) << 8; // Set system time tp->sys_time = TIME_I2S(chVTGetSystemTime()); @@ -320,7 +333,7 @@ THD_FUNCTION(collectorThread, arg) { "%s Pos %d.%05d %d.%05d Alt %dm\r\n" "%s Sats %d TTFF %dsec\r\n" "%s ADC Vbat=%d.%03dV Vsol=%d.%03dV Pbat=%dmW\r\n" - "%s AIR p=%6d.%01dPa T=%2d.%02ddegC phi=%2d.%01d%%", + "%s AIR p=%d.%01dPa T=%d.%02ddegC phi=%d.%01d%%", tp->id, TRACE_TAB, time.year, time.month, time.day, time.hour, time.minute, time.day, TRACE_TAB, tp->gps_lat/10000000, (tp->gps_lat > 0 ? 1:-1)*(tp->gps_lat/100)%100000, tp->gps_lon/10000000, (tp->gps_lon > 0 ? 1:-1)*(tp->gps_lon/100)%100000, tp->gps_alt, @@ -345,9 +358,9 @@ THD_FUNCTION(collectorThread, arg) { */ void init_data_collector(void) { - if(!threadStarted) - { - threadStarted = true; + if(!threadStarted) { + + threadStarted = true; TRACE_INFO("COLL > Startup data collector thread"); thread_t *th = chThdCreateFromHeap(NULL, diff --git a/tracker/software/threads/rxtx/image.c b/tracker/software/threads/rxtx/image.c index aec2804c..ac9d4fe6 100644 --- a/tracker/software/threads/rxtx/image.c +++ b/tracker/software/threads/rxtx/image.c @@ -648,14 +648,14 @@ THD_FUNCTION(imgThread, arg) { chThdSleep(TIME_S2I(60)); continue; } - + uint32_t my_image_id = gimage_id++; /* Create image capture buffer. */ uint8_t *buffer = chHeapAllocAligned(NULL, conf->buf_size, DMA_FIFO_BURST_ALIGN); if(buffer == NULL) { /* Could not get a capture buffer. */ TRACE_WARN("IMG > Unable to get capture buffer for image %i", - gimage_id); + my_image_id); /* Allow time for other threads. */ chThdSleep(TIME_MS2I(10)); /* Try again at next run time. */ @@ -668,11 +668,11 @@ THD_FUNCTION(imgThread, arg) { /* Nothing captured? */ if(size_sampled == 0) { TRACE_INFO("IMG > Encode/Transmit SSDV (camera error) ID=%d", - gimage_id); + my_image_id); if(!transmit_image_packets(noCameraFound, sizeof(noCameraFound), - conf, (uint8_t)(gimage_id))) { + conf, (uint8_t)(my_image_id))) { TRACE_ERROR("IMG > Error in encoding dummy image %i" - " - discarded", gimage_id); + " - discarded", my_image_id); } /* Return the buffer to the heap. */ chHeapFree(buffer); @@ -699,7 +699,7 @@ THD_FUNCTION(imgThread, arg) { chsnprintf(filename, sizeof(filename), "r%02xi%04x.jpg", getLastDataPoint()->reset % 0xFF, - (gimage_id - 1) % 0xFFFF); + (my_image_id) % 0xFFFF); writeBufferToFile(filename, &buffer[soi], size_sampled - soi); } /* End initSD() */ @@ -710,13 +710,12 @@ THD_FUNCTION(imgThread, arg) { } /* Encode and transmit picture. */ - TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id); + TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", my_image_id); if(!transmit_image_packets(buffer, size_sampled, conf, - (uint8_t)(gimage_id))) { + (uint8_t)(my_image_id))) { TRACE_ERROR("IMG > Error in encoding snapshot image" - " %i - discarded", gimage_id); + " %i - discarded", my_image_id); } - gimage_id++; break; } /* End if SOI in buffer. */ } /* End while soi < size_sampled - 1. */ diff --git a/tracker/software/threads/rxtx/radio.c b/tracker/software/threads/rxtx/radio.c index 61a01b14..403dfddd 100644 --- a/tracker/software/threads/rxtx/radio.c +++ b/tracker/software/threads/rxtx/radio.c @@ -69,11 +69,12 @@ void mapCallback(pkt_data_object_t *pkt_buff) { pktDiagnosticOutput(pkt_buff->handler, pkt_buff); #endif */ -if(pktIsBufferValidAX25Frame(pkt_buff)) { +if(pktGetAX25FrameStatus(pkt_buff)) { + /* Perform the callback. */ processPacket(frame_buffer, frame_size); } else { - TRACE_INFO("RX > Invalid frame - dropped"); + TRACE_INFO("RX > Frame has bad CRC - dropped"); } }