2021 mbed Grove GPS
GroveGPS.h@6:dffd55375288, 2021-02-12 (annotated)
- Committer:
- anthonyv
- Date:
- Fri Feb 12 08:18:15 2021 +0000
- Revision:
- 6:dffd55375288
- Parent:
- 4:0ee729b4a211
IUT mbed GPS
Who changed what in which revision?
User | Revision | Line number | New 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 | |
michaelray | 0:56d6407653a7 | 38 | void getTimestamp(char* buffer) { |
sarahmarshy | 1:0607ba3aa02d | 39 | m.lock(); |
c1728p9 | 4:0ee729b4a211 | 40 | parseLine(); |
c1728p9 | 4:0ee729b4a211 | 41 | |
michaelray | 0:56d6407653a7 | 42 | sprintf(buffer, "%f", gps_gga.utc_time); |
sarahmarshy | 1:0607ba3aa02d | 43 | m.unlock(); |
michaelray | 0:56d6407653a7 | 44 | } |
michaelray | 0:56d6407653a7 | 45 | |
michaelray | 0:56d6407653a7 | 46 | void getLatitude(char* buffer) { |
sarahmarshy | 1:0607ba3aa02d | 47 | m.lock(); |
c1728p9 | 4:0ee729b4a211 | 48 | parseLine(); |
c1728p9 | 4:0ee729b4a211 | 49 | |
anthonyv | 6:dffd55375288 | 50 | double coordinate = gps_gga.latitude; |
michaelray | 0:56d6407653a7 | 51 | if (gps_gga.position_fix==0) |
michaelray | 0:56d6407653a7 | 52 | sprintf(buffer, "N/A"); |
michaelray | 0:56d6407653a7 | 53 | else |
michaelray | 0:56d6407653a7 | 54 | sprintf(buffer, "%c%f", (gps_gga.ns_indicator == 'N') ? '0' : '-', coordinate); |
sarahmarshy | 1:0607ba3aa02d | 55 | m.unlock(); |
michaelray | 0:56d6407653a7 | 56 | } |
michaelray | 0:56d6407653a7 | 57 | |
michaelray | 0:56d6407653a7 | 58 | void getLongitude(char* buffer) { |
sarahmarshy | 1:0607ba3aa02d | 59 | m.lock(); |
c1728p9 | 4:0ee729b4a211 | 60 | parseLine(); |
c1728p9 | 4:0ee729b4a211 | 61 | |
anthonyv | 6:dffd55375288 | 62 | double coordinate = gps_gga.longitude; |
michaelray | 0:56d6407653a7 | 63 | if (gps_gga.position_fix==0) |
michaelray | 0:56d6407653a7 | 64 | sprintf(buffer, "N/A"); |
michaelray | 0:56d6407653a7 | 65 | else |
michaelray | 0:56d6407653a7 | 66 | sprintf(buffer, "%c%f", (gps_gga.ew_indicator == 'E') ? '0' : '-', coordinate); |
sarahmarshy | 1:0607ba3aa02d | 67 | m.unlock(); |
michaelray | 0:56d6407653a7 | 68 | } |
anthonyv | 6:dffd55375288 | 69 | |
anthonyv | 6:dffd55375288 | 70 | void getAltitude(char* buffer) { |
anthonyv | 6:dffd55375288 | 71 | m.lock(); |
anthonyv | 6:dffd55375288 | 72 | parseLine(); |
anthonyv | 6:dffd55375288 | 73 | |
anthonyv | 6:dffd55375288 | 74 | double coordinate = gps_gga.msl_altitude; |
anthonyv | 6:dffd55375288 | 75 | if (gps_gga.position_fix==0) |
anthonyv | 6:dffd55375288 | 76 | sprintf(buffer, "N/A"); |
anthonyv | 6:dffd55375288 | 77 | else |
anthonyv | 6:dffd55375288 | 78 | sprintf(buffer, " %f", coordinate); |
anthonyv | 6:dffd55375288 | 79 | m.unlock(); |
anthonyv | 6:dffd55375288 | 80 | } |
anthonyv | 6:dffd55375288 | 81 | |
anthonyv | 6:dffd55375288 | 82 | void update() { |
anthonyv | 6:dffd55375288 | 83 | m.lock(); |
anthonyv | 6:dffd55375288 | 84 | parseLine(); |
anthonyv | 6:dffd55375288 | 85 | m.unlock(); |
anthonyv | 6:dffd55375288 | 86 | } |
michaelray | 0:56d6407653a7 | 87 | |
michaelray | 0:56d6407653a7 | 88 | private: |
c1728p9 | 4:0ee729b4a211 | 89 | static const size_t max_line_length = 256; |
c1728p9 | 4:0ee729b4a211 | 90 | char _isr_line_bufs[2][max_line_length]; |
c1728p9 | 4:0ee729b4a211 | 91 | bool _first_line_in_use; |
c1728p9 | 4:0ee729b4a211 | 92 | size_t _isr_line_buf_pos; |
c1728p9 | 4:0ee729b4a211 | 93 | char _last_line[max_line_length]; |
c1728p9 | 4:0ee729b4a211 | 94 | bool _last_line_updated; |
c1728p9 | 4:0ee729b4a211 | 95 | |
c1728p9 | 4:0ee729b4a211 | 96 | RawSerial gps_serial; |
sarahmarshy | 1:0607ba3aa02d | 97 | Mutex m; |
c1728p9 | 4:0ee729b4a211 | 98 | |
sarahmarshy | 1:0607ba3aa02d | 99 | void read_serial() { |
c1728p9 | 4:0ee729b4a211 | 100 | while (gps_serial.readable()) { |
c1728p9 | 4:0ee729b4a211 | 101 | |
c1728p9 | 4:0ee729b4a211 | 102 | // Check for overflow |
c1728p9 | 4:0ee729b4a211 | 103 | if (_isr_line_buf_pos > max_line_length -1 ) { |
c1728p9 | 4:0ee729b4a211 | 104 | error("GPS error - line too long"); |
c1728p9 | 4:0ee729b4a211 | 105 | _isr_line_buf_pos = 0; |
c1728p9 | 4:0ee729b4a211 | 106 | } |
c1728p9 | 4:0ee729b4a211 | 107 | |
c1728p9 | 4:0ee729b4a211 | 108 | // Add a character to the active buffer |
c1728p9 | 4:0ee729b4a211 | 109 | char *buf = _isr_line_bufs[_first_line_in_use ? 0 : 1]; |
c1728p9 | 4:0ee729b4a211 | 110 | char value = gps_serial.getc(); |
c1728p9 | 4:0ee729b4a211 | 111 | buf[_isr_line_buf_pos] = value; |
c1728p9 | 4:0ee729b4a211 | 112 | _isr_line_buf_pos++; |
c1728p9 | 4:0ee729b4a211 | 113 | |
c1728p9 | 4:0ee729b4a211 | 114 | // Check for end of line |
c1728p9 | 4:0ee729b4a211 | 115 | if (value == '\n') { |
c1728p9 | 4:0ee729b4a211 | 116 | buf[_isr_line_buf_pos] = 0; |
c1728p9 | 4:0ee729b4a211 | 117 | _isr_line_buf_pos = 0; |
c1728p9 | 4:0ee729b4a211 | 118 | |
c1728p9 | 4:0ee729b4a211 | 119 | // Save off this line if it is valid |
c1728p9 | 4:0ee729b4a211 | 120 | if (memcmp("$GPGGA", buf, 6) == 0) { |
c1728p9 | 4:0ee729b4a211 | 121 | _first_line_in_use = !_first_line_in_use; |
c1728p9 | 4:0ee729b4a211 | 122 | _last_line_updated = true; |
c1728p9 | 4:0ee729b4a211 | 123 | } |
sarahmarshy | 1:0607ba3aa02d | 124 | } |
sarahmarshy | 1:0607ba3aa02d | 125 | } |
sarahmarshy | 1:0607ba3aa02d | 126 | } |
michaelray | 0:56d6407653a7 | 127 | |
michaelray | 0:56d6407653a7 | 128 | double convertGPSToDecimal(double coordinate) { |
michaelray | 0:56d6407653a7 | 129 | int degrees = coordinate/100.0; |
michaelray | 0:56d6407653a7 | 130 | int minutes = ((int)coordinate) % 100; |
michaelray | 0:56d6407653a7 | 131 | double seconds = coordinate - ((int)coordinate); |
michaelray | 0:56d6407653a7 | 132 | return degrees + (minutes+seconds)/60; |
michaelray | 0:56d6407653a7 | 133 | |
michaelray | 0:56d6407653a7 | 134 | } |
michaelray | 0:56d6407653a7 | 135 | |
michaelray | 0:56d6407653a7 | 136 | void parseLine() { |
c1728p9 | 4:0ee729b4a211 | 137 | bool parse_gga = false; |
c1728p9 | 4:0ee729b4a211 | 138 | |
c1728p9 | 4:0ee729b4a211 | 139 | // Atomically copy the line buffer since the ISR can change it at any time |
c1728p9 | 4:0ee729b4a211 | 140 | core_util_critical_section_enter(); |
c1728p9 | 4:0ee729b4a211 | 141 | if (_last_line_updated) { |
c1728p9 | 4:0ee729b4a211 | 142 | char *buf_saved = _isr_line_bufs[_first_line_in_use ? 1 : 0]; |
c1728p9 | 4:0ee729b4a211 | 143 | strcpy(_last_line, buf_saved); |
c1728p9 | 4:0ee729b4a211 | 144 | parse_gga = true; |
c1728p9 | 4:0ee729b4a211 | 145 | _last_line_updated = false; |
c1728p9 | 4:0ee729b4a211 | 146 | } |
c1728p9 | 4:0ee729b4a211 | 147 | core_util_critical_section_exit(); |
c1728p9 | 4:0ee729b4a211 | 148 | |
c1728p9 | 4:0ee729b4a211 | 149 | if (parse_gga) { |
c1728p9 | 4:0ee729b4a211 | 150 | parseGGA(); |
c1728p9 | 4:0ee729b4a211 | 151 | } |
michaelray | 0:56d6407653a7 | 152 | } |
michaelray | 0:56d6407653a7 | 153 | |
michaelray | 0:56d6407653a7 | 154 | void parseGGA() { |
c1728p9 | 4:0ee729b4a211 | 155 | char *line_pos = _last_line; |
michaelray | 0:56d6407653a7 | 156 | for (int i=0; i<14; i++) { |
michaelray | 0:56d6407653a7 | 157 | if (i==0) { // NMEA Tag |
michaelray | 0:56d6407653a7 | 158 | } else if (i==1) { // UTC time |
c1728p9 | 4:0ee729b4a211 | 159 | gps_gga.utc_time = strtod(line_pos, 0); |
anthonyv | 6:dffd55375288 | 160 | |
anthonyv | 6:dffd55375288 | 161 | |
michaelray | 0:56d6407653a7 | 162 | } else if (i==2) { // Latitude |
c1728p9 | 4:0ee729b4a211 | 163 | gps_gga.latitude = strtod(line_pos, 0); |
anthonyv | 6:dffd55375288 | 164 | gps_gga.latitude=convertGPSToDecimal(gps_gga.latitude); |
anthonyv | 6:dffd55375288 | 165 | |
michaelray | 0:56d6407653a7 | 166 | } else if (i==3) { // Latitude North/South indicator |
c1728p9 | 4:0ee729b4a211 | 167 | gps_gga.ns_indicator = line_pos[0]; |
michaelray | 0:56d6407653a7 | 168 | } else if (i==4) { // Longitude |
c1728p9 | 4:0ee729b4a211 | 169 | gps_gga.longitude = strtod(line_pos, 0); |
anthonyv | 6:dffd55375288 | 170 | gps_gga.longitude=convertGPSToDecimal(gps_gga.longitude); |
michaelray | 0:56d6407653a7 | 171 | } else if (i==5) { // Longitude indicator |
c1728p9 | 4:0ee729b4a211 | 172 | gps_gga.ew_indicator = line_pos[0]; |
michaelray | 0:56d6407653a7 | 173 | } else if (i==6) { |
anthonyv | 6:dffd55375288 | 174 | gps_gga.position_fix= strtod(line_pos, 0); |
anthonyv | 6:dffd55375288 | 175 | } |
anthonyv | 6:dffd55375288 | 176 | else if (i==7) { |
anthonyv | 6:dffd55375288 | 177 | gps_gga.sats_used= strtod(line_pos, 0);//nb satellite used |
michaelray | 0:56d6407653a7 | 178 | } |
anthonyv | 6:dffd55375288 | 179 | else if (i==8) { |
anthonyv | 6:dffd55375288 | 180 | gps_gga.hdop= strtod(line_pos, 0);//horizontal precision |
anthonyv | 6:dffd55375288 | 181 | } |
anthonyv | 6:dffd55375288 | 182 | else if (i==9) { |
anthonyv | 6:dffd55375288 | 183 | gps_gga.msl_altitude= strtod(line_pos, 0);//altitute |
anthonyv | 6:dffd55375288 | 184 | } |
anthonyv | 6:dffd55375288 | 185 | |
anthonyv | 6:dffd55375288 | 186 | |
c1728p9 | 4:0ee729b4a211 | 187 | line_pos = strchr(line_pos, ','); |
c1728p9 | 4:0ee729b4a211 | 188 | if (line_pos == NULL) { |
c1728p9 | 4:0ee729b4a211 | 189 | break; |
c1728p9 | 4:0ee729b4a211 | 190 | } |
c1728p9 | 4:0ee729b4a211 | 191 | line_pos += 1; |
michaelray | 0:56d6407653a7 | 192 | } |
michaelray | 0:56d6407653a7 | 193 | } |
michaelray | 0:56d6407653a7 | 194 | }; |
michaelray | 0:56d6407653a7 | 195 | |
sarahmarshy | 1:0607ba3aa02d | 196 | #endif |