diff options
-rw-r--r-- | src/fsm/common.h | 7 | ||||
-rw-r--r-- | src/fsm/gps.c | 114 | ||||
-rw-r--r-- | src/fsm/main.c | 2 | ||||
-rw-r--r-- | src/fsm/minmea.c | 572 | ||||
-rw-r--r-- | src/fsm/minmea.h | 234 | ||||
-rw-r--r-- | src/fsm/ubx.c | 157 | ||||
-rw-r--r-- | src/fsm/ubx.h | 193 | ||||
-rw-r--r-- | src/fsm/usart.c | 155 | ||||
-rw-r--r-- | src/fsm/usart.h | 42 |
9 files changed, 1039 insertions, 437 deletions
diff --git a/src/fsm/common.h b/src/fsm/common.h index 53da672..b33f1f8 100644 --- a/src/fsm/common.h +++ b/src/fsm/common.h @@ -41,9 +41,10 @@ uint64_t timestamp_now(void); int random_bool(void); // Fault handling mechanism -#define FAULT_SOURCE_MAIN 1 -#define FAULT_SOURCE_GPS 2 -#define FAULT_SOURCE_I2C 3 +#define FAULT_SOURCE_MAIN 1 +#define FAULT_SOURCE_GPS 2 +#define FAULT_SOURCE_I2C 3 +#define FAULT_SOURCE_USART 4 void trigger_fault(int source); #endif // _COMMON_H_ diff --git a/src/fsm/gps.c b/src/fsm/gps.c index c0eab66..1908fb3 100644 --- a/src/fsm/gps.c +++ b/src/fsm/gps.c @@ -30,8 +30,8 @@ #include "semphr.h" #include "common.h" #include "gps.h" -#include "i2c.h" -#include "ubx.h" +#include "usart.h" +#include "minmea.h" TickType_t gps_timeutc_last_updated = 0; @@ -44,29 +44,7 @@ static int gps_fix = 0; static void gps_task(void *pvParameters); -// Callback functions for UBX parser -static void gps_nav_sol(ubx_nav_sol_t *sol) -{ - gps_fix_last_updated = xTaskGetTickCount(); - gps_fix = sol->GPSfix == GPSFIX_3D; -} - -static void gps_tim_tm2(ubx_tim_tm2_t *posllh) {} - SemaphoreHandle_t timeutc_semaphore; -static void gps_nav_timeutc(ubx_nav_timeutc_t *timeutc) -{ - xSemaphoreTake(timeutc_semaphore, portMAX_DELAY); - gps_timeutc_last_updated = xTaskGetTickCount(); - gps_timeutc.year = timeutc->year; - gps_timeutc.month = timeutc->month; - gps_timeutc.day = timeutc->day; - gps_timeutc.hour = timeutc->hour; - gps_timeutc.min = timeutc->min; - gps_timeutc.sec = timeutc->sec; - gps_timeutc.valid = timeutc->valid; - xSemaphoreGive(timeutc_semaphore); -} // Get current time from GPS void gps_utctime(struct gps_time_s *timeutc) @@ -87,71 +65,41 @@ void gps_utctime(struct gps_time_s *timeutc) xSemaphoreGive(timeutc_semaphore); } - -const ubx_callbacks_t gps_ubx_cb = { - gps_nav_sol, - gps_nav_timeutc, - gps_tim_tm2 -}; - - -#define RXBUF_LEN 128 -static uint8_t rxbuf[RXBUF_LEN]; -static uint8_t gps_init_messages[] = { - UBX_ENABLE_NAV_SOL, - UBX_ENABLE_NAV_TIMEUTC, - UBX_ENABLE_TIM_TM2 }; +#define RXBUF_LEN MAX_NMEA_SENTENCE_LEN +static char rxbuf[RXBUF_LEN]; static void gps_task(void *pvParameters) { // Periodically reinit the GPS - const TickType_t init_timeout = 10000ul / portTICK_PERIOD_MS; - - const uint8_t address = 0xFD; - int must_init_gps = 1; - - TickType_t time_last_init = xTaskGetTickCount(); - while (1) { taskYIELD(); - if (time_last_init + init_timeout >= xTaskGetTickCount()) { - must_init_gps = 1; - } - - i2c_transaction_start(); - - if (must_init_gps) { - i2c_write(GPS_I2C_ADDR, - gps_init_messages, - sizeof(gps_init_messages)); - - must_init_gps = 0; - } - - int bytes_read = i2c_read_from(GPS_I2C_ADDR, address, rxbuf, 2); - - if (bytes_read != 2) { - i2c_transaction_end(); - continue; - } - - uint16_t bytes_available = (rxbuf[0] << 8) | rxbuf[1]; - - if (bytes_available) { - if (bytes_available > RXBUF_LEN) { - bytes_available = RXBUF_LEN; + int success = usart_get_nmea_sentence(rxbuf); + + if (success) { + const int strict = 1; + switch (minmea_sentence_id(rxbuf, strict)) { + case MINMEA_SENTENCE_RMC: + { + struct minmea_sentence_rmc frame; + if (minmea_parse_rmc(&frame, rxbuf)) { + xSemaphoreTake(timeutc_semaphore, portMAX_DELAY); + gps_timeutc_last_updated = xTaskGetTickCount(); + gps_timeutc.year = frame.date.year; + gps_timeutc.month = frame.date.month; + gps_timeutc.day = frame.date.day; + gps_timeutc.hour = frame.time.hours; + gps_timeutc.min = frame.time.minutes; + gps_timeutc.sec = frame.time.seconds; + gps_timeutc.valid = frame.valid; + gps_fix = frame.valid; + gps_fix_last_updated = xTaskGetTickCount(); + xSemaphoreGive(timeutc_semaphore); + } + } break; + default: + break; } - - bytes_read = i2c_read(GPS_I2C_ADDR, rxbuf, bytes_available); - i2c_transaction_end(); - - for (int i = 0; i < bytes_read; i++) { - ubx_parse(rxbuf[i]); - } - } - else { - i2c_transaction_end(); } } } @@ -160,7 +108,7 @@ void gps_init() { gps_timeutc.valid = 0; - ubx_register(&gps_ubx_cb); + usart_init(); timeutc_semaphore = xSemaphoreCreateBinary(); @@ -184,7 +132,7 @@ void gps_init() int gps_locked() { if (gps_fix_last_updated + gps_data_validity_timeout < xTaskGetTickCount()) { - return (gps_fix == GPSFIX_3D) ? 1 : 0; + return gps_fix; } else { return 0; diff --git a/src/fsm/main.c b/src/fsm/main.c index 339c1ed..655ab45 100644 --- a/src/fsm/main.c +++ b/src/fsm/main.c @@ -144,7 +144,7 @@ static void launcher_task(void *pvParameters) } InitializeAudio(Audio16000HzSettings); - SetAudioVolume(164); + SetAudioVolume(210); PlayAudioWithCallback(audio_callback, NULL); diff --git a/src/fsm/minmea.c b/src/fsm/minmea.c new file mode 100644 index 0000000..fc6aa43 --- /dev/null +++ b/src/fsm/minmea.c @@ -0,0 +1,572 @@ +/* + * Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com> + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +// We are lacking timegm, but we only handle UTC and use mktime instead. See +// https://github.com/cloudyourcar/minmea-t/ README.md +#define timegm mktime + +#include "minmea.h" + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdarg.h> + +#define boolstr(s) ((s) ? "true" : "false") + +static int hex2int(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return -1; +} + +uint8_t minmea_checksum(const char *sentence) +{ + // Support senteces with or without the starting dollar sign. + if (*sentence == '$') + sentence++; + + uint8_t checksum = 0x00; + + // The optional checksum is an XOR of all bytes between "$" and "*". + while (*sentence && *sentence != '*') + checksum ^= *sentence++; + + return checksum; +} + +bool minmea_check(const char *sentence, bool strict) +{ + uint8_t checksum = 0x00; + + // Sequence length is limited. + if (strlen(sentence) > MINMEA_MAX_LENGTH + 3) + return false; + + // A valid sentence starts with "$". + if (*sentence++ != '$') + return false; + + // The optional checksum is an XOR of all bytes between "$" and "*". + while (*sentence && *sentence != '*' && isprint((unsigned char) *sentence)) + checksum ^= *sentence++; + + // If checksum is present... + if (*sentence == '*') { + // Extract checksum. + sentence++; + int upper = hex2int(*sentence++); + if (upper == -1) + return false; + int lower = hex2int(*sentence++); + if (lower == -1) + return false; + int expected = upper << 4 | lower; + + // Check for checksum mismatch. + if (checksum != expected) + return false; + } else if (strict) { + // Discard non-checksummed frames in strict mode. + return false; + } + + // The only stuff allowed at this point is a newline. + if (*sentence && strcmp(sentence, "\n") && strcmp(sentence, "\r\n")) + return false; + + return true; +} + +static inline bool minmea_isfield(char c) { + return isprint((unsigned char) c) && c != ',' && c != '*'; +} + +bool minmea_scan(const char *sentence, const char *format, ...) +{ + bool result = false; + bool optional = false; + va_list ap; + va_start(ap, format); + + const char *field = sentence; +#define next_field() \ + do { \ + /* Progress to the next field. */ \ + while (minmea_isfield(*sentence)) \ + sentence++; \ + /* Make sure there is a field there. */ \ + if (*sentence == ',') { \ + sentence++; \ + field = sentence; \ + } else { \ + field = NULL; \ + } \ + } while (0) + + while (*format) { + char type = *format++; + + if (type == ';') { + // All further fields are optional. + optional = true; + continue; + } + + if (!field && !optional) { + // Field requested but we ran out if input. Bail out. + goto parse_error; + } + + switch (type) { + case 'c': { // Single character field (char). + char value = '\0'; + + if (field && minmea_isfield(*field)) + value = *field; + + *va_arg(ap, char *) = value; + } break; + + case 'd': { // Single character direction field (int). + int value = 0; + + if (field && minmea_isfield(*field)) { + switch (*field) { + case 'N': + case 'E': + value = 1; + break; + case 'S': + case 'W': + value = -1; + break; + default: + goto parse_error; + } + } + + *va_arg(ap, int *) = value; + } break; + + case 'f': { // Fractional value with scale (struct minmea_float). + int sign = 0; + int_least32_t value = -1; + int_least32_t scale = 0; + + if (field) { + while (minmea_isfield(*field)) { + if (*field == '+' && !sign && value == -1) { + sign = 1; + } else if (*field == '-' && !sign && value == -1) { + sign = -1; + } else if (isdigit((unsigned char) *field)) { + int digit = *field - '0'; + if (value == -1) + value = 0; + if (value > (INT_LEAST32_MAX-digit) / 10) { + /* we ran out of bits, what do we do? */ + if (scale) { + /* truncate extra precision */ + break; + } else { + /* integer overflow. bail out. */ + goto parse_error; + } + } + value = (10 * value) + digit; + if (scale) + scale *= 10; + } else if (*field == '.' && scale == 0) { + scale = 1; + } else if (*field == ' ') { + /* Allow spaces at the start of the field. Not NMEA + * conformant, but some modules do this. */ + if (sign != 0 || value != -1 || scale != 0) + goto parse_error; + } else { + goto parse_error; + } + field++; + } + } + + if ((sign || scale) && value == -1) + goto parse_error; + + if (value == -1) { + /* No digits were scanned. */ + value = 0; + scale = 0; + } else if (scale == 0) { + /* No decimal point. */ + scale = 1; + } + if (sign) + value *= sign; + + *va_arg(ap, struct minmea_float *) = (struct minmea_float) {value, scale}; + } break; + + case 'i': { // Integer value, default 0 (int). + int value = 0; + + if (field) { + char *endptr; + value = strtol(field, &endptr, 10); + if (minmea_isfield(*endptr)) + goto parse_error; + } + + *va_arg(ap, int *) = value; + } break; + + case 's': { // String value (char *). + char *buf = va_arg(ap, char *); + + if (field) { + while (minmea_isfield(*field)) + *buf++ = *field++; + } + + *buf = '\0'; + } break; + + case 't': { // NMEA talker+sentence identifier (char *). + // This field is always mandatory. + if (!field) + goto parse_error; + + if (field[0] != '$') + goto parse_error; + for (int f=0; f<5; f++) + if (!minmea_isfield(field[1+f])) + goto parse_error; + + char *buf = va_arg(ap, char *); + memcpy(buf, field+1, 5); + buf[5] = '\0'; + } break; + + case 'D': { // Date (int, int, int), -1 if empty. + struct minmea_date *date = va_arg(ap, struct minmea_date *); + + int d = -1, m = -1, y = -1; + + if (field && minmea_isfield(*field)) { + // Always six digits. + for (int f=0; f<6; f++) + if (!isdigit((unsigned char) field[f])) + goto parse_error; + + d = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); + m = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10); + y = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10); + } + + date->day = d; + date->month = m; + date->year = y; + } break; + + case 'T': { // Time (int, int, int, int), -1 if empty. + struct minmea_time *time_ = va_arg(ap, struct minmea_time *); + + int h = -1, i = -1, s = -1, u = -1; + + if (field && minmea_isfield(*field)) { + // Minimum required: integer time. + for (int f=0; f<6; f++) + if (!isdigit((unsigned char) field[f])) + goto parse_error; + + h = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); + i = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10); + s = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10); + field += 6; + + // Extra: fractional time. Saved as microseconds. + if (*field++ == '.') { + int value = 0; + int scale = 1000000; + while (isdigit((unsigned char) *field) && scale > 1) { + value = (value * 10) + (*field++ - '0'); + scale /= 10; + } + u = value * scale; + } else { + u = 0; + } + } + + time_->hours = h; + time_->minutes = i; + time_->seconds = s; + time_->microseconds = u; + } break; + + case '_': { // Ignore the field. + } break; + + default: { // Unknown. + goto parse_error; + } break; + } + + next_field(); + } + + result = true; + +parse_error: + va_end(ap); + return result; +} + +bool minmea_talker_id(char talker[3], const char *sentence) +{ + char type[6]; + if (!minmea_scan(sentence, "t", type)) + return false; + + talker[0] = type[0]; + talker[1] = type[1]; + talker[2] = '\0'; + + return true; +} + +enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict) +{ + if (!minmea_check(sentence, strict)) + return MINMEA_INVALID; + + char type[6]; + if (!minmea_scan(sentence, "t", type)) + return MINMEA_INVALID; + + if (!strcmp(type+2, "RMC")) + return MINMEA_SENTENCE_RMC; + if (!strcmp(type+2, "GGA")) + return MINMEA_SENTENCE_GGA; + if (!strcmp(type+2, "GSA")) + return MINMEA_SENTENCE_GSA; + if (!strcmp(type+2, "GLL")) + return MINMEA_SENTENCE_GLL; + if (!strcmp(type+2, "GST")) + return MINMEA_SENTENCE_GST; + if (!strcmp(type+2, "GSV")) + return MINMEA_SENTENCE_GSV; + + return MINMEA_UNKNOWN; +} + +bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence) +{ + // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62 + char type[6]; + char validity; + int latitude_direction; + int longitude_direction; + int variation_direction; + if (!minmea_scan(sentence, "tTcfdfdffDfd", + type, + &frame->time, + &validity, + &frame->latitude, &latitude_direction, + &frame->longitude, &longitude_direction, + &frame->speed, + &frame->course, + &frame->date, + &frame->variation, &variation_direction)) + return false; + if (strcmp(type+2, "RMC")) + return false; + + frame->valid = (validity == 'A'); + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + frame->variation.value *= variation_direction; + + return true; +} + +bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence) +{ + // $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 + char type[6]; + int latitude_direction; + int longitude_direction; + + if (!minmea_scan(sentence, "tTfdfdiiffcfci_", + type, + &frame->time, + &frame->latitude, &latitude_direction, + &frame->longitude, &longitude_direction, + &frame->fix_quality, + &frame->satellites_tracked, + &frame->hdop, + &frame->altitude, &frame->altitude_units, + &frame->height, &frame->height_units, + &frame->dgps_age)) + return false; + if (strcmp(type+2, "GGA")) + return false; + + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + + return true; +} + +bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence) +{ + // $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39 + char type[6]; + + if (!minmea_scan(sentence, "tciiiiiiiiiiiiifff", + type, + &frame->mode, + &frame->fix_type, + &frame->sats[0], + &frame->sats[1], + &frame->sats[2], + &frame->sats[3], + &frame->sats[4], + &frame->sats[5], + &frame->sats[6], + &frame->sats[7], + &frame->sats[8], + &frame->sats[9], + &frame->sats[10], + &frame->sats[11], + &frame->pdop, + &frame->hdop, + &frame->vdop)) + return false; + if (strcmp(type+2, "GSA")) + return false; + + return true; +} + +bool minmea_parse_gll(struct minmea_sentence_gll *frame, const char *sentence) +{ + // $GPGLL,3723.2475,N,12158.3416,W,161229.487,A,A*41$; + char type[6]; + int latitude_direction; + int longitude_direction; + + if (!minmea_scan(sentence, "tfdfdTc;c", + type, + &frame->latitude, &latitude_direction, + &frame->longitude, &longitude_direction, + &frame->time, + &frame->status, + &frame->mode)) + return false; + if (strcmp(type+2, "GLL")) + return false; + + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + + return true; +} + +bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence) +{ + // $GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58 + char type[6]; + + if (!minmea_scan(sentence, "tTfffffff", + type, + &frame->time, + &frame->rms_deviation, + &frame->semi_major_deviation, + &frame->semi_minor_deviation, + &frame->semi_major_orientation, + &frame->latitude_error_deviation, + &frame->longitude_error_deviation, + &frame->altitude_error_deviation)) + return false; + if (strcmp(type+2, "GST")) + return false; + + return true; +} + +bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence) +{ + // $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74 + // $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D + // $GPGSV,4,2,11,08,51,203,30,09,45,215,28*75 + // $GPGSV,4,4,13,39,31,170,27*40 + // $GPGSV,4,4,13*7B + char type[6]; + + if (!minmea_scan(sentence, "tiii;iiiiiiiiiiiiiiii", + type, + &frame->total_msgs, + &frame->msg_nr, + &frame->total_sats, + &frame->sats[0].nr, + &frame->sats[0].elevation, + &frame->sats[0].azimuth, + &frame->sats[0].snr, + &frame->sats[1].nr, + &frame->sats[1].elevation, + &frame->sats[1].azimuth, + &frame->sats[1].snr, + &frame->sats[2].nr, + &frame->sats[2].elevation, + &frame->sats[2].azimuth, + &frame->sats[2].snr, + &frame->sats[3].nr, + &frame->sats[3].elevation, + &frame->sats[3].azimuth, + &frame->sats[3].snr + )) { + return false; + } + if (strcmp(type+2, "GSV")) + return false; + + return true; +} + +int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_) +{ + if (date->year == -1 || time_->hours == -1) + return -1; + + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_year = 2000 + date->year - 1900; + tm.tm_mon = date->month - 1; + tm.tm_mday = date->day; + tm.tm_hour = time_->hours; + tm.tm_min = time_->minutes; + tm.tm_sec = time_->seconds; + + time_t timestamp = timegm(&tm); /* See README.md if your system lacks timegm(). */ + if (timestamp != -1) { + ts->tv_sec = timestamp; + ts->tv_nsec = time_->microseconds * 1000; + return 0; + } else { + return -1; + } +} + +/* vim: set ts=4 sw=4 et: */ diff --git a/src/fsm/minmea.h b/src/fsm/minmea.h new file mode 100644 index 0000000..6d7fe18 --- /dev/null +++ b/src/fsm/minmea.h @@ -0,0 +1,234 @@ +/* + * Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com> + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +#ifndef MINMEA_H +#define MINMEA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <errno.h> +#include <time.h> +#include <math.h> + +#define MINMEA_MAX_LENGTH 80 + +enum minmea_sentence_id { + MINMEA_INVALID = -1, + MINMEA_UNKNOWN = 0, + MINMEA_SENTENCE_RMC, + MINMEA_SENTENCE_GGA, + MINMEA_SENTENCE_GSA, + MINMEA_SENTENCE_GLL, + MINMEA_SENTENCE_GST, + MINMEA_SENTENCE_GSV, +}; + +struct minmea_float { + int_least32_t value; + int_least32_t scale; +}; + +struct minmea_date { + int day; + int month; + int year; +}; + +struct minmea_time { + int hours; + int minutes; + int seconds; + int microseconds; +}; + +struct minmea_sentence_rmc { + struct minmea_time time; + bool valid; + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_float speed; + struct minmea_float course; + struct minmea_date date; + struct minmea_float variation; +}; + +struct minmea_sentence_gga { + struct minmea_time time; + struct minmea_float latitude; + struct minmea_float longitude; + int fix_quality; + int satellites_tracked; + struct minmea_float hdop; + struct minmea_float altitude; char altitude_units; + struct minmea_float height; char height_units; + int dgps_age; +}; + +enum minmea_gll_status { + MINMEA_GLL_STATUS_DATA_VALID = 'A', + MINMEA_GLL_STATUS_DATA_NOT_VALID = 'V', +}; + +enum minmea_gll_mode { + MINMEA_GLL_MODE_AUTONOMOUS = 'A', + MINMEA_GLL_MODE_DPGS = 'D', + MINMEA_GLL_MODE_DR = 'E', +}; + +struct minmea_sentence_gll { + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_time time; + char status; + char mode; +}; + +struct minmea_sentence_gst { + struct minmea_time time; + struct minmea_float rms_deviation; + struct minmea_float semi_major_deviation; + struct minmea_float semi_minor_deviation; + struct minmea_float semi_major_orientation; + struct minmea_float latitude_error_deviation; + struct minmea_float longitude_error_deviation; + struct minmea_float altitude_error_deviation; +}; + +enum minmea_gsa_mode { + MINMEA_GPGSA_MODE_AUTO = 'A', + MINMEA_GPGSA_MODE_FORCED = 'M', +}; + +enum minmea_gsa_fix_type { + MINMEA_GPGSA_FIX_NONE = 1, + MINMEA_GPGSA_FIX_2D = 2, + MINMEA_GPGSA_FIX_3D = 3, +}; + +struct minmea_sentence_gsa { + char mode; + int fix_type; + int sats[12]; + struct minmea_float pdop; + struct minmea_float hdop; + struct minmea_float vdop; +}; + +struct minmea_sat_info { + int nr; + int elevation; + int azimuth; + int snr; +}; + +struct minmea_sentence_gsv { + int total_msgs; + int msg_nr; + int total_sats; + struct minmea_sat_info sats[4]; +}; + +/** + * Calculate raw sentence checksum. Does not check sentence integrity. + */ +uint8_t minmea_checksum(const char *sentence); + +/** + * Check sentence validity and checksum. Returns true for valid sentences. + */ +bool minmea_check(const char *sentence, bool strict); + +/** + * Determine talker identifier. + */ +bool minmea_talker_id(char talker[3], const char *sentence); + +/** + * Determine sentence identifier. + */ +enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict); + +/** + * Scanf-like processor for NMEA sentences. Supports the following formats: + * c - single character (char *) + * d - direction, returned as 1/-1, default 0 (int *) + * f - fractional, returned as value + scale (int *, int *) + * i - decimal, default zero (int *) + * s - string (char *) + * t - talker identifier and type (char *) + * T - date/time stamp (int *, int *, int *) + * Returns true on success. See library source code for details. + */ +bool minmea_scan(const char *sentence, const char *format, ...); + +/* + * Parse a specific type of sentence. Return true on success. + */ +bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence); +bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence); +bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence); +bool minmea_parse_gll(struct minmea_sentence_gll *frame, const char *sentence); +bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence); +bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence); + +/** + * Convert GPS UTC date/time representation to a UNIX timestamp. + */ +int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_); + +/** + * Rescale a fixed-point value to a different scale. Rounds towards zero. + */ +static inline int_least32_t minmea_rescale(struct minmea_float *f, int_least32_t new_scale) +{ + if (f->scale == 0) + return 0; + if (f->scale == new_scale) + return f->value; + if (f->scale > new_scale) + return (f->value + ((f->value > 0) - (f->value < 0)) * f->scale/new_scale/2) / (f->scale/new_scale); + else + return f->value * (new_scale/f->scale); +} + +/** + * Convert a fixed-point value to a floating-point value. + * Returns NaN for "unknown" values. + */ +static inline float minmea_tofloat(struct minmea_float *f) +{ + if (f->scale == 0) + return NAN; + return (float) f->value / (float) f->scale; +} + +/** + * Convert a raw coordinate to a floating point DD.DDD... value. + * Returns NaN for "unknown" values. + */ +static inline float minmea_tocoord(struct minmea_float *f) +{ + if (f->scale == 0) + return NAN; + int_least32_t degrees = f->value / (f->scale * 100); + int_least32_t minutes = f->value % (f->scale * 100); + return (float) degrees + (float) minutes / (60 * f->scale); +} + +#ifdef __cplusplus +} +#endif + +#endif /* MINMEA_H */ + +/* vim: set ts=4 sw=4 et: */ diff --git a/src/fsm/ubx.c b/src/fsm/ubx.c deleted file mode 100644 index 19c427b..0000000 --- a/src/fsm/ubx.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Matthias P. Braendli - * - * 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 <stdint.h> -#include <string.h> -#include "ubx.h" - -static ubx_callbacks_t ubx_cb; - -void ubx_register(const ubx_callbacks_t *cb) -{ - memcpy(&ubx_cb, cb, sizeof(ubx_callbacks_t)); -} - -static ubx_nav_sol_t navsol_temp; -static ubx_nav_timeutc_t navtimeutc_temp; -static ubx_tim_tm2_t timtm2_temp; - -// State machine variables -static ubx_state_t ubxState = UBXSTATE_SYNC1; -static uint16_t msglen; -static uint8_t* messagedata; -static uint8_t msgclass; -static uint8_t msgid; -static uint8_t cka, ckb; - -int ubx_parse(const uint8_t c) -{ - switch (ubxState) - { - case UBXSTATE_SYNC1: - if (c == UBX_SYNC1_CHAR) { - ubxState = UBXSTATE_SYNC2; - } - break; - - case UBXSTATE_SYNC2: - if (c == UBX_SYNC2_CHAR) { - ubxState = UBXSTATE_CLASS; - } - else if (c == UBX_SYNC1_CHAR) { - ubxState = UBXSTATE_SYNC2; - } - else { - ubxState = UBXSTATE_SYNC1; - } - break; - - case UBXSTATE_CLASS: - ubxState = UBXSTATE_ID; - msgclass = c; - - // re-init checksum - cka = c; - ckb = cka; - break; - - case UBXSTATE_ID: - msgid = c; - ubxState = UBXSTATE_LEN1; - if (msgclass == UBX_CLASS_NAV) { - if (c == UBX_NAV_SOL) { - messagedata = (uint8_t*)&navsol_temp; - } - else if (c == UBX_NAV_TIMEUTC) { - messagedata = (uint8_t*)&navtimeutc_temp; - } - } - else if (msgclass == UBX_CLASS_TIM && c == UBX_TIM_TM2) { - messagedata = (uint8_t*)&timtm2_temp; - } - else { - msgclass = 0; - msgid = 0; - ubxState = UBXSTATE_SYNC1; - } - cka += c; - ckb += cka; - break; - - case UBXSTATE_LEN1: - msglen = (uint16_t)c; - ubxState = UBXSTATE_LEN2; - cka += c; - ckb += cka; - break; - - case UBXSTATE_LEN2: - msglen |= ((uint16_t)c) << 8; - ubxState = UBXSTATE_DATA; - cka += c; - ckb += cka; - break; - - case UBXSTATE_DATA: - cka += c; - ckb += cka; - if (--msglen) { - *(messagedata++) = c; - } - else { - ubxState = UBXSTATE_CKA; - } - break; - - case UBXSTATE_CKA: - if (c == cka) { - ubxState = UBXSTATE_CKB; - } - else { - ubxState = UBXSTATE_SYNC1; - } - break; - - case UBXSTATE_CKB: - if (c == ckb) { - if (msgclass == UBX_CLASS_NAV) { - if (msgid == UBX_NAV_SOL && ubx_cb.new_ubx_nav_sol) { - ubx_cb.new_ubx_nav_sol(&navsol_temp); - } - else if (msgid == UBX_NAV_TIMEUTC && ubx_cb.new_ubx_nav_timeutc) { - ubx_cb.new_ubx_nav_timeutc(&navtimeutc_temp); - } - } - else if (msgclass == UBX_CLASS_TIM) { - if (msgid == UBX_TIM_TM2 && ubx_cb.new_ubx_tim_tm2) { - ubx_cb.new_ubx_tim_tm2(&timtm2_temp); - } - } - } - - ubxState = UBXSTATE_SYNC1; - } - return ubxState; -} - diff --git a/src/fsm/ubx.h b/src/fsm/ubx.h deleted file mode 100644 index e80464e..0000000 --- a/src/fsm/ubx.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Matthias P. Braendli - * - * 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. -*/ - -/* u-blox UBX protocol parser, register callbacks for specific messages using - * ubx_register() and and push in chars with ubx_parse() - */ - -#ifndef __UBX_H_ -#define __UBX_H_ - -#include <stdint.h> - -// message sync bytes -#define UBX_SYNC1_CHAR 0xB5 -#define UBX_SYNC2_CHAR 0x62 - -// UBX Class -typedef enum app_ubx_class_e // {{{ -{ - UBX_CLASS_NAV = 0x01, // Navigation Results: Position, Speed, Time, Acc, Heading, DOP, SVs used - UBX_CLASS_RXM = 0x02, // Receiver Manager Messages: Satellite Status, RTC Status - UBX_CLASS_TRK = 0x03, // Tracking Information: Measurements, Commands - UBX_CLASS_INF = 0x04, // Information Messages: Printf-Style Messages, with IDs such as Error, Warning, Notice - UBX_CLASS_ACK = 0x05, // Ack/Nack Messages: as replies to CFG Input Messages - UBX_CLASS_CFG = 0x06, // Configuration Input Messages: Set Dynamic Model, Set DOP Mask, Set Baud Rate, etc. - UBX_CLASS_MON = 0x0A, // Monitoring Messages: Communication Status, CPU Load, Stack Usage, Task Status - UBX_CLASS_TIM = 0x0D, // Timing Messages: Timepulse Output, Timemark Results -} app_ubx_class_t; //}}} - -// UBX Message Id -typedef enum app_ubx_id_e // {{{ -{ - // ACK - UBX_ACK_NACK = 0x00, // Not acknowledge - UBX_ACK_ACK = 0x01, // acknowledge - - // NAV - UBX_NAV_STATUS = 0x03, // Navigation status - UBX_NAV_SOL = 0x06, // Navigation solution - UBX_NAV_PVT = 0x07, // Navigation PVT solution - UBX_NAV_TIMEGPS = 0x20, // GPS time - UBX_NAV_TIMEUTC = 0x21, // UTC time - - // TIM - UBX_TIM_TM2 = 0x03, // timemark data -} app_ubx_id_t; //}}} - -// ubx parser state -typedef enum ubx_state_e -{ - UBXSTATE_SYNC1, - UBXSTATE_SYNC2, - UBXSTATE_CLASS, - UBXSTATE_ID, - UBXSTATE_LEN1, - UBXSTATE_LEN2, - UBXSTATE_DATA, - UBXSTATE_CKA, - UBXSTATE_CKB -} ubx_state_t; - -#define GPSFIX_3D 0x3 - -// UBX messages to enable these messages on the I2C port -#define UBX_ENABLE_NAV_SOL \ - 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, \ - 0x01, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x17, 0xDB - -#define UBX_ENABLE_NAV_TIMEUTC \ - 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, \ - 0x01, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x32, 0x98 - -#define UBX_ENABLE_TIM_TM2 \ - 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, \ - 0x0D, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x20, 0x26 - -typedef struct ubx_nav_sol_s -{ - uint32_t itow; // ms GPS Millisecond Time of Week - int32_t frac; // ns remainder of rounded ms above - int16_t week; // GPS week - uint8_t GPSfix; // GPSfix Type, range 0..6 - uint8_t Flags; // Navigation Status Flags - int32_t ECEF_X; // cm ECEF X coordinate - int32_t ECEF_Y; // cm ECEF Y coordinate - int32_t ECEF_Z; // cm ECEF Z coordinate - int32_t PAcc; // cm 3D Position Accuracy Estimate - int32_t ECEFVX; // cm/s ECEF X velocity - int32_t ECEFVY; // cm/s ECEF Y velocity - int32_t ECEFVZ; // cm/s ECEF Z velocity - uint32_t SAcc; // cm/s Speed Accuracy Estimate - uint16_t PDOP; // 0.01 Position DOP - uint8_t res1; // reserved - uint8_t numSV; // Number of SVs used in navigation solution - uint32_t res2; // reserved -} __attribute__((packed)) ubx_nav_sol_t; - - -typedef struct ubx_nav_velned_s -{ - uint32_t itow; // ms GPS Millisecond Time of Week - int32_t VEL_N; // cm/s NED north velocity - int32_t VEL_E; // cm/s NED east velocity - int32_t VEL_D; // cm/s NED down velocity - int32_t Speed; // cm/s Speed (3-D) - int32_t GSpeed; // cm/s Ground Speed (2-D) - int32_t Heading; // 1e-05 deg Heading 2-D - uint32_t SAcc; // cm/s Speed Accuracy Estimate - uint32_t CAcc; // deg Course / Heading Accuracy Estimate -} __attribute__((packed)) ubx_nav_velned_t; - -typedef struct ubx_nav_posllh_s -{ - uint32_t itow; // ms GPS Millisecond Time of Week - int32_t LON; // 1e-07 deg Longitude - int32_t LAT; // 1e-07 deg Latitude - int32_t HEIGHT; // mm Height above Ellipsoid - int32_t HMSL; // mm Height above mean sea level - uint32_t Hacc; // mm Horizontal Accuracy Estimate - uint32_t Vacc; // mm Vertical Accuracy Estimate -} __attribute__((packed)) ubx_nav_posllh_t; - -typedef struct ubx_tim_tm2_s -{ - uint8_t ch; // marker channel (0 or 1) - uint8_t flags; // bitmask - uint16_t count; // edge counter - uint16_t wnoR; // week number of last rising edge - uint16_t wnoF; // week number of last falling edge - uint32_t towMsR; // tow in ms of last rising edge [ms] - uint32_t towSubMsR; // millisecond fraction of tow of last rising edge [ns] - uint32_t towMsF; // tow in ms of last rising edge [ms] - uint32_t towSubMsF; // millisecond fraction of tow of last rising edge [ns] - uint32_t accEst; // accuracy estimate [ns] -} __attribute__((packed)) ubx_tim_tm2_t; - - -typedef struct ubx_nav_timeutc_s -{ - uint32_t itow; // GPS Millisecond Time of Week - uint32_t tacc; // time accuracy estimate [ns] - int32_t nano; // Nanoseconds of second - uint16_t year; // Year, range 1999..2099 (UTC) - uint8_t month; // Month, range 1..12 (UTC) - uint8_t day; // Day of Month, range 1..31 (UTC) - uint8_t hour; // Hour of Day, range 0..23 (UTC) - uint8_t min; // Minute of Hour, range 0..59 (UTC) - uint8_t sec; // Seconds of Minute, range 0..59 (UTC) - uint8_t valid; // validity flag -} __attribute__((packed)) ubx_nav_timeutc_t; - - -typedef struct ubx_callbacks_s -{ - void (*new_ubx_nav_sol)(ubx_nav_sol_t *sol); - void (*new_ubx_nav_timeutc)(ubx_nav_timeutc_t *timeutc); - void (*new_ubx_tim_tm2)(ubx_tim_tm2_t *posllh); -} ubx_callbacks_t; - -/* Register callbacks when a complete message was received */ -void ubx_register(const ubx_callbacks_t *cb); - -/* Parse a new incoming byte, calls the callbacks if a complete message - * was received. Returns the current parser state - */ -int ubx_parse(const uint8_t c); - -#endif - diff --git a/src/fsm/usart.c b/src/fsm/usart.c new file mode 100644 index 0000000..013c4db --- /dev/null +++ b/src/fsm/usart.c @@ -0,0 +1,155 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Matthias P. Braendli + * + * 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 "common.h" +#include "usart.h" +#include <stm32f4xx.h> +#include <stm32f4xx_usart.h> +#include <stm32f4xx_conf.h> +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +// USART 3 on PD8 and PD9 +const uint16_t GPIOD_PIN_USART3_TX = GPIO_Pin_8; +const uint16_t GPIOD_PIN_USART3_RX = GPIO_Pin_9; + +// The ISR writes into this buffer +static char nmea_sentence[MAX_NMEA_SENTENCE_LEN]; +static int nmea_sentence_last_written = 0; + +// Once a completed NMEA sentence is received in the ISR, +// it is appended to this queue +static QueueHandle_t usart_nmea_queue; + +void usart_init() +{ + usart_nmea_queue = xQueueCreate(15, MAX_NMEA_SENTENCE_LEN); + if (usart_nmea_queue == 0) { + while(1); /* fatal error */ + } + + // Setup GPIO D and connect to USART 3 + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); + + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.GPIO_Pin = GPIOD_PIN_USART3_RX | GPIOD_PIN_USART3_TX; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; + GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(GPIOD, &GPIO_InitStruct); + + GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_USART3); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_USART3); + + // Setup USART3 for 9600,8,N,1 + + USART_InitTypeDef USART_InitStruct; + USART_InitStruct.USART_BaudRate = 9600; + USART_InitStruct.USART_WordLength = USART_WordLength_8b; + USART_InitStruct.USART_StopBits = USART_StopBits_1; + USART_InitStruct.USART_Parity = USART_Parity_No; + USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + USART_Init(USART3, &USART_InitStruct); + + + // enable the USART3 receive interrupt + USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + NVIC_SetPriority(USART3_IRQn, 6); + + // finally this enables the complete USART3 peripheral + USART_Cmd(USART3, ENABLE); +} + +void usart_puts(const char* str) +{ + while(*str) { + // wait until data register is empty + while ( !(USART3->SR & 0x00000040) ); + USART_SendData(USART3, *str); + str++; + } +} + +int usart_get_nmea_sentence(char* nmea) +{ + return xQueueReceive(usart_nmea_queue, nmea, portMAX_DELAY); +} + + +static void usart_clear_nmea_buffer(void) +{ + for (int i = 0; i < MAX_NMEA_SENTENCE_LEN; i++) { + nmea_sentence[i] = '\0'; + } + nmea_sentence_last_written = 0; +} + +void USART3_IRQHandler(void) +{ + if (USART_GetITStatus(USART3, USART_IT_RXNE)) { + char t = USART3->DR; + + if (nmea_sentence_last_written == 0) { + if (t == '$') { + // Likely new start of sentence + nmea_sentence[nmea_sentence_last_written] = t; + nmea_sentence_last_written++; + } + } + else if (nmea_sentence_last_written < MAX_NMEA_SENTENCE_LEN) { + nmea_sentence[nmea_sentence_last_written] = t; + nmea_sentence_last_written++; + + if (t == '\n') { + int success = xQueueSendToBackFromISR( + usart_nmea_queue, + nmea_sentence, + NULL); + + if (success == pdFALSE) { + trigger_fault(FAULT_SOURCE_USART); + } + + usart_clear_nmea_buffer(); + } + } + else { + // Buffer overrun without a meaningful NMEA message. + usart_clear_nmea_buffer(); + } + } +} + diff --git a/src/fsm/usart.h b/src/fsm/usart.h new file mode 100644 index 0000000..4ab8b22 --- /dev/null +++ b/src/fsm/usart.h @@ -0,0 +1,42 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Matthias P. Braendli + * + * 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. +*/ + +/* This handles the USART to the GPS receiver, and fills a queue of + * NMEA messages. + */ + +#ifndef __USART_H_ +#define __USART_H_ + +#define MAX_NMEA_SENTENCE_LEN 256 + +void usart_init(void); + +void usart_puts(const char* str); + +// Get a MAX_NMEA_SENTENCE_LEN sized NMEA sentence +int usart_get_nmea_sentence(char* nmea); + +#endif //__USART_H_ + |