From 299725405da1589aff1fce7151b836ee270b7bf5 Mon Sep 17 00:00:00 2001 From: Richard Eoin Meadows Date: Sat, 30 Aug 2014 21:11:35 +0100 Subject: [PATCH] Working RTTY! And new gps stuff --- firmware/inc/gps.h | 131 +-------------- firmware/inc/hw_config.h | 3 +- firmware/inc/rtty.h | 32 ++++ firmware/inc/si4060.h | 2 +- firmware/inc/system/clock.h | 15 +- firmware/inc/tc/tc_driver.h | 4 +- firmware/inc/ubx_messages.h | 240 +++++++++++++++++++++++++++ firmware/src/gps.c | 318 +++++++++++++++++------------------- firmware/src/main.c | 121 +++++++++----- firmware/src/rtty.c | 139 ++++++++++++++++ firmware/src/si4060.c | 8 +- firmware/src/tc/tc_driver.c | 4 +- firmware/src/telemetry.c | 73 +++++++++ 13 files changed, 741 insertions(+), 349 deletions(-) create mode 100644 firmware/inc/rtty.h create mode 100644 firmware/inc/ubx_messages.h create mode 100644 firmware/src/rtty.c create mode 100644 firmware/src/telemetry.c diff --git a/firmware/inc/gps.h b/firmware/inc/gps.h index 5af9eac..d47b480 100644 --- a/firmware/inc/gps.h +++ b/firmware/inc/gps.h @@ -25,134 +25,13 @@ #ifndef GPS_H #define GPS_H -/* We use the packed attribute so we can copy direct to structs */ -#define __PACKED__ __attribute__((packed)) +void gps_update(); -/** - * UBX CFG ANT Antenna Control Settings - */ -volatile struct ubx_cfg_ant { - uint16_t flags; - uint16_t pins; -} __PACKED__ ubx_cfg_ant; -/** - * UBX CFG NAV5 Navigation Engine Settings - */ -volatile struct ubx_cfg_nav5 { - uint16_t mask; - uint8_t dynModel; - uint8_t fixMode; - int32_t fixedAlt; - uint32_t fixedAltVar; - int8_t minElev; - uint8_t drLimit; - uint16_t pDop; - uint16_t tDop; - uint16_t pAcc; - uint16_t tAcc; - uint8_t staticHoldThresh; - uint8_t dgpsTimeOut; - uint32_t res2; - uint32_t res3; - uint32_t res4; -} __PACKED__ ubx_cfg_nav5; -/** - * UBX CFG TP TimePulse Parameters - */ -volatile struct ubx_cfg_tp { - uint32_t interval; - uint32_t length; - int8_t status; - uint8_t timeRef; - uint8_t flags; - uint8_t res; - int16_t antennaCableDelay; - int16_t rfGroupDelay; - int32_t userDelay; -} __PACKED__ ubx_cfg_tp; -/** - * UBX CFG TP5 TimePulse Parameters - */ -volatile struct ubx_cfg_tp5 { - uint8_t tpIdx; - uint8_t res0; - uint16_t res1; - int16_t antCableDelay; - int16_t rfGroupDelay; - uint32_t freqPeriod; - uint32_t freqPeriodLoc; - uint32_t pulseLenRatio; - uint32_t pulseLenRatioLock; - int32_t userConfigDelay; - uint32_t flags; -} __PACKED__ ubx_cfg_tp5; -/** - * UBX NAV POSLLH Geodetic Position Solution - */ -volatile struct ubx_nav_posllh { - uint32_t iTOW; - int32_t lon; - int32_t lat; - int32_t height; - int32_t hMSL; - uint32_t hAcc; - uint32_t vAcc; -} __PACKED__ ubx_nav_posllh; -/** - * UBX NAV TIMEUTC - */ -volatile struct ubx_nav_timeutc { - uint32_t iTOW; - uint32_t tAcc; - int32_t nano; - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t min; - uint8_t sec; - uint8_t valid; -} __PACKED__ ubx_nav_timeutc; -/** - * UBX NAV SOL Navigation Solution Information - */ -volatile struct ubx_nav_sol { - uint32_t iTOW; - int32_t fTOW; - uint16_t week; - uint8_t gpsFix; - uint8_t flags; - int32_t ecefX; - int32_t ecefY; - int32_t ecefZ; - uint32_t pAcc; - int32_t ecefVX; - int32_t ecefVY; - int32_t ecefVZ; - uint32_t sAcc; - uint16_t pDOP; - uint8_t res1; - uint8_t numSV; - uint32_t res2; -} __PACKED__ ubx_nav_sol; +struct ubx_nav_posllh gps_get_nav_posllh(); +struct ubx_nav_sol gps_get_nav_sol(); +struct ubx_nav_timeutc gps_get_nav_timeutc(); -/** - * UBX Dynamic Platform Model - */ -enum { - UBX_PLATFORM_MODEL_PORTABLE = 0, - UBX_PLATFORM_MODEL_STATIONARY = 2, - UBX_PLATFORM_MODEL_PEDESTRIAN = 3, - UBX_PLATFORM_MODEL_AUTOMOTIVE = 4, - UBX_PLATFORM_MODEL_SEA = 5, - UBX_PLATFORM_MODEL_AIRBORNE_1G = 6, - UBX_PLATFORM_MODEL_AIRBORNE_2G = 7, - UBX_PLATFORM_MODEL_AIRBORNE_4G = 8, -}; - - -void usart_loopback_test(void); -char* gps_get_frame(void); void gps_init(void); +void usart_loopback_test(void); #endif /* GPS_H */ diff --git a/firmware/inc/hw_config.h b/firmware/inc/hw_config.h index 4946682..59def64 100644 --- a/firmware/inc/hw_config.h +++ b/firmware/inc/hw_config.h @@ -76,8 +76,7 @@ #define GPS_TIME_PIN PIN_PA28 #define GPS_TIME_PINMUX PINMUX_PA28H_GCLK_IO0 #define GPS_SERCOM_MUX USART_RX_1_TX_0_XCK_1 -#define GPS_TIMEPULSE_FREQ 4000000 -// 32768 +#define GPS_TIMEPULSE_FREQ 1000 #define GPS_PLATFORM_MODEL UBX_PLATFORM_MODEL_AIRBORNE_1G /** diff --git a/firmware/inc/rtty.h b/firmware/inc/rtty.h new file mode 100644 index 0000000..600e323 --- /dev/null +++ b/firmware/inc/rtty.h @@ -0,0 +1,32 @@ +/* + * Bit-bangs RTTY + * Copyright (C) 2014 Richard Meadows + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef RTTY_H +#define RTTY_H + +int rtty_active(void); +int rtty_set_string(char* string, uint32_t length); +void rtty_tick(void); + +#endif /* RTTY_H */ diff --git a/firmware/inc/si4060.h b/firmware/inc/si4060.h index 96bfd61..2ffaaba 100644 --- a/firmware/inc/si4060.h +++ b/firmware/inc/si4060.h @@ -35,7 +35,7 @@ void si4060_shutdown(void); #define XO_FREQ 16000000UL #define RF_FREQ_HZ 434600000.0f -#define RF_DEV_HZ 100.0f +#define RF_DEV_HZ 200.0f #define F_INT (2 * XO_FREQ / 8) #define FDIV_INTE ( (RF_FREQ_HZ / F_INT) - 1) diff --git a/firmware/inc/system/clock.h b/firmware/inc/system/clock.h index 5715b4c..ead254e 100644 --- a/firmware/inc/system/clock.h +++ b/firmware/inc/system/clock.h @@ -394,6 +394,19 @@ enum system_clock_apb_bus { SYSTEM_CLOCK_APB_APBC, }; +/** + * Possible NVM flash wait state settings + */ +enum system_wait_states { + /** Wait state maximum frequencies at 1.8V */ + SYSTEM_WAIT_STATE_1_8V_14MHZ = 0, + SYSTEM_WAIT_STATE_1_8V_28MHZ = 1, + SYSTEM_WAIT_STATE_1_8V_42MHZ = 2, + SYSTEM_WAIT_STATE_1_8V_48MHZ = 3, + /** Wait state maximum frequencies at 3.3V */ + SYSTEM_WAIT_STATE_3_3V_24MHZ = 0, + SYSTEM_WAIT_STATE_3_3V_48MHZ = 1, +}; void system_clock_source_osc8m_set_config(enum system_osc8m_div prescaler, bool run_in_standby, @@ -819,7 +832,7 @@ void system_clock_init(void); * * \param[in] wait_states Number of wait states to use for internal flash */ -static inline void system_flash_set_waitstates(uint8_t wait_states) +static inline void system_flash_set_waitstates(const enum system_wait_states wait_states) { assert(NVMCTRL_CTRLB_RWS((uint32_t)wait_states) == ((uint32_t)wait_states << NVMCTRL_CTRLB_RWS_Pos)); diff --git a/firmware/inc/tc/tc_driver.h b/firmware/inc/tc/tc_driver.h index 1c4311a..25d81e9 100644 --- a/firmware/inc/tc/tc_driver.h +++ b/firmware/inc/tc/tc_driver.h @@ -543,9 +543,9 @@ void tc_set_top_value (Tc* const hw, const uint32_t top_value); void tc_enable_events(Tc* const hw, - struct tc_events *const events); + struct tc_events *const events); void tc_disable_events(Tc* const hw, - struct tc_events *const events); + struct tc_events *const events); enum tc_status_t tc_init(Tc* const hw, enum gclk_generator source_clock, diff --git a/firmware/inc/ubx_messages.h b/firmware/inc/ubx_messages.h new file mode 100644 index 0000000..084156c --- /dev/null +++ b/firmware/inc/ubx_messages.h @@ -0,0 +1,240 @@ +/* + * UBX Message definitions + * Copyright (C) 2014 Richard Meadows + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef UBX_MESSAGES_H +#define UBX_MESSAGES_H + +#include "samd20.h" + +/** We use the packed attribute so we can copy direct to structs */ +#define __PACKED__ __attribute__((packed)) +/** UBX Message IDs are 16-bit types */ +typedef uint16_t ubx_message_id_t; +/** Used for storing the state of each packet */ +enum ubx_packet_state { + UBX_PACKET_WAITING, + UBX_PACKET_ACK, + UBX_PACKET_NACK, +}; +/** Generic UBX Message Type. Each message type extended is from this */ +typedef struct { + ubx_message_id_t id; + enum ubx_packet_state state; +} ubx_message_t; + +/** UBX Class Types */ +enum { + UBX_NAV = 0x01, + UBX_RXM = 0x02, + UBX_INF = 0x04, + UBX_ACK = 0x05, + UBX_CFG = 0x06, + UBX_MON = 0x0A, + UBX_AID = 0x0B, + UBX_TIM = 0x0D, + UBX_ESF = 0x10, +}; + +/** + * ============================================================================= + * UBX CFG Message Types ======================================================= + * ============================================================================= + */ + +/** + * UBX CFG ANT Antenna Control Settings + */ +__PACKED__ struct ubx_cfg_ant { + ubx_message_id_t id; + enum ubx_packet_state state; + struct { + uint16_t flags; + uint16_t pins; + } payload; +}; +/** + * UBX CFG NAV5 Navigation Engine Settings + */ +__PACKED__ struct ubx_cfg_nav5 { + ubx_message_id_t id; + enum ubx_packet_state state; + struct { + uint16_t mask; + uint8_t dynModel; + uint8_t fixMode; + int32_t fixedAlt; + uint32_t fixedAltVar; + int8_t minElev; + uint8_t drLimit; + uint16_t pDop; + uint16_t tDop; + uint16_t pAcc; + uint16_t tAcc; + uint8_t staticHoldThresh; + uint8_t dgpsTimeOut; + uint32_t res2; + uint32_t res3; + uint32_t res4; + } payload; +}; +/** + * UBX CFG TP TimePulse Parameters + */ +__PACKED__ struct ubx_cfg_tp { + ubx_message_id_t id; + enum ubx_packet_state state; + struct { + uint32_t interval; + uint32_t length; + int8_t status; + uint8_t timeRef; + uint8_t flags; + uint8_t res; + int16_t antennaCableDelay; + int16_t rfGroupDelay; + int32_t userDelay; + } payload; +}; +/** + * UBX CFG TP5 TimePulse Parameters + */ +__PACKED__ struct ubx_cfg_tp5 { + ubx_message_id_t id; + enum ubx_packet_state state; + struct { + uint8_t tpIdx; + uint8_t res0; + uint16_t res1; + int16_t antCableDelay; + int16_t rfGroupDelay; + uint32_t freqPeriod; + uint32_t freqPeriodLoc; + uint32_t pulseLenRatio; + uint32_t pulseLenRatioLock; + int32_t userConfigDelay; + uint32_t flags; + } payload; +}; +/** + * UBX CFG PRT Polls the configuration for one I/O Port + */ +__PACKED__ struct ubx_cfg_prt { + ubx_message_id_t id; + enum ubx_packet_state state; + struct { + uint8_t portID; + uint8_t res0; + uint16_t txReady; + uint32_t mode; + uint32_t baudRate; + uint16_t inProtoMask; + uint16_t outProtoMask; + uint16_t flags; + uint16_t res3; + } payload; +}; + +/** + * UBX Dynamic Platform Model + */ +enum { + UBX_PLATFORM_MODEL_PORTABLE = 0, + UBX_PLATFORM_MODEL_STATIONARY = 2, + UBX_PLATFORM_MODEL_PEDESTRIAN = 3, + UBX_PLATFORM_MODEL_AUTOMOTIVE = 4, + UBX_PLATFORM_MODEL_SEA = 5, + UBX_PLATFORM_MODEL_AIRBORNE_1G = 6, + UBX_PLATFORM_MODEL_AIRBORNE_2G = 7, + UBX_PLATFORM_MODEL_AIRBORNE_4G = 8, +}; + +/** + * ============================================================================= + * UBX NAV Message Types ======================================================= + * ============================================================================= + */ + +/** + * UBX NAV POSLLH Geodetic Position Solution + */ +__PACKED__ struct ubx_nav_posllh { + ubx_message_id_t id; + enum ubx_packet_state state; + struct { + uint32_t iTOW; + int32_t lon; + int32_t lat; + int32_t height; + int32_t hMSL; + uint32_t hAcc; + uint32_t vAcc; + } payload; +}; +/** + * UBX NAV TIMEUTC + */ +__PACKED__ struct ubx_nav_timeutc { + ubx_message_id_t id; + enum ubx_packet_state state; + struct { + uint32_t iTOW; + uint32_t tAcc; + int32_t nano; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t min; + uint8_t sec; + uint8_t valid; + } payload; +}; +/** + * UBX NAV SOL Navigation Solution Information + */ +__PACKED__ struct ubx_nav_sol { + ubx_message_id_t id; + enum ubx_packet_state state; + struct { + uint32_t iTOW; + int32_t fTOW; + uint16_t week; + uint8_t gpsFix; + uint8_t flags; + int32_t ecefX; + int32_t ecefY; + int32_t ecefZ; + uint32_t pAcc; + int32_t ecefVX; + int32_t ecefVY; + int32_t ecefVZ; + uint32_t sAcc; + uint16_t pDOP; + uint8_t res1; + uint8_t numSV; + uint32_t res2; + } payload; +}; + +#endif /* UBX_MESSAGES_H */ diff --git a/firmware/src/gps.c b/firmware/src/gps.c index f810910..1283cb9 100644 --- a/firmware/src/gps.c +++ b/firmware/src/gps.c @@ -33,45 +33,14 @@ #include "sercom/sercom.h" #include "sercom/usart.h" #include "util/dbuffer.h" +#include "ubx_messages.h" #include "gps.h" /** * UBX Constants */ const uint16_t ubx_header = (0xB5 | (0x62 << 8)); -/** - * UBX Class Types - */ -enum { - UBX_NAV = 0x01, - UBX_RXM = 0x02, - UBX_INF = 0x04, - UBX_ACK = 0x05, - UBX_CFG = 0x06, - UBX_MON = 0x0A, - UBX_AID = 0x0B, - UBX_TIM = 0x0D, - UBX_ESF = 0x10, -}; -/** - * UBX NAV Message Types - */ -enum { - UBX_NAV_POSLLH = (UBX_NAV | (0x02 << 8)), - UBX_NAV_SOL = (UBX_NAV | (0x06 << 8)), - UBX_NAV_TIMEGPS = (UBX_NAV | (0x20 << 8)), - UBX_NAV_TIMEUTC = (UBX_NAV | (0x21 << 8)), -}; -/** - * UBX CFG Message Types - */ -enum { - UBX_CFG_PRT = (UBX_CFG | (0x00 << 8)), - UBX_CFG_TP = (UBX_CFG | (0x07 << 8)), - UBX_CFG_ANT = (UBX_CFG | (0x13 << 8)), - UBX_CFG_NAV5 = (UBX_CFG | (0x24 << 8)), - UBX_CFG_TP5 = (UBX_CFG | (0x31 << 8)), -}; + /** * UBX ACK Message Types */ @@ -80,8 +49,10 @@ enum { UBX_ACK_ACK = (UBX_ACK | (0x01 << 8)), }; +/** + * Internal buffers and things + */ #define UBX_BUFFER_LEN 0x80 - #define SFD1 0xB5 #define SFD2 0x62 @@ -94,104 +65,102 @@ int32_t ubx_index = SFD_WAITING; uint16_t ubx_payload_length = 0; uint8_t ubx_irq_buffer[UBX_BUFFER_LEN]; -#define _send_buffer(tx_data, length) \ - usart_write_buffer_wait(GPS_SERCOM, tx_data, length) -#define _get_buffer(rx_data, length) \ - usart_read_buffer_wait(GPS_SERCOM, rx_data, length) +/** + * UBX Messages + */ +volatile struct ubx_cfg_ant ubx_cfg_ant = { .id = (UBX_CFG | (0x07 << 8)) }; +volatile struct ubx_cfg_nav5 ubx_cfg_nav5 = { .id = (UBX_CFG | (0x24 << 8)) }; +volatile struct ubx_cfg_tp ubx_cfg_tp = { .id = (UBX_CFG | (0x07 << 8)) }; +volatile struct ubx_cfg_tp5 ubx_cfg_tp5 = { .id = (UBX_CFG | (0x31 << 8)) }; +volatile struct ubx_cfg_prt ubx_cfg_prt = { .id = (UBX_CFG | (0x00 << 8)) }; +volatile struct ubx_nav_posllh ubx_nav_posllh = { .id = (UBX_NAV | (0x02 << 8)) }; +volatile struct ubx_nav_timeutc ubx_nav_timeutc = { .id = (UBX_NAV | (0x21 << 8)) }; +volatile struct ubx_nav_sol ubx_nav_sol = { .id = (UBX_NAV | (0x06 << 8)) }; +/** + * UBX Message Type List + */ +volatile ubx_message_t* const ubx_messages[] = { + (ubx_message_t*)&ubx_cfg_ant, + (ubx_message_t*)&ubx_cfg_nav5, + (ubx_message_t*)&ubx_cfg_tp, + (ubx_message_t*)&ubx_cfg_tp5, + (ubx_message_t*)&ubx_cfg_prt, + (ubx_message_t*)&ubx_nav_posllh, + (ubx_message_t*)&ubx_nav_timeutc, + (ubx_message_t*)&ubx_nav_sol}; + + /** - * Flags for pending ubx pakcets + * Platform specific handlers */ -enum ubx_packet_state { - UBX_PACKET_WAITING, - UBX_PACKET_ACK, - UBX_PACKET_NACK, -}; -enum ubx_packet_state _ubx_cfg_tp_state; -enum ubx_packet_state _ubx_cfg_tp5_state; +#define _send_buffer(tx_data, length) \ + usart_write_buffer_wait(GPS_SERCOM, tx_data, length) +#define _error_handler(error_type) \ + /* TODO */ + + + + + + + /** * Processes UBX ack/nack packets */ -void ubx_process_ack(uint16_t message, enum ubx_packet_state state) +void ubx_process_ack(ubx_message_id_t message_id, enum ubx_packet_state state) { - switch (message) { - case UBX_CFG_TP: - _ubx_cfg_tp_state = state; - break; - case UBX_CFG_TP5: - _ubx_cfg_tp5_state = state; - break; - default: - break; + for (uint32_t i = 0; i < sizeof(ubx_messages)/sizeof(ubx_message_t*); i++) { + if (message_id == ubx_messages[i]->id) { /* Match! */ + /* Set the message state */ + ubx_messages[i]->state = state; + } } } /** * Macro for the function below */ #define UBX_POPULATE_STRUCT(ubx_type) \ - if (payload_length == sizeof(ubx_type)) { \ - memcpy((void*)&ubx_type, frame + 4, payload_length); \ - } + /** * Process a single ubx frame. Runs in the IRQ so should be short and sweet. */ void ubx_process_frame(uint8_t* frame) { uint16_t* frame16 = (uint16_t*)frame; + uint16_t message_id = frame16[0]; uint16_t payload_length = frame16[1]; /* Checksum.. */ - switch (frame[0]) { /* Switch by Class */ - case UBX_NAV: - switch (frame16[0]) { - case UBX_NAV_SOL: /* Navigation Solution Information */ - UBX_POPULATE_STRUCT(ubx_nav_sol); - break; - case UBX_NAV_TIMEUTC: /* UTC Time Solution */ - UBX_POPULATE_STRUCT(ubx_nav_timeutc); - break; - case UBX_NAV_POSLLH: /* Geodetic Position Solution */ - UBX_POPULATE_STRUCT(ubx_nav_posllh); - break; + /** Parse the message ID */ + if (message_id == UBX_ACK_ACK) { + /* Ack */ + ubx_process_ack(frame16[2], UBX_PACKET_ACK); + + } else if (message_id == UBX_ACK_NACK) { + /* Nack */ + ubx_process_ack(frame16[2], UBX_PACKET_NACK); + + } else { + /** Otherwise it could be a message frame, search for a type */ + for (uint32_t i = 0; i < sizeof(ubx_messages)/sizeof(ubx_message_t*); i++) { + if (message_id == ubx_messages[i]->id) { // Match! + /* Populate struct */ + memcpy((void*)(ubx_messages[i]+1), frame + 4, payload_length); + + return; } - break; - case UBX_CFG: - switch (frame16[0]) { - case UBX_CFG_NAV5: /* Navigation Engine Settings */ - UBX_POPULATE_STRUCT(ubx_cfg_nav5); - break; - case UBX_CFG_ANT: /* Antenna Control Settings */ - UBX_POPULATE_STRUCT(ubx_cfg_ant); - break; - case UBX_CFG_TP: /* TimePulse Parameters */ - UBX_POPULATE_STRUCT(ubx_cfg_tp); - break; - case UBX_CFG_TP5: /* TimePulse Parameters */ - UBX_POPULATE_STRUCT(ubx_cfg_tp5); - break; - } - break; - case UBX_ACK: - if (payload_length == 2) { /* All ACK packets should have a payload len of 2 */ - switch (frame16[0]) { - case UBX_ACK_ACK: - ubx_process_ack(frame16[2], UBX_PACKET_ACK); - break; - case UBX_ACK_NACK: - ubx_process_ack(frame16[2], UBX_PACKET_NACK); - break; - } - } - break; - default: - break; + } } + + /* Unknown frame */ + } /** - * Rx Callback. Processes a stream of + * Rx Callback. Processes a stream of usart bytes */ void gps_rx_callback(SercomUsart* const hw, uint16_t data) { @@ -259,26 +228,32 @@ uint16_t _ubx_checksum(uint8_t* data, uint8_t len) /** * Sends a standard UBX message */ -void _ubx_send_message(uint16_t message, uint8_t* payload, uint16_t length) +void _ubx_send_message(ubx_message_t* message, uint8_t* payload, uint16_t length) { uint8_t ubx[UBX_BUFFER_LEN]; uint8_t* ubx_buffer = ubx; /* Copy little endian */ memcpy(ubx_buffer, &ubx_header, 2); ubx_buffer += 2; /* Header */ - memcpy(ubx_buffer, &message, 2); ubx_buffer += 2; /* Message Type */ + memcpy(ubx_buffer, &message->id, 2); ubx_buffer += 2; /* Message Type */ memcpy(ubx_buffer, &length, 2); ubx_buffer += 2; /* Length */ - memcpy(ubx_buffer, payload, length); ubx_buffer += length; /* Payload*/ + memcpy(ubx_buffer, payload, length); ubx_buffer += length; /* Payload */ uint16_t checksum = _ubx_checksum(ubx + 2, length + 4); memcpy(ubx_buffer, &checksum, 2); ubx_buffer += 2; /* Checksum */ _send_buffer(ubx, length + 8); } /** - * Sends a UBX Poll Request + * Polls the GPS for packets */ -void _ubx_poll(uint16_t message) { +void _ubx_poll(ubx_message_t* message) { + /* Clear the packet state */ + message->state = UBX_PACKET_WAITING; + _ubx_send_message(message, NULL, 0); + + /* Wait for acknoledge */ + while (message->state == UBX_PACKET_WAITING); } /** @@ -286,36 +261,65 @@ void _ubx_poll(uint16_t message) { */ void gps_disable_nmea(void) { - for (int i = 0; i < 1000*100; i++); + ubx_cfg_prt.payload.portID = 1; + ubx_cfg_prt.payload.res0 = 0; + ubx_cfg_prt.payload.txReady = 0; + ubx_cfg_prt.payload.mode = 0x08D0; /* 8 bit, No Parity */ + ubx_cfg_prt.payload.baudRate = 9600; + ubx_cfg_prt.payload.inProtoMask = 0x7; /* UBX */ + ubx_cfg_prt.payload.outProtoMask = 0x1; /* UBX */ + ubx_cfg_prt.payload.flags = 0; - uint8_t setNMEAoff[] = { - 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x80, 0x25, - 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - _ubx_send_message(UBX_CFG_PRT, setNMEAoff, sizeof(setNMEAoff)/sizeof(uint8_t)); + _ubx_send_message((ubx_message_t*)&ubx_cfg_prt, + (uint8_t*)&ubx_cfg_prt.payload, + sizeof(ubx_cfg_prt.payload)); for (int i = 0; i < 1000*100; i++); } /** - * Check the navigation status to determine the quality of the - * fix currently held by the receiver with a NAV-STATUS message. + * Sends messages to the GPS to update the messages we want */ -void gps_check_lock() +void gps_update() { - /* Send the poll request */ - _ubx_poll(UBX_NAV_SOL); + _ubx_send_message((ubx_message_t*)&ubx_nav_posllh, NULL, 0); + _ubx_send_message((ubx_message_t*)&ubx_nav_sol, NULL, 0); + _ubx_send_message((ubx_message_t*)&ubx_nav_timeutc, NULL, 0); +} +/** + * Return the latest received messages + */ +struct ubx_nav_posllh gps_get_nav_posllh() +{ + return ubx_nav_posllh; +} +struct ubx_nav_sol gps_get_nav_sol() +{ + return ubx_nav_sol; +} +struct ubx_nav_timeutc gps_get_nav_timeutc() +{ + return ubx_nav_timeutc; } /** * Verify that the uBlox 6 GPS receiver is set to the <1g airborne * navigaion mode. */ -void gps_check_nav(void) +void gps_set_platform_model(void) { /* Send the poll request */ - _ubx_poll(UBX_CFG_NAV5); + _ubx_poll((ubx_message_t*)&ubx_cfg_nav5); + + /* If we need to update */ + if (ubx_cfg_nav5.payload.dynModel != GPS_PLATFORM_MODEL) { + /* Update */ + ubx_cfg_nav5.payload.dynModel = GPS_PLATFORM_MODEL; + /* Send */ + _ubx_send_message((ubx_message_t*)&ubx_cfg_nav5, + (uint8_t*)&ubx_cfg_nav5.payload, + sizeof(ubx_cfg_nav5.payload)); + } } /** @@ -323,60 +327,42 @@ void gps_check_nav(void) */ void gps_set_timepulse(void) { - /* Clear the packet state */ - _ubx_cfg_tp_state = UBX_PACKET_WAITING; - /* Send the Request */ - _ubx_poll(UBX_CFG_TP); + _ubx_poll((ubx_message_t*)&ubx_cfg_tp); /* Define the settings we want */ - struct ubx_cfg_tp timepulse; - memset(&timepulse, 0, sizeof(ubx_cfg_tp)); - timepulse.interval = 2; /* 2µS */ - timepulse.length = 1; /* 1µS */ - timepulse.status = 1; /* On, Positive */ - timepulse.timeRef = 1; /* Align GPS time */ - timepulse.flags = 0x1; /* Run outside lock */ - timepulse.antennaCableDelay = 50; /* 50 nS */ + ubx_cfg_tp.payload.interval = 2; /* 2µS */ + ubx_cfg_tp.payload.length = 1; /* 1µS */ + ubx_cfg_tp.payload.status = 1; /* On, Positive */ + ubx_cfg_tp.payload.timeRef = 1; /* Align GPS time */ + ubx_cfg_tp.payload.flags = 0x1; /* Run outside lock */ + ubx_cfg_tp.payload.antennaCableDelay = 50; /* 50 nS */ - /* Wait for acknoledge */ - while (_ubx_cfg_tp_state == UBX_PACKET_WAITING); - - /* Compare with current settings */ - if (memcmp((void*)&ubx_cfg_tp, &timepulse, sizeof(ubx_cfg_tp)) != 0) { - /* Write the new settings */ - _ubx_send_message(UBX_CFG_TP, (uint8_t*)&timepulse, sizeof(ubx_cfg_tp)); - } + /* Write the new settings */ + _ubx_send_message((ubx_message_t*)&ubx_cfg_tp, + (uint8_t*)&ubx_cfg_tp.payload, + sizeof(ubx_cfg_tp.payload)); } /** * Set the GPS timepulse settings using the CFG_TP5 message */ void gps_set_timepulse_five(uint32_t frequency) { - /* Clear the packet state */ - _ubx_cfg_tp5_state = UBX_PACKET_WAITING; - /* Send the Request */ - _ubx_poll(UBX_CFG_TP5); + _ubx_poll((ubx_message_t*)&ubx_cfg_tp5); /* Define the settings we want */ - struct ubx_cfg_tp5 timepulse5; - memset(&timepulse5, 0, sizeof(ubx_cfg_tp5)); - timepulse5.tpIdx = 0; - timepulse5.antCableDelay = 50; /* 50 nS */ + ubx_cfg_tp5.payload.tpIdx = 0; + ubx_cfg_tp5.payload.antCableDelay = 50; /* 50 nS */ /* GPS time, duty cyle, frequency, lock to GPS, active */ - timepulse5.flags = 0x80 | 0x8 | 0x3; - timepulse5.freqPeriod = frequency; - timepulse5.pulseLenRatio = 0x80000000; /* 50 % duty cycle*/ + ubx_cfg_tp5.payload.flags = 0x80 | 0x8 | 0x3; + ubx_cfg_tp5.payload.freqPeriod = frequency; + ubx_cfg_tp5.payload.pulseLenRatio = 0x80000000; /* 50 % duty cycle*/ - /* Wait for acknoledge */ - while (_ubx_cfg_tp5_state == UBX_PACKET_WAITING); - - /* Compare with current settings */ - if (memcmp((void*)&ubx_cfg_tp5, &timepulse5, sizeof(ubx_cfg_tp5)) != 0) { - /* Write the new settings */ - _ubx_send_message(UBX_CFG_TP5, (uint8_t*)&timepulse5, sizeof(ubx_cfg_tp5)); - } + /* Write the new settings */ + _ubx_send_message((ubx_message_t*)&ubx_cfg_tp5, + (uint8_t*)&ubx_cfg_tp5.payload, + sizeof(ubx_cfg_tp5.payload)); } /** @@ -420,13 +406,8 @@ void gps_init(void) /* Incoming ubx messages are handled in an irq */ usart_register_rx_callback(GPS_SERCOM, gps_rx_callback, 0); - /* Fill some configuration structures */ - - gps_check_lock(); - _ubx_poll(UBX_CFG_ANT); - - /* */ - gps_check_nav(); + /* Set the platform model */ + gps_set_platform_model(); /* Set the timepulse */ gps_set_timepulse_five(GPS_TIMEPULSE_FREQ); @@ -473,4 +454,3 @@ void usart_loopback_test(void) semihost_printf("Rx'ed: 0x%02x\n", data); } - diff --git a/firmware/src/main.c b/firmware/src/main.c index 38b6be0..5e0e951 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -31,11 +31,14 @@ #include "system/system.h" #include "sercom/usart.h" #include "system/port.h" +#include "tc/tc_driver.h" #include "gps.h" #include "timepulse.h" //#include "si406x.h" #include "si4060.h" #include "spi_bitbang.h" +#include "rtty.h" +#include "system/interrupt.h" void si4060_hw_init(void) { @@ -58,23 +61,65 @@ void si4060_hw_init(void) /* Put the SEL pin in reset */ _si406x_cs_disable(); + /* Configure the serial port */ + spi_bitbang_init(SI406X_SERCOM_MOSI_PIN, + SI406X_SERCOM_MISO_PIN, + SI406X_SERCOM_SCK_PIN); +} +void si4060_gpio_init() +{ /* Configure the GPIO and IRQ pins */ port_pin_set_config(SI406X_GPIO0_PIN, PORT_PIN_DIR_OUTPUT, /* Direction */ PORT_PIN_PULL_NONE, /* Pull */ false); /* Powersave */ port_pin_set_output_level(SI406X_GPIO0_PIN, 0); - - /* Configure the serial port */ - spi_bitbang_init(SI406X_SERCOM_MOSI_PIN, - SI406X_SERCOM_MISO_PIN, - SI406X_SERCOM_SCK_PIN); + /* Configure the GPIO and IRQ pins */ + port_pin_set_config(SI406X_GPIO1_PIN, + PORT_PIN_DIR_OUTPUT, /* Direction */ + PORT_PIN_PULL_NONE, /* Pull */ + false); /* Powersave */ + port_pin_set_output_level(SI406X_GPIO1_PIN, 0); } +void set_timer(uint32_t time) +{ + bool capture_channel_enables[] = {false, false}; + uint32_t compare_channel_values[] = {time, 0x0000}; + + tc_init(TC2, + GCLK_GENERATOR_0, + TC_COUNTER_SIZE_32BIT, + TC_CLOCK_PRESCALER_DIV1, + TC_WAVE_GENERATION_NORMAL_FREQ, + TC_RELOAD_ACTION_GCLK, + TC_COUNT_DIRECTION_UP, + TC_WAVEFORM_INVERT_OUTPUT_NONE, + false, /* Oneshot = false */ + false, /* Run in standby = false */ + 0x0000, /* Initial value */ + time+1, /* Top value */ + capture_channel_enables, /* Capture Channel Enables */ + compare_channel_values); /* Compare Channels Values */ + + struct tc_events ev; + memset(&ev, 0, sizeof(ev)); + ev.generate_event_on_compare_channel[0] = true; + ev.event_action = TC_EVENT_ACTION_RETRIGGER; + + tc_enable_events(TC2, &ev); + + irq_register_handler(TC2_IRQn, 3); + + tc_enable(TC2); + tc_start_counter(TC2); +} + + int main(void) { - /* Clock up to 28MHz with 1 wait state */ - system_flash_set_waitstates(1); + /* Clock up to 14MHz with 0 wait states */ + system_flash_set_waitstates(SYSTEM_WAIT_STATE_1_8V_14MHZ); /* Up the clock rate to 4MHz */ system_clock_source_osc8m_set_config(SYSTEM_OSC8M_DIV_2, /* Prescaler */ @@ -87,39 +132,19 @@ int main(void) /* Get the current CPU Clock */ SystemCoreClock = system_cpu_clock_get_hz(); - /* Set LED0 as output */ - //PORTA.DIRSET.reg = (1 << SI406X_HF_CLK_PIN); - - /* Configure the SysTick for cpu/1000 output*//*for 50ms interrupts */ - //SysTick_Config(500); //SystemCoreClock / 20); - /* Configure Sleep Mode */ system_set_sleepmode(SYSTEM_SLEEPMODE_IDLE_0); //TODO: system_set_sleepmode(SYSTEM_SLEEPMODE_STANDBY); semihost_printf("Hello World %fHz\n", RF_FREQ_HZ); - /* Initialise GPS */ gps_init(); /* Wait for GPS timepulse to stabilise */ for (int i = 0; i < 1000*100; i++); - - /* For the moment output GCLK_MAIN / 2 on HF CLK */ - switch_gclk_main_to_timepulse(); - /* Wait for GCLK to stabilise */ - for (int i = 0; i < 1000*100; i++); - - //half_glck_main_on_hf_clk(); - timepulse_init(); - /* Wait for HF CLK to stabilise */ - for (int i = 0; i < 1000*100; i++); - - semihost_printf("GCLK_MAIN = %d\n", gclk_main_frequency()); - - /* Drop the CPU clock to 1.5Mhz */ - //system_cpu_clock_set_divider(SYSTEM_MAIN_CLOCK_DIV_16); + /* Configure the SysTick for 50Hz triggering */ + SysTick_Config(SystemCoreClock / 50); /* Initialise Si4060 */ si4060_hw_init(); @@ -133,30 +158,42 @@ int main(void) while(1); } - si4060_power_up(); - si4060_setup(MOD_TYPE_CW); - si4060_start_tx(0); + /* si4060_power_up(); */ + /* si4060_setup(MOD_TYPE_2FSK); */ - uint32_t ll = 0; + /* si4060_gpio_init(); */ + /* si4060_start_tx(0); */ + + /* Set the watchdog timer. On 32kHz ULP internal clock */ + /* wdt_set_config(true, /\* Lock WDT *\/ */ + /* true, /\* Enable WDT *\/ */ + /* GCLK_GENERATOR_4, /\* Clock Source *\/ */ + /* WDT_PERIOD_16384CLK, /\* Timeout Period *\/ */ + /* WDT_PERIOD_NONE, /\* Window Period *\/ */ + /* WDT_PERIOD_NONE); /\* Early Warning Period *\/ */ while (1) { - semihost_printf("State is %d %d\n", si4060_get_state(), ll++); - si4060_get_freq(); - for (int i = 0; i < 1000*10; i++); - port_pin_set_output_level(SI406X_GPIO0_PIN, 0); - //si4060_start_tx(0); - for (int i = 0; i < 1000*10; i++); - port_pin_set_output_level(SI406X_GPIO0_PIN, 1); + /* Send the last packet */ + while (rtty_active()); + /* Send requests to the gps */ + gps_update(); + /* Wait between frames */ + for (int i = 0; i < 100*1000; i++); + /* Set the next packet */ + set_telemetry_string(); //system_sleep(); } } +/** + * Called at 50Hz + */ void SysTick_Handler(void) { - /* Toggle LED0 */ - //PORTA.OUTTGL.reg = (1 << SI406X_HF_CLK_PIN); + /* Output RTTY */ + rtty_tick(); } diff --git a/firmware/src/rtty.c b/firmware/src/rtty.c new file mode 100644 index 0000000..f452dc4 --- /dev/null +++ b/firmware/src/rtty.c @@ -0,0 +1,139 @@ +/* + * Bit-bangs RTTY + * Copyright (C) 2014 Richard Meadows + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "samd20.h" +#include "hw_config.h" +#include "system/port.h" + +/** + * Interface to the physical world. + */ +#define RTTY_ACTIVATE() +#define RTTY_DEACTIVATE() +#define RTTY_SET(b) port_pin_set_output_level(SI406X_GPIO1_PIN, b); +#define RTTY_NEXT() + +/** + * Formatting 8N2 + */ +#define ASCII_BITS 8 +#define BITS_PER_CHAR 11 + +/** + * Output String + */ +#define RTTY_STRING_MAX 0x200 + +/** + * Where we currently are in the rtty output byte + * + * 0 = Start Bit + * 1-8 = Data Bit + * 10 = Stop Bit + * 11 = Stop Bit + */ +uint8_t rtty_phase; +/** + * Where we are in the current output string + */ +uint32_t rtty_index; + +/** + * Details of the string that is currently being output + */ +char rtty_string[RTTY_STRING_MAX]; +uint32_t rtty_string_length = 0; + +/** + * Returns 1 if we're currently outputting. + */ +int rtty_active(void) { + return (rtty_string_length > 0); +} + +/** + * Sets an output string. + * + * Returns 0 on success, 1 if a string is already active or 2 if the + * specified string was too long. + */ +int rtty_set_string(char* string, uint32_t length) { + if (length > RTTY_STRING_MAX) return 2; // To long + + if (!rtty_active()) { + // Copy + memcpy(rtty_string, string, length); + rtty_string_length = length; + // Initialise + rtty_index = 0; + rtty_phase = 0; + + return 0; // Success + } else { + return 1; // Already active + } +} + +/** + * Called at the baud rate, outputs bits of rtty + */ +void rtty_tick(void) { + if (rtty_active()) { + RTTY_ACTIVATE(); + + if (rtty_phase == 0) { // Start + // Low + //RTTY_SET(0); + port_pin_set_output_level(SI406X_GPIO1_PIN, 0); + } else if (rtty_phase < ASCII_BITS + 1) { + // Data + if ((rtty_string[rtty_index] >> (rtty_phase - 1)) & 1) { + //RTTY_SET(1); + port_pin_set_output_level(SI406X_GPIO1_PIN, 1); + } else { + //RTTY_SET(0); + port_pin_set_output_level(SI406X_GPIO1_PIN, 0); + } + } else if (rtty_phase < BITS_PER_CHAR) { // Stop + // High + //RTTY_SET(1); + port_pin_set_output_level(SI406X_GPIO1_PIN, 1); + } + + rtty_phase++; + + if (rtty_phase >= BITS_PER_CHAR) { // Next character + rtty_phase = 0; rtty_index++; RTTY_NEXT(); + + if (rtty_index >= rtty_string_length) { // All done, deactivate + rtty_string_length = 0; // Deactivate + } + } + } else { +// RTTY_DEACTIVATE(); + } +} + diff --git a/firmware/src/si4060.c b/firmware/src/si4060.c index 4a42867..854115f 100644 --- a/firmware/src/si4060.c +++ b/firmware/src/si4060.c @@ -88,7 +88,7 @@ void si4060_power_up(void) { spi_select(); spi_write(CMD_POWER_UP); spi_write(FUNC); - spi_write(0x01);/* TCXO used */ + spi_write(0x00);/* TCXO not used */ spi_write((uint8_t) (XO_FREQ >> 24)); spi_write((uint8_t) (XO_FREQ >> 16)); spi_write((uint8_t) (XO_FREQ >> 8)); @@ -393,8 +393,8 @@ void si4060_setup(uint8_t mod_type) { GLOBAL_CONFIG, GLOBAL_RESERVED | POWER_MODE_HIGH_PERF | SEQUENCER_MODE_FAST); /* set up GPIOs */ - si4060_gpio_pin_cfg(GPIO_MODE_DONOTHING, - GPIO_MODE_DONOTHING, + si4060_gpio_pin_cfg(GPIO_MODE_INPUT, + PULL_CTL + GPIO_MODE_INPUT, GPIO_MODE_DONOTHING, PULL_CTL + GPIO_MODE_INPUT, DRV_STRENGTH_HIGH); @@ -409,7 +409,7 @@ void si4060_setup(uint8_t mod_type) { /* use 2FSK from async GPIO0 */ si4060_set_property_8(PROP_MODEM, MODEM_MOD_TYPE, - (mod_type & 0x07) | MOD_SOURCE_DIRECT | MOD_GPIO_0 | MOD_DIRECT_MODE_ASYNC); + (mod_type & 0x07) | MOD_SOURCE_DIRECT | MOD_GPIO_1 | MOD_DIRECT_MODE_ASYNC); /* setup frequency deviation */ si4060_set_property_24(PROP_MODEM, MODEM_FREQ_DEV, diff --git a/firmware/src/tc/tc_driver.c b/firmware/src/tc/tc_driver.c index 3dc0cb6..7b3655f 100644 --- a/firmware/src/tc/tc_driver.c +++ b/firmware/src/tc/tc_driver.c @@ -506,7 +506,7 @@ void tc_set_top_value(Tc* const hw, * \param[in] events Struct containing flags of events to enable */ void tc_enable_events(Tc* const hw, - struct tc_events *const events) + struct tc_events *const events) { /* Sanity check arguments */ assert(hw); @@ -679,7 +679,7 @@ enum tc_status_t tc_init(Tc* const hw, /* Write Start Value */ tc_set_count_value(hw, value); - /* Write Top Value - Only cool for 8 bit!*/ + /* Write Top Value */ tc_set_top_value(hw, top_value); /* Write Compare Values */ diff --git a/firmware/src/telemetry.c b/firmware/src/telemetry.c new file mode 100644 index 0000000..a00dc33 --- /dev/null +++ b/firmware/src/telemetry.c @@ -0,0 +1,73 @@ +/* + * Telemetry strings and formatting + * Copyright (C) 2014 Richard Meadows + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "samd20.h" +#include "semihosting.h" +#include "gps.h" +#include "ubx_messages.h" + +#define SEMIHOST_LOG + +char telemetry_string[0x200]; + +/** + * + */ +void set_telemetry_string(void) +{ + /* Time */ + struct ubx_nav_timeutc time = gps_get_nav_timeutc(); + uint8_t hours = time.payload.hour; + uint8_t minutes = time.payload.min; + uint8_t seconds = time.payload.sec; + + /* GPS Position */ + struct ubx_nav_posllh pos = gps_get_nav_posllh(); + double lat_fmt = (double)pos.payload.lat / 10000000.0; + double lon_fmt = (double)pos.payload.lon / 10000000.0; + uint32_t altitude = pos.payload.height / 1000; + + /* GPS Status */ + struct ubx_nav_sol sol = gps_get_nav_sol(); + uint8_t fix_type = sol.payload.gpsFix; + uint8_t satillite_count = sol.payload.numSV; + + /* Analogue */ + + +#ifdef SEMIHOST_LOG + + semihost_printf("%02.7f,%03.7f,%ld", lat_fmt, lon_fmt, altitude); + semihost_printf("Fix: %d Sats: %d\n", fix_type, satillite_count); + semihost_printf("%02u:%02u:%02u\n", hours, minutes, seconds); + + +#else + /* sprintf */ + sprintf(telemetry_string, "$$emf,%02.7f,%03.7f,%ld,%u", + lat_fmt, lon_fmt, altitude, ubx_nav_sol.numSV); + + rtty_set_string(telemetry_string, strlen(telemetry_string)); +#endif +}