GPS driver

Dependents:   GroveGPS-Example ncu_project

Committer:
c1728p9
Date:
Tue Jun 18 12:14:13 2019 -0500
Revision:
5:8a5414710483
Parent:
4:0ee729b4a211
Add bool return values for GPS functions to indicate success

Who changed what in which revision?

UserRevisionLine numberNew contents of line
michaelray 0:56d6407653a7 1 #ifndef _GROVE_GPS_H_
michaelray 0:56d6407653a7 2 #define _GROVE_GPS_H_
michaelray 0:56d6407653a7 3
michaelray 0:56d6407653a7 4 #include "mbed.h"
michaelray 0:56d6407653a7 5 #include <stdlib.h>
michaelray 0:56d6407653a7 6 #include <string>
michaelray 0:56d6407653a7 7
michaelray 0:56d6407653a7 8 class GroveGPS {
michaelray 0:56d6407653a7 9
michaelray 0:56d6407653a7 10 public:
michaelray 0:56d6407653a7 11
c1728p9 4:0ee729b4a211 12 GroveGPS(PinName tx=D1, PinName rx=D0) : gps_serial(tx, rx, 9600) {
c1728p9 4:0ee729b4a211 13 memset(_isr_line_bufs, 0, sizeof(_isr_line_bufs));
c1728p9 4:0ee729b4a211 14 _first_line_in_use = true;
c1728p9 4:0ee729b4a211 15 _isr_line_buf_pos = 0;
c1728p9 4:0ee729b4a211 16 memset(_last_line, 0, sizeof(_last_line));
c1728p9 4:0ee729b4a211 17 _last_line_updated = false;
c1728p9 4:0ee729b4a211 18 gps_serial.attach(callback(this, &GroveGPS::read_serial), SerialBase::RxIrq);
michaelray 0:56d6407653a7 19 }
michaelray 0:56d6407653a7 20
michaelray 0:56d6407653a7 21 struct GGA {
michaelray 0:56d6407653a7 22 double utc_time; // Format: hhmmss.sss
michaelray 0:56d6407653a7 23 double latitude; // Format: ddmm.mmmm
michaelray 0:56d6407653a7 24 char ns_indicator; // Format: N=north or S=south
michaelray 0:56d6407653a7 25 double longitude; // Format: dddmm.mmmm
michaelray 0:56d6407653a7 26 char ew_indicator; // Format: E=east or W=west
michaelray 0:56d6407653a7 27 int position_fix; // Options: [0=not available, 1=GPS SPS mode, 2=Differential GPS, 6=dead reckoning]
michaelray 0:56d6407653a7 28 int sats_used; // Range: 0-12
michaelray 0:56d6407653a7 29 double hdop; // Horizontal Dilution of Precision
michaelray 0:56d6407653a7 30 double msl_altitude;
michaelray 0:56d6407653a7 31 char msl_altitude_units;
michaelray 0:56d6407653a7 32 double geoid_separation;
michaelray 0:56d6407653a7 33 char geoid_separation_units;
michaelray 0:56d6407653a7 34 long age_of_diff;
michaelray 0:56d6407653a7 35 long diff_ref_station_id;
michaelray 0:56d6407653a7 36 } gps_gga;
michaelray 0:56d6407653a7 37
c1728p9 5:8a5414710483 38 bool getTimestamp(char* buffer) {
sarahmarshy 1:0607ba3aa02d 39 m.lock();
c1728p9 4:0ee729b4a211 40 parseLine();
c1728p9 4:0ee729b4a211 41
c1728p9 5:8a5414710483 42 bool valid = gps_gga.position_fix != 0;
michaelray 0:56d6407653a7 43 sprintf(buffer, "%f", gps_gga.utc_time);
sarahmarshy 1:0607ba3aa02d 44 m.unlock();
c1728p9 5:8a5414710483 45
c1728p9 5:8a5414710483 46 return valid;
michaelray 0:56d6407653a7 47 }
michaelray 0:56d6407653a7 48
c1728p9 5:8a5414710483 49 bool getLatitude(char* buffer) {
sarahmarshy 1:0607ba3aa02d 50 m.lock();
c1728p9 4:0ee729b4a211 51 parseLine();
c1728p9 4:0ee729b4a211 52
c1728p9 5:8a5414710483 53 bool valid = false;
michaelray 0:56d6407653a7 54 double coordinate = convertGPSToDecimal(gps_gga.latitude);
c1728p9 5:8a5414710483 55 if (gps_gga.position_fix==0) {
michaelray 0:56d6407653a7 56 sprintf(buffer, "N/A");
c1728p9 5:8a5414710483 57 } else {
michaelray 0:56d6407653a7 58 sprintf(buffer, "%c%f", (gps_gga.ns_indicator == 'N') ? '0' : '-', coordinate);
c1728p9 5:8a5414710483 59 valid = true;
c1728p9 5:8a5414710483 60 }
sarahmarshy 1:0607ba3aa02d 61 m.unlock();
c1728p9 5:8a5414710483 62
c1728p9 5:8a5414710483 63 return valid;
michaelray 0:56d6407653a7 64 }
michaelray 0:56d6407653a7 65
c1728p9 5:8a5414710483 66 bool getLongitude(char* buffer) {
sarahmarshy 1:0607ba3aa02d 67 m.lock();
c1728p9 4:0ee729b4a211 68 parseLine();
c1728p9 4:0ee729b4a211 69
c1728p9 5:8a5414710483 70 bool valid = false;
michaelray 0:56d6407653a7 71 double coordinate = convertGPSToDecimal(gps_gga.longitude);
c1728p9 5:8a5414710483 72 if (gps_gga.position_fix==0) {
michaelray 0:56d6407653a7 73 sprintf(buffer, "N/A");
c1728p9 5:8a5414710483 74 } else {
c1728p9 5:8a5414710483 75 sprintf(buffer, "%c%f", (gps_gga.ew_indicator == 'E') ? '0' : '-', coordinate);
c1728p9 5:8a5414710483 76 valid = true;
c1728p9 5:8a5414710483 77 }
sarahmarshy 1:0607ba3aa02d 78 m.unlock();
c1728p9 5:8a5414710483 79
c1728p9 5:8a5414710483 80 return valid;
michaelray 0:56d6407653a7 81 }
michaelray 0:56d6407653a7 82
michaelray 0:56d6407653a7 83 private:
c1728p9 4:0ee729b4a211 84 static const size_t max_line_length = 256;
c1728p9 4:0ee729b4a211 85 char _isr_line_bufs[2][max_line_length];
c1728p9 4:0ee729b4a211 86 bool _first_line_in_use;
c1728p9 4:0ee729b4a211 87 size_t _isr_line_buf_pos;
c1728p9 4:0ee729b4a211 88 char _last_line[max_line_length];
c1728p9 4:0ee729b4a211 89 bool _last_line_updated;
c1728p9 4:0ee729b4a211 90
c1728p9 4:0ee729b4a211 91 RawSerial gps_serial;
sarahmarshy 1:0607ba3aa02d 92 Mutex m;
c1728p9 4:0ee729b4a211 93
sarahmarshy 1:0607ba3aa02d 94 void read_serial() {
c1728p9 4:0ee729b4a211 95 while (gps_serial.readable()) {
c1728p9 4:0ee729b4a211 96
c1728p9 4:0ee729b4a211 97 // Check for overflow
c1728p9 4:0ee729b4a211 98 if (_isr_line_buf_pos > max_line_length -1 ) {
c1728p9 4:0ee729b4a211 99 error("GPS error - line too long");
c1728p9 4:0ee729b4a211 100 _isr_line_buf_pos = 0;
c1728p9 4:0ee729b4a211 101 }
c1728p9 4:0ee729b4a211 102
c1728p9 4:0ee729b4a211 103 // Add a character to the active buffer
c1728p9 4:0ee729b4a211 104 char *buf = _isr_line_bufs[_first_line_in_use ? 0 : 1];
c1728p9 4:0ee729b4a211 105 char value = gps_serial.getc();
c1728p9 4:0ee729b4a211 106 buf[_isr_line_buf_pos] = value;
c1728p9 4:0ee729b4a211 107 _isr_line_buf_pos++;
c1728p9 4:0ee729b4a211 108
c1728p9 4:0ee729b4a211 109 // Check for end of line
c1728p9 4:0ee729b4a211 110 if (value == '\n') {
c1728p9 4:0ee729b4a211 111 buf[_isr_line_buf_pos] = 0;
c1728p9 4:0ee729b4a211 112 _isr_line_buf_pos = 0;
c1728p9 4:0ee729b4a211 113
c1728p9 4:0ee729b4a211 114 // Save off this line if it is valid
c1728p9 4:0ee729b4a211 115 if (memcmp("$GPGGA", buf, 6) == 0) {
c1728p9 4:0ee729b4a211 116 _first_line_in_use = !_first_line_in_use;
c1728p9 4:0ee729b4a211 117 _last_line_updated = true;
c1728p9 4:0ee729b4a211 118 }
sarahmarshy 1:0607ba3aa02d 119 }
sarahmarshy 1:0607ba3aa02d 120 }
sarahmarshy 1:0607ba3aa02d 121 }
michaelray 0:56d6407653a7 122
michaelray 0:56d6407653a7 123 double convertGPSToDecimal(double coordinate) {
michaelray 0:56d6407653a7 124 int degrees = coordinate/100.0;
michaelray 0:56d6407653a7 125 int minutes = ((int)coordinate) % 100;
michaelray 0:56d6407653a7 126 double seconds = coordinate - ((int)coordinate);
michaelray 0:56d6407653a7 127 return degrees + (minutes+seconds)/60;
michaelray 0:56d6407653a7 128
michaelray 0:56d6407653a7 129 }
michaelray 0:56d6407653a7 130
michaelray 0:56d6407653a7 131 void parseLine() {
c1728p9 4:0ee729b4a211 132 bool parse_gga = false;
c1728p9 4:0ee729b4a211 133
c1728p9 4:0ee729b4a211 134 // Atomically copy the line buffer since the ISR can change it at any time
c1728p9 4:0ee729b4a211 135 core_util_critical_section_enter();
c1728p9 4:0ee729b4a211 136 if (_last_line_updated) {
c1728p9 4:0ee729b4a211 137 char *buf_saved = _isr_line_bufs[_first_line_in_use ? 1 : 0];
c1728p9 4:0ee729b4a211 138 strcpy(_last_line, buf_saved);
c1728p9 4:0ee729b4a211 139 parse_gga = true;
c1728p9 4:0ee729b4a211 140 _last_line_updated = false;
c1728p9 4:0ee729b4a211 141 }
c1728p9 4:0ee729b4a211 142 core_util_critical_section_exit();
c1728p9 4:0ee729b4a211 143
c1728p9 4:0ee729b4a211 144 if (parse_gga) {
c1728p9 4:0ee729b4a211 145 parseGGA();
c1728p9 4:0ee729b4a211 146 }
michaelray 0:56d6407653a7 147 }
michaelray 0:56d6407653a7 148
michaelray 0:56d6407653a7 149 void parseGGA() {
c1728p9 4:0ee729b4a211 150 char *line_pos = _last_line;
michaelray 0:56d6407653a7 151 for (int i=0; i<14; i++) {
michaelray 0:56d6407653a7 152 if (i==0) { // NMEA Tag
michaelray 0:56d6407653a7 153 } else if (i==1) { // UTC time
c1728p9 4:0ee729b4a211 154 gps_gga.utc_time = strtod(line_pos, 0);
michaelray 0:56d6407653a7 155 } else if (i==2) { // Latitude
c1728p9 4:0ee729b4a211 156 gps_gga.latitude = strtod(line_pos, 0);
michaelray 0:56d6407653a7 157 } else if (i==3) { // Latitude North/South indicator
c1728p9 4:0ee729b4a211 158 gps_gga.ns_indicator = line_pos[0];
michaelray 0:56d6407653a7 159 } else if (i==4) { // Longitude
c1728p9 4:0ee729b4a211 160 gps_gga.longitude = strtod(line_pos, 0);
michaelray 0:56d6407653a7 161 } else if (i==5) { // Longitude indicator
c1728p9 4:0ee729b4a211 162 gps_gga.ew_indicator = line_pos[0];
michaelray 0:56d6407653a7 163 } else if (i==6) {
c1728p9 4:0ee729b4a211 164 gps_gga.position_fix = strtod(line_pos, 0);
michaelray 0:56d6407653a7 165 }
c1728p9 4:0ee729b4a211 166 line_pos = strchr(line_pos, ',');
c1728p9 4:0ee729b4a211 167 if (line_pos == NULL) {
c1728p9 4:0ee729b4a211 168 break;
c1728p9 4:0ee729b4a211 169 }
c1728p9 4:0ee729b4a211 170 line_pos += 1;
michaelray 0:56d6407653a7 171 }
michaelray 0:56d6407653a7 172 }
michaelray 0:56d6407653a7 173 };
michaelray 0:56d6407653a7 174
sarahmarshy 1:0607ba3aa02d 175 #endif