GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 99.4% 173 / 0 / 174
Functions: 100.0% 9 / 0 / 9
Branches: 89.1% 57 / 0 / 64

driver/gps/gps.c
Line Branch Exec Source
1 /*******************************************************************************
2 *
3 * FILE:
4 * gps.c
5 *
6 * DESCRIPTION:
7 * Contains API functions for GPS
8 *
9 * COPYRIGHT:
10 * Copyright (c) 2025 Sun Devil Rocketry.
11 * All rights reserved.
12 *
13 * This software is licensed under terms that can be found in the LICENSE
14 * file in the root directory of this software component.
15 * If no LICENSE file comes with this software, it is covered under the
16 * BSD-3-Clause.
17 *
18 * https://opensource.org/license/bsd-3-clause
19 *
20 *******************************************************************************/
21
22
23 /*------------------------------------------------------------------------------
24 Standard Includes
25 ------------------------------------------------------------------------------*/
26
27
28 /*------------------------------------------------------------------------------
29 MCU Pins
30 ------------------------------------------------------------------------------*/
31 #if defined( FLIGHT_COMPUTER )
32 #include "sdr_pin_defines_A0002.h"
33 #include "stm32h7xx_hal.h"
34 #elif defined( ENGINE_CONTROLLER )
35 #include "sdr_pin_defines_L0002.h"
36 #elif defined( VALVE_CONTROLLER )
37 #include "sdr_pin_defines_L0005.h"
38 #elif defined( GROUND_STATION )
39 #include "sdr_pin_defines_A0005.h"
40 #elif defined( FLIGHT_COMPUTER_LITE )
41 #include "sdr_pin_defines_A0007.h"
42 #elif defined( A0010 )
43 #include "sdr_pin_defines_A0010.h"
44 #endif
45
46
47 /*------------------------------------------------------------------------------
48 Project Includes
49 ------------------------------------------------------------------------------*/
50 #include "main.h"
51 #include "gps.h"
52 #include <string.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <stdint.h>
56
57 /*------------------------------------------------------------------------------
58 Preprocesor Directives
59 ------------------------------------------------------------------------------*/
60
61 /*------------------------------------------------------------------------------
62 Internal function prototypes
63 ------------------------------------------------------------------------------*/
64 static GPS_NMEA_MSG_TYPE parse_token
65 (
66 char* nmea_token
67 );
68
69 /*------------------------------------------------------------------------------
70 Local Variables
71 ------------------------------------------------------------------------------*/
72 static GPS_DATA gps_last_valid_rmc_data;
73 static GPS_DATA gps_last_valid_gll_data;
74
75 /*------------------------------------------------------------------------------
76 Procedures
77 ------------------------------------------------------------------------------*/
78
79
80 /*******************************************************************************
81 * *
82 * PROCEDURE: *
83 * gps_transmit *
84 * *
85 * DESCRIPTION: *
86 * transmits a specified number of bytes over USB *
87 * *
88 *******************************************************************************/
89 4 GPS_STATUS gps_transmit
90 (
91 void* tx_data_ptr , /* Data to be sent */
92 size_t tx_data_size, /* Size of transmit data */
93 uint32_t timeout /* UART timeout */
94 )
95 {
96 /*------------------------------------------------------------------------------
97 Local Variables
98 ------------------------------------------------------------------------------*/
99 4 HAL_StatusTypeDef gps_status;
100
101
102 /*------------------------------------------------------------------------------
103 API Function Implementation
104 ------------------------------------------------------------------------------*/
105
106 /* Transmit byte */
107 4 gps_status = HAL_UART_Transmit( &( GPS_HUART ),
108 tx_data_ptr ,
109 tx_data_size ,
110 timeout );
111
112 /* Return HAL status */
113 4 if ( gps_status != HAL_OK )
114 {
115 return gps_status;
116 }
117 else
118 {
119 4 return GPS_OK;
120 }
121
122 } /* usb_transmit */
123
124
125 /*******************************************************************************
126 * *
127 * PROCEDURE: *
128 * usb_recieve *
129 * *
130 * DESCRIPTION: *
131 * Receives bytes from the USB port *
132 * *
133 *******************************************************************************/
134 4 GPS_STATUS gps_receive
135 (
136 void* rx_data_ptr , /* Buffer to export data to */
137 size_t rx_data_size, /* Size of the data to be received */
138 uint32_t timeout /* UART timeout */
139 )
140 {
141 /*------------------------------------------------------------------------------
142 Local Variables
143 ------------------------------------------------------------------------------*/
144 4 HAL_StatusTypeDef gps_status;
145
146
147 /*------------------------------------------------------------------------------
148 API Function Implementation
149 ------------------------------------------------------------------------------*/
150
151 /* Transmit byte */
152 4 gps_status = HAL_UART_Receive( &( GPS_HUART ),
153 rx_data_ptr ,
154 rx_data_size ,
155 timeout );
156 /* Return HAL status */
157
3/3
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
4 switch ( gps_status )
158 {
159 case HAL_TIMEOUT:
160 {
161 return GPS_TIMEOUT;
162 1 break;
163 }
164 1 case HAL_OK:
165 {
166 1 return GPS_OK;
167 2 break;
168 }
169 2 default:
170 {
171 2 return GPS_FAIL;
172 4 break;
173 }
174 }
175
176 } /* usb_receive */
177
178
179 /*******************************************************************************
180 * *
181 * PROCEDURE: *
182 * usb_recieve_IT *
183 * *
184 * DESCRIPTION: *
185 * Receives bytes from the USB port *
186 * *
187 *******************************************************************************/
188 126 GPS_STATUS gps_receive_IT
189 (
190 uint8_t* rx_data_ptr , /* Buffer to export data to */
191 size_t rx_data_size /* Size of the data to be received */
192 )
193 {
194 /*------------------------------------------------------------------------------
195 Local Variables
196 ------------------------------------------------------------------------------*/
197 126 HAL_StatusTypeDef gps_status;
198
199
200 /*------------------------------------------------------------------------------
201 API Function Implementation
202 ------------------------------------------------------------------------------*/
203
204 /* Transmit byte */
205 126 gps_status = HAL_UART_Receive_IT( &( GPS_HUART ),
206 rx_data_ptr ,
207 rx_data_size );
208
209 /* Return HAL status */
210
3/3
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
126 switch ( gps_status )
211 {
212 case HAL_TIMEOUT:
213 {
214 return GPS_TIMEOUT;
215 123 break;
216 }
217 123 case HAL_OK:
218 {
219 123 return GPS_OK;
220 2 break;
221 }
222 2 default:
223 {
224 2 return GPS_FAIL;
225 126 break;
226 }
227 }
228
229 } /* usb_receive_IT */
230
231 /*******************************************************************************
232 * *
233 * PROCEDURE: *
234 * gps_mesg_validate *
235 * *
236 * DESCRIPTION: *
237 * Validate message returned from GPS *
238 * *
239 * TEST: *
240 * test_gps_mesg_validate provides coverage. If this function *
241 * is updated, make sure the test cases are updated to match. *
242 * *
243 *******************************************************************************/
244 127 int gps_mesg_validate(char *nmeastr){
245 127 char check[3];
246 127 char checkcalcstr[3];
247 127 int i;
248 127 int calculated_check;
249
250 127 i=0;
251 127 calculated_check=0;
252
253 // check to ensure that the string starts with a $
254
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 1 time.
127 if(nmeastr[i] == '$')
255 i++;
256 else
257 return 0;
258
259 //No NULL reached, 75 char largest possible NMEA message, no '*' reached
260
4/4
✓ Branch 0 taken 7165 times.
✓ Branch 1 taken 125 times.
✓ Branch 2 taken 7164 times.
✓ Branch 3 taken 1 time.
7290 while((nmeastr[i] != 0) && (nmeastr[i] != '*') && (i < 75)){
261 7164 calculated_check ^= nmeastr[i];// calculate the checksum
262 7164 i++;
263 }
264
265
2/2
✓ Branch 0 taken 125 times.
✓ Branch 1 taken 1 time.
126 if(i >= 75){
266 return 0;// the string was too long so return an error
267 }
268
269
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 1 time.
125 if (nmeastr[i] == '*'){
270 124 check[0] = nmeastr[i+1]; //put hex chars in check string
271 124 check[1] = nmeastr[i+2];
272 124 check[2] = 0;
273 }
274 else
275 return 0;// no checksum separator found there for invalid
276
277
1/2
✓ Branch 0 taken 124 times.
✗ Branch 1 not taken.
124 sprintf(checkcalcstr,"%02X",calculated_check);
278 124 return((checkcalcstr[0] == check[0])
279
2/4
✓ Branch 0 taken 124 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 124 times.
124 && (checkcalcstr[1] == check[1])) ? 1 : 0 ;
280 } /*gps_mesg_validate*/
281
282 /*******************************************************************************
283 * *
284 * PROCEDURE: *
285 * GPS_parse *
286 * *
287 * DESCRIPTION: *
288 * Convert raw NMEA string to usable data *
289 * *
290 * TEST: *
291 * test_GPS_parse provides coverage. If this function or its *
292 * helpers are updated, make sure the test cases are updated to match. *
293 * *
294 *******************************************************************************/
295 140 void GPS_parse
296 (
297 GPS_DATA* gps_ptr,
298 char *GPSstrParse
299 )
300 {
301 /* Get message type */
302 140 char token[8]; // Needs to be 8 chars for memory alignment
303
5/5
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 11 times.
140 strncpy(token, GPSstrParse, 6);
304 140 token[6] = '\0';
305 140 int idx = 7; /* Skips "$GPXXX,"*/
306
307 140 GPS_NMEA_MSG_TYPE message_type = parse_token(token);
308
309 /* Parse by message type */
310
5/5
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 11 times.
140 switch ( message_type )
311 {
312 24 case NMEA_MSG_GGA:
313 24 gps_ptr->utc_time = gps_string_to_float(GPSstrParse, &idx);
314 24 gps_ptr->nmea_latitude = gps_string_to_float(GPSstrParse, &idx);
315 24 gps_ptr->ns = gps_string_to_char(GPSstrParse, &idx);
316 24 gps_ptr->nmea_longitude = gps_string_to_float(GPSstrParse, &idx);
317 24 gps_ptr->ew = gps_string_to_char(GPSstrParse, &idx);
318 24 gps_ptr->lock = (int)gps_string_to_float(GPSstrParse, &idx) + 0.5;
319 24 gps_ptr->satelites = (int)(gps_string_to_float(GPSstrParse, &idx) + 0.5); // This is a decimal number.
320 24 gps_ptr->hdop = gps_string_to_float(GPSstrParse, &idx);
321 24 gps_ptr->msl_altitude = gps_string_to_float(GPSstrParse, &idx);
322 24 gps_ptr->msl_units = gps_string_to_char(GPSstrParse, &idx);
323 24 gps_conv_latitude_longitude( gps_ptr );
324 24 break;
325 58 case NMEA_MSG_RMC:
326 /* Parse data */
327 {
328 58 float utc_time = gps_string_to_float(GPSstrParse, &idx);
329 58 char rmc_status = gps_string_to_char(GPSstrParse, &idx);
330 58 float nmea_latitude = gps_string_to_float(GPSstrParse, &idx);
331 58 char ns = gps_string_to_char(GPSstrParse, &idx);
332 58 float nmea_longitude = gps_string_to_float(GPSstrParse, &idx);
333 58 char ew = gps_string_to_char(GPSstrParse, &idx);
334 58 float speed_k = gps_string_to_float(GPSstrParse, &idx);
335 58 float course_d = gps_string_to_float(GPSstrParse, &idx);
336 58 int date = (int)(0.5 + gps_string_to_float(GPSstrParse, &idx));
337
338 /* Save status */
339 58 gps_ptr->rmc_status = rmc_status;
340
341 /* Save rest of data only if status is A: Active */
342
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 2 times.
58 if ( rmc_status == 'A' )
343 {
344 56 gps_ptr->utc_time = utc_time;
345 56 gps_ptr->nmea_latitude = nmea_latitude;
346 56 gps_ptr->ns = ns;
347 56 gps_ptr->nmea_longitude = nmea_longitude;
348 56 gps_ptr->ew = ew;
349 56 gps_ptr->speed_k = speed_k;
350 56 gps_ptr->course_d = course_d;
351 56 gps_ptr->date = date;
352 56 gps_conv_latitude_longitude( gps_ptr );
353
354 /* Update last valid data */
355 56 gps_last_valid_rmc_data = *gps_ptr;
356 }
357 /* Discard new data, use last valid data instead if the status is V: Void */
358
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 else if ( rmc_status == 'V' )
359 {
360 2 *gps_ptr = gps_last_valid_rmc_data;
361
362 /* Re-save rmc_status since the previous statement may override it */
363 2 gps_ptr->rmc_status = rmc_status;
364 }
365 }
366 break;
367 25 case NMEA_MSG_GLL:
368 {
369 /* Parse data */
370 25 float nmea_latitude = gps_string_to_float(GPSstrParse, &idx);
371 25 char ns = gps_string_to_char(GPSstrParse, &idx);
372 25 float nmea_longitude = gps_string_to_float(GPSstrParse, &idx);
373 25 char ew = gps_string_to_char(GPSstrParse, &idx);
374 25 float utc_time = gps_string_to_float(GPSstrParse, &idx);
375 25 char gll_status = gps_string_to_char(GPSstrParse, &idx);
376
377 /* Store status */
378 25 gps_ptr->gll_status = gll_status;
379
380 /* Save rest of data only if status is A: Data Valid */
381
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 3 times.
25 if (gll_status == 'A')
382 {
383 22 gps_ptr->nmea_latitude = nmea_latitude;
384 22 gps_ptr->ns = ns;
385 22 gps_ptr->nmea_longitude = nmea_longitude;
386 22 gps_ptr->ew = ew;
387 22 gps_ptr->utc_time = utc_time;
388 22 gps_conv_latitude_longitude( gps_ptr );
389
390 22 gps_last_valid_gll_data = *gps_ptr;
391 }
392
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 else if (gll_status == 'V')
393 {
394 2 *gps_ptr = gps_last_valid_gll_data;
395
396 /* Resave gll status since the previous statement may override it */
397 2 gps_ptr->gll_status = gll_status;
398 }
399 }
400 break;
401 22 case NMEA_MSG_VTG:
402 22 gps_ptr->course_t = gps_string_to_float(GPSstrParse, &idx);
403 22 gps_ptr->course_t_unit = gps_string_to_char(GPSstrParse, &idx);
404 22 gps_ptr->course_m = gps_string_to_float(GPSstrParse, &idx);
405 22 gps_ptr->course_m_unit = gps_string_to_char(GPSstrParse, &idx);
406 22 gps_ptr->speed_k = gps_string_to_float(GPSstrParse, &idx);
407 22 gps_ptr->speed_k_unit = gps_string_to_char(GPSstrParse, &idx);
408 22 gps_ptr->speed_km = gps_string_to_float(GPSstrParse, &idx);
409 22 gps_ptr->speed_km_unit = gps_string_to_char(GPSstrParse, &idx);
410 22 break;
411 default:
412 break; /* Ignore */
413 }
414 140 } /* GPS_parse */
415
416 /*******************************************************************************
417 * *
418 * PROCEDURE: *
419 * gps_string_to_float *
420 * *
421 * DESCRIPTION: *
422 * Convert part of an NMEA string to a float *
423 * *
424 * TEST: *
425 * test_GPS_parse provides coverage. If this function is *
426 * updated, please re-run the test and update if necessary *
427 * *
428 *******************************************************************************/
429 679 float gps_string_to_float(char *GPSstrParse, int* inputIdx)
430 {
431 679 int idx = *inputIdx;
432 679 char currChar = GPSstrParse[idx];
433 679 char tempstr[16];
434 679 int tempidx = 0;
435
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 654 times.
679 if (GPSstrParse[idx] == ',') /* Checks if subsequent comma */
436 {
437 25 *inputIdx = *inputIdx + 1;
438 25 return 0.0f; /* null return */
439 }
440
2/2
✓ Branch 0 taken 4614 times.
✓ Branch 1 taken 654 times.
5268 while (currChar != ',')
441 {
442
1/2
✓ Branch 0 taken 4614 times.
✗ Branch 1 not taken.
4614 if (tempidx > 15)
443 {
444 /* ERROR HANDLING */
445 // maybe just exit loop? and deal with bad data? or make it null.
446 return 0.0f;
447 }
448 4614 tempstr[tempidx] = GPSstrParse[idx];
449 4614 tempidx++;
450 4614 idx++;
451 4614 currChar = GPSstrParse[idx];
452 }
453 654 *inputIdx = idx + 1;
454 654 tempstr[tempidx] = '\0';
455 654 return strtof(tempstr, NULL);
456 } /* gps_string_to_float */
457
458 /*******************************************************************************
459 * *
460 * PROCEDURE: *
461 * gps_string_to_char *
462 * *
463 * DESCRIPTION: *
464 * Get a char from an NMEA string if it exists *
465 * *
466 * TEST: *
467 * test_GPS_parse provides coverage. If this function is *
468 * updated, please re-run the test and update if necessary *
469 * *
470 *******************************************************************************/
471 409 char gps_string_to_char(char *GPSstrParse, int* inputIdx)
472 {
473 409 int idx = *inputIdx;
474
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 406 times.
409 if (GPSstrParse[idx] == ',') /* Checks if subsequent comma */
475 {
476 3 *inputIdx = *inputIdx + 1;
477 3 return 0; /* null return */
478 }
479 else
480 {
481 406 *inputIdx = *inputIdx + 2;
482 406 return GPSstrParse[idx];
483 }
484 } /* gps_string_to_char */
485
486 /*******************************************************************************
487 * *
488 * PROCEDURE: *
489 * gps_conv_latitude_longitude *
490 * *
491 * DESCRIPTION: *
492 * Convert latitude and longitude from NMEA strings to the standard *
493 * format. *
494 * *
495 * TEST: *
496 * test_GPS_parse provides coverage. If this function is *
497 * updated, please re-run the test and update if necessary *
498 * *
499 *******************************************************************************/
500 102 void gps_conv_latitude_longitude( GPS_DATA* data )
501 {
502 /* Initialize variables */
503 102 uint8_t latitude_deg; /* Range: 0 to 90 */
504 102 uint8_t longitude_deg; /* Range: 0 to 180 */
505 102 float latitude;
506 102 float longitude;
507
508 /* Compute the degrees. Dividing by 100 drops the last two digits before the decimal.
509 Truncate -- do not round */
510 102 latitude_deg = (uint8_t)(data->nmea_latitude / 100);
511 102 longitude_deg = (uint8_t)(data->nmea_longitude / 100);
512
513 /* Compute the minutes, divide by 100 to move after the decimal */
514 102 latitude = (data->nmea_latitude - (100 * latitude_deg)) / 60;
515 102 longitude = (data->nmea_longitude - (100 * longitude_deg)) / 60;
516
517 /* Add the degrees */
518 102 latitude += (float)latitude_deg;
519 102 longitude += (float)longitude_deg;
520
521 /* Compute the sign */
522
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 101 times.
102 if (data->ns == 'S') {
523 1 latitude = -latitude;
524 }
525
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 78 times.
102 if (data->ew == 'W') {
526 24 longitude = -longitude;
527 }
528
529 /* Set the values in the struct */
530 102 data->dec_latitude = latitude;
531 102 data->dec_longitude = longitude;
532 102 } /* gps_conv_latitude_longitude */
533
534
535 /*------------------------------------------------------------------------------
536 Internal procedures
537 ------------------------------------------------------------------------------*/
538
539 140 static GPS_NMEA_MSG_TYPE parse_token
540 (
541 char* nmea_token
542 )
543 {
544 140 GPS_NMEA_MSG_TYPE message_type;
545
546 /* Skip '$' and talker ID */
547 140 char* sentence_id = nmea_token + 3;
548
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 24 times.
140 if ( !strncmp(sentence_id, "GGA", 3) ) message_type = NMEA_MSG_GGA;
549
2/2
✓ Branch 0 taken 91 times.
✓ Branch 1 taken 25 times.
116 else if ( !strncmp(sentence_id, "GLL", 3) ) message_type = NMEA_MSG_GLL;
550
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 11 times.
91 else if ( !strncmp(sentence_id, "GSA", 3) ) message_type = NMEA_MSG_GSA;
551
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 else if ( !strncmp(sentence_id, "GSV", 3) ) message_type = NMEA_MSG_GSV;
552
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 58 times.
80 else if ( !strncmp(sentence_id, "RMC", 3) ) message_type = NMEA_MSG_RMC;
553
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 else if ( !strncmp(sentence_id, "VTG", 3) ) message_type = NMEA_MSG_VTG;
554 ✗ else message_type = NMEA_MSG_UNSUPPORTED;
555
556 140 return message_type;
557
558 }
559
560 /*******************************************************************************
561 * END OF FILE *
562 *******************************************************************************/
563