GPS Device
Loading...
Searching...
No Matches
neo6.c
Go to the documentation of this file.
1
11
12#include <stdint.h>
13
14#include "neo6.h"
15#include "cmd.h"
16#include "common.h"
17#include "err.h"
18#include "init.h"
19#include "log.h"
20#include "modules.h"
21#include "stm32f401re_gpio.h"
22#include "stm32f401re_usart.h"
23#include "cbuf.h"
24#include "configuration.h"
25
26/* ---- VTG sentence field positions ---- */
27#define VTG_TMG_T_DEGREE_POS 1
28#define VTG_TMG_T_POS 2
29#define VTG_TMG_M_DEGREE_POS 3
30#define VTG_TMG_M_POS 4
31#define VTG_SPEED_KNOTS_POS 5
32#define VTG_SPEED_N_POS 6
33#define VTG_SPEED_OVER_GROUND_POS 7
34#define VTG_SPEED_OVER_GROUND_K_POS 8
35#define VTG_MODE_INDICATOR_POS 9
36
37/* ---- GGA sentence field positions ---- */
38#define GGA_UTC_POS 1
39#define GGA_LAT_POS 2
40#define GGA_LAT_DIR_POS 3
41#define GGA_LON_POS 4
42#define GGA_LON_DIR_POS 5
43#define GGA_QUALITY_FIX_POS 6
44#define GGA_NUM_SATS_POS 7
45#define GGA_HDOP_POS 8
46#define GGA_ORTH_HEIGHT_POS 9
47#define GGA_ORTH_HEIGHT_UNIT_POS 10
48#define GGA_GEOID_SEP_POS 11
49#define GGA_GEOID_SEP_UNIT_POS 12
50#define GGA_DATA_REC_AGE_POS 13
51#define GGA_REF_STAT_ID_POS 14
52
53/* ---- GSA sentence field positions ---- */
54#define GSA_MODE_POS 1
55#define GSA_FIX_TYPE_POS 2
56#define GSA_PRN_NUM_POS 3
57#define GSA_PDOP_POS 15
58#define GSA_HDOP_POS 16
59#define GSA_VDOP_POS 17
60
61/* ---- GSV sentence field positions ---- */
62#define GSV_NUM_OF_MESSAGES_POS 1
63#define GSV_NUM_OF_THIS_MESSAGE_POS 2
64#define GSV_NUM_SATS_VISIBLE_POS 3
65#define GSV_SV_PRN_NUM_POS 4
66#define GSV_ELEVATION_POS 5
67#define GSV_AZIMUTH_POS 6
68#define GSV_SNR_POS 7
69
75static uint8_t usart_data_recv_cbuf_mem[128];
76
83static uint8_t nmea_cbuf_mem[128];
84
118
126struct internal_state_s {
129
132
135
138
141
144
147
149 bool initialized;
150
152 bool started;
153};
154
157
158/* ---- Forward declarations for internal helpers ---- */
159static ERR_te neo6_calc_checksum(char *msg, uint32_t msg_len, uint8_t *checksum_o);
160static ERR_te neo6_process_msg(uint8_t *msg, uint32_t msg_len);
161static ERR_te neo6_process_rmc(char **tokens);
162static ERR_te neo6_process_vtg(char **tokens);
163static ERR_te neo6_process_gga(char **tokens);
164static ERR_te neo6_process_gsa(char **tokens);
165static ERR_te neo6_process_gsv(char **tokens);
166
167/* ---- Forward declaration for command handler ---- */
168static ERR_te neo6_dumpnmea_handler(uint32_t argc, char **argv);
169
178 {
179 .name = "dumpnmea",
180 .help = "Dumps raw NMEA messages, usage: neo6 dumpnmea <true|false>",
181 .handler = neo6_dumpnmea_handler
182 },
183};
184
193 .cmds_ptr = neo6_cmds,
194 .num_cmds = sizeof(neo6_cmds) / sizeof(neo6_cmds[0]),
195 .name = "neo6",
196 .log_level_ptr = &internal_state.log_level
197};
198
203
206 ERR_te err;
207
208 if(internal_state.initialized) {
210 }
211
212 internal_state = (struct internal_state_s){ 0 };
213 internal_state.log_level = LOG_LEVEL_INFO;
215 internal_state.usart_data_recv_cbuf.ptr = usart_data_recv_cbuf_mem;
216 internal_state.usart_data_recv_cbuf.size = sizeof(usart_data_recv_cbuf_mem);
217 internal_state.nmea_cbuf.ptr = nmea_cbuf_mem;
218 internal_state.nmea_cbuf.size = sizeof(nmea_cbuf_mem);
219 internal_state.dump_raw_nmea = false;
220 internal_state.initialized = true;
221 internal_state.started = false;
222
223 init_log();
224
226 if(err != ERR_OK) {
227 LOG_ERROR(
228 internal_state.subsys,
229 internal_state.log_level,
230 "neo6_init_subsys: cmd_register error"
231 );
232
233 return err;
234 }
235
236 LOG_INFO(
237 internal_state.subsys,
238 internal_state.log_level,
239 "neo6_init_subsys: subsys initialized"
240 );
241
242 return ERR_OK;
243}
244
247 if(internal_state.initialized && !internal_state.started) {
248 internal_state = (struct internal_state_s){ 0 };
249
251 if(err != ERR_OK) {
252 return err;
253 }
254 }
255 else {
256 LOG_ERROR(
257 internal_state.subsys,
258 internal_state.log_level,
259 "neo6_deinit_subsys: subsys is not initialized or stopped"
260 );
261
263 }
264
265 LOG_INFO(
266 internal_state.subsys,
267 internal_state.log_level,
268 "neo6_deinit_subsys: subsystem deinitialized"
269 );
270 return ERR_OK;
271}
272
275 if(internal_state.initialized && !internal_state.started) {
276 internal_state.started = true;
277
278 LOG_INFO(
279 internal_state.subsys,
280 internal_state.log_level,
281 "neo6_start_subsys: subsys started"
282 );
283 }
284 else {
285 LOG_ERROR(
286 internal_state.subsys,
287 internal_state.log_level,
288 "neo6_start_subsys: subsys not initialized or already started"
289 );
290
291 return ERR_UNKNOWN;
292 }
293
294 return ERR_OK;
295}
296
299 if(internal_state.initialized && internal_state.started) {
300 internal_state.started = false;
301
302 LOG_INFO(
303 internal_state.subsys,
304 internal_state.log_level,
305 "neo6_stop_subsys: subsys stopped"
306 );
307 }
308 else {
309 LOG_ERROR(
310 internal_state.subsys,
311 internal_state.log_level,
312 "neo6_stop_subsys: subsys not initialized or already already stopped"
313 );
314
315 return ERR_UNKNOWN;
316 }
317
318 return ERR_OK;
319}
320
323 if(internal_state.neo6_handle.initialized == true) {
324 LOG_ERROR(
325 internal_state.subsys,
326 internal_state.log_level,
327 "neo6_init_handle: handle already initialized"
328 );
329
331 }
332
333 if(neo6_cfg->usart_instance == (void*)0) {
334 LOG_ERROR(
335 internal_state.subsys,
336 internal_state.log_level,
337 "neo6_init_handle: No USART peripheral given"
338 );
339
341 }
342 else if(neo6_cfg->rx_gpio_port == (void*)0 || neo6_cfg->tx_gpio_port == (void*)0) {
343 LOG_ERROR(
344 internal_state.subsys,
345 internal_state.log_level,
346 "neo6_init_handle: No GPIO peripheral given"
347 );
348
350 }
351
352 internal_state.neo6_handle.usart_instance = neo6_cfg->usart_instance;
353 internal_state.neo6_handle.usart_baud_rate = neo6_cfg->usart_baud_rate;
354 internal_state.neo6_handle.rx_gpio_port = neo6_cfg->rx_gpio_port;
355 internal_state.neo6_handle.tx_gpio_port = neo6_cfg->tx_gpio_port;
356 internal_state.neo6_handle.rx_gpio_pin = neo6_cfg->rx_gpio_pin;
357 internal_state.neo6_handle.tx_gpio_pin = neo6_cfg->tx_gpio_pin;
358 internal_state.neo6_handle.gpio_alternate_function = neo6_cfg->gpio_alternate_function;
359
360 USART_CFG_ts neo6_usart = { 0 };
361 neo6_usart.instance = neo6_cfg->usart_instance;
362 neo6_usart.baud_rate = neo6_cfg->usart_baud_rate;
364 usart_init(&neo6_usart);
366
367 GPIO_CFG_ts neo6_rx = { 0 };
368 neo6_rx.port = neo6_cfg->rx_gpio_port;
369 neo6_rx.pin = neo6_cfg->rx_gpio_pin;
372 gpio_init(&neo6_rx);
373
374 GPIO_CFG_ts neo6_tx = { 0 };
375 neo6_tx.port = neo6_cfg->tx_gpio_port;
376 neo6_tx.pin = neo6_cfg->tx_gpio_pin;
379 gpio_init(&neo6_tx);
380
381 internal_state.neo6_handle.initialized = true;
382
383 return ERR_OK;
384}
385
388 uint8_t data_len = 0;
389 cbuf_len(&internal_state.usart_data_recv_cbuf, &data_len);
390
391 if(data_len) {
392 uint8_t data[data_len];
393
394 cbuf_read(&internal_state.usart_data_recv_cbuf, data);
395
396 cbuf_write(&internal_state.nmea_cbuf, data, data_len);
397
398 for(uint32_t i = 0; i < data_len; i++) {
399 if(data[i] == '\n') {
400 uint8_t nmea_len = 0;
401 cbuf_len(&internal_state.nmea_cbuf, &nmea_len);
402
403 uint8_t nmea_msg[nmea_len];
404 cbuf_read(&internal_state.nmea_cbuf, nmea_msg);
405
406 nmea_msg[nmea_len - 2] = '\0';
407 nmea_msg[nmea_len - 1] = '\0';
408
409 if(internal_state.dump_raw_nmea == true) {
410 LOG_INFO(
411 internal_state.subsys,
412 internal_state.log_level,
413 "%s", nmea_msg
414 );
415 }
416
417 // Process NMEA message
418 ERR_te err = neo6_process_msg(nmea_msg, nmea_len);
419 if(err != ERR_OK) {
420 return err;
421 }
422 }
423 }
424 }
425
426 return ERR_OK;
427}
428
431 *neo6_info_o = &internal_state.neo6_info;
432
433 return ERR_OK;
434}
435
437
442
458static ERR_te neo6_calc_checksum(char *msg, uint32_t msg_len, uint8_t *checksum_o) {
459 uint8_t xor_val = 0;
460 uint32_t char_count = 0;
461
462 while(*msg != '*' && char_count < msg_len) {
463 if(*msg == '$') {
464 msg++;
465 continue;
466 }
467
468 xor_val ^= *msg;
469
470 msg++;
471 char_count++;
472
473 if(char_count == msg_len) {
474 return ERR_UNKNOWN;
475 }
476 }
477 *checksum_o = xor_val;
478
479 return ERR_OK;
480}
481
501static ERR_te neo6_process_msg(uint8_t *msg, uint32_t msg_len) {
502 ERR_te err;
503
504 uint8_t checksum_recv = ascii_hex_to_byte(msg[msg_len - 4], msg[msg_len - 3]);
505
506 uint8_t checksum_calc = 0;
507 err = neo6_calc_checksum((char*)msg, msg_len, &checksum_calc);
508 if(err != ERR_OK) {
509 return err;
510 }
511
512 char *tokens[CONFIG_NEO6_MAX_TOKENS];
513 uint16_t num_tokens = 0;
514
515 if(checksum_recv == checksum_calc) {
516 str_tokenize((char*)msg, ",", CONFIG_NEO6_MAX_TOKENS, tokens, &num_tokens);
517
518 if(str_cmp("$GPRMC", tokens[0]) == true) {
519 neo6_process_rmc(tokens);
520 }
521 else if(str_cmp("$GPVTG", tokens[0]) == true) {
522 neo6_process_vtg(tokens);
523 }
524 else if(str_cmp("$GPGGA", tokens[0]) == true) {
525 neo6_process_gga(tokens);
526 }
527 else if(str_cmp("$GPGSA", tokens[0]) == true) {
528 neo6_process_gsa(tokens);
529 }
530 else if(str_cmp("$GPGSV", tokens[0]) == true) {
531 neo6_process_gsv(tokens);
532 }
533 }
534 else {
536 }
537
538 return ERR_OK;
539}
540
556static ERR_te neo6_process_rmc(char **tokens) {
557 if(str_cmp(tokens[2], "A") == false) {
558 str_cpy(internal_state.neo6_info.time, "No data",
559 get_str_len("No data") + 1);
560
561 str_cpy(internal_state.neo6_info.date, "No data",
562 get_str_len("No data") + 1);
563
564 str_cpy(internal_state.neo6_info.lon, "No data",
565 get_str_len("No data") + 1);
566
567 str_cpy(internal_state.neo6_info.lat, "No data",
568 get_str_len("No data") + 1);
569
570 return ERR_UNKNOWN;
571 }
572
573 // UTC — format hhmmss → HH:MM:SS UTC
574 uint8_t time_len = 6;
575 int8_t real_counter = 0;
576 for(uint8_t j = 0; j < time_len; j++) {
577 internal_state.neo6_info.time[real_counter++] = tokens[1][j];
578
579 if((real_counter + 1) % 3 == 0 && real_counter <= 5) {
580 internal_state.neo6_info.time[real_counter++] = ':';
581 }
582 }
583 internal_state.neo6_info.time[real_counter++] = ' ';
584 internal_state.neo6_info.time[real_counter++] = 'U';
585 internal_state.neo6_info.time[real_counter++] = 'T';
586 internal_state.neo6_info.time[real_counter++] = 'C';
587 internal_state.neo6_info.time[real_counter++] = '\0';
588
589 // Latitude
590 uint8_t lat_txt_len = get_str_len(tokens[3]);
591 for(uint8_t j = 0; j < lat_txt_len; j++) {
592 internal_state.neo6_info.lat[j] = tokens[3][j];
593 }
594 internal_state.neo6_info.lat[lat_txt_len] = tokens[4][0];
595 internal_state.neo6_info.lat[lat_txt_len + 1] = '\0';
596
597 // Longitude
598 uint8_t lon_txt_len = get_str_len(tokens[5]);
599 for(uint8_t j = 0; j < lon_txt_len; j++) {
600 internal_state.neo6_info.lon[j] = tokens[5][j];
601 }
602 internal_state.neo6_info.lon[lon_txt_len] = tokens[6][0];
603 internal_state.neo6_info.lon[lon_txt_len + 1] = '\0';
604
605 // Date — format ddmmyy → DD/MM/20YY
606 internal_state.neo6_info.date[0] = tokens[9][0];
607 internal_state.neo6_info.date[1] = tokens[9][1];
608 internal_state.neo6_info.date[2] = '/';
609 internal_state.neo6_info.date[3] = tokens[9][2];
610 internal_state.neo6_info.date[4] = tokens[9][3];
611 internal_state.neo6_info.date[5] = '/';
612 internal_state.neo6_info.date[6] = '2';
613 internal_state.neo6_info.date[7] = '0';
614 internal_state.neo6_info.date[8] = tokens[9][4];
615 internal_state.neo6_info.date[9] = tokens[9][5];
616 internal_state.neo6_info.date[10] = '\0';
617
618 return ERR_OK;
619}
620
634static ERR_te neo6_process_vtg(char **tokens) {
635 // Movement direction (relative to true north)
636 uint8_t track_made_good_len = get_str_len(tokens[VTG_TMG_T_DEGREE_POS]);
637 if(track_made_good_len == 0) {
638 str_cpy(internal_state.neo6_info.mov_dir, "No data",
639 get_str_len("No data") + 1);
640 }
641 else {
642 str_cpy(internal_state.neo6_info.mov_dir, &tokens[VTG_TMG_T_DEGREE_POS][0],
643 track_made_good_len + 1);
644 }
645
646 // Movement speed (kph)
647 uint8_t kph_len = get_str_len(tokens[VTG_SPEED_OVER_GROUND_POS]);
648 if(kph_len == 0) {
649 str_cpy(internal_state.neo6_info.mov_speed, "No data",
650 get_str_len("No data") + 1);
651 }
652 else {
653 str_cpy(internal_state.neo6_info.mov_speed, &tokens[VTG_SPEED_OVER_GROUND_POS][0],
654 kph_len + 1);
655 internal_state.neo6_info.mov_speed[kph_len++] = ' ';
656 internal_state.neo6_info.mov_speed[kph_len++] = 'k';
657 internal_state.neo6_info.mov_speed[kph_len++] = 'p';
658 internal_state.neo6_info.mov_speed[kph_len++] = 'h';
659 internal_state.neo6_info.mov_speed[kph_len++] = '\0';
660 }
661
662 return ERR_OK;
663}
664
678static ERR_te neo6_process_gga(char **tokens) {
679 // Quality of the fix
680 uint8_t fix_status_len = get_str_len(tokens[GGA_QUALITY_FIX_POS]);
681 if(fix_status_len == 0) {
682 str_cpy(internal_state.neo6_info.fix_status, "No data",
683 get_str_len("No data") + 1);
684 }
685 else if(str_cmp(tokens[GGA_QUALITY_FIX_POS], "0") == true) {
686 char msg[] = "No valid fix";
687 str_cpy(internal_state.neo6_info.fix_status, msg, get_str_len(msg) + 1);
688 }
689 else if(str_cmp(tokens[GGA_QUALITY_FIX_POS], "1") == true) {
690 char msg[] = "GPS fix";
691 str_cpy(internal_state.neo6_info.fix_status, msg, get_str_len(msg) + 1);
692 }
693 else if(str_cmp(tokens[GGA_QUALITY_FIX_POS], "2") == true) {
694 char msg[] = "DGPS fix";
695 str_cpy(internal_state.neo6_info.fix_status, msg, get_str_len(msg) + 1);
696 }
697 else {
698 char msg[] = "?";
699 str_cpy(internal_state.neo6_info.fix_status, msg, get_str_len(msg) + 1);
700 }
701
702 // Number of satellites used
703 uint8_t num_sats_used_len = get_str_len(tokens[GGA_NUM_SATS_POS]);
704 if(num_sats_used_len == 0) {
705 str_cpy(internal_state.neo6_info.num_sats_used, "No data",
706 get_str_len("No data") + 1);
707 }
708 else {
709 str_cpy(internal_state.neo6_info.num_sats_used, tokens[GGA_NUM_SATS_POS],
710 get_str_len(tokens[GGA_NUM_SATS_POS]) + 1);
711 }
712
713 // Orthometric height
714 uint8_t ort_height_len = get_str_len(tokens[GGA_ORTH_HEIGHT_POS]);
715 if(ort_height_len == 0) {
716 str_cpy(internal_state.neo6_info.ort_height, "No data",
717 get_str_len("No data") + 1);
718 }
719 else {
720 str_cpy(internal_state.neo6_info.ort_height, tokens[GGA_ORTH_HEIGHT_POS],
721 ort_height_len + 1);
722 internal_state.neo6_info.ort_height[ort_height_len] = ' ';
723 internal_state.neo6_info.ort_height[ort_height_len + 1] = tokens[GGA_ORTH_HEIGHT_UNIT_POS][0];
724 internal_state.neo6_info.ort_height[ort_height_len + 2] = '\0';
725 }
726
727 // Geoid separation
728 uint8_t geoid_sep_len = get_str_len(tokens[GGA_GEOID_SEP_POS]);
729 if(geoid_sep_len == 0) {
730 str_cpy(internal_state.neo6_info.geoid_sep, "No data",
731 get_str_len("No data") + 1);
732 }
733 else {
734 str_cpy(internal_state.neo6_info.geoid_sep, tokens[GGA_GEOID_SEP_POS],
735 geoid_sep_len + 1);
736 internal_state.neo6_info.geoid_sep[geoid_sep_len] = ' ';
737 internal_state.neo6_info.geoid_sep[geoid_sep_len + 1] = tokens[GGA_GEOID_SEP_UNIT_POS][0];
738 internal_state.neo6_info.geoid_sep[geoid_sep_len + 2] = '\0';
739 }
740
741 return ERR_OK;
742}
743
757static ERR_te neo6_process_gsa(char **tokens) {
758 uint8_t msg_part_len = get_str_len(tokens[GSA_FIX_TYPE_POS]);
759 if(msg_part_len == 0) {
760 str_cpy(internal_state.neo6_info.fix_type, "No data",
761 get_str_len("No data") + 1);
762 }
763 else {
764 if(str_cmp(tokens[GSA_FIX_TYPE_POS], "1") == true) {
765 str_cpy(internal_state.neo6_info.fix_type, "Not available",
766 get_str_len("Not available") + 1);
767 }
768 else if(str_cmp(tokens[GSA_FIX_TYPE_POS], "2") == true) {
769 str_cpy(internal_state.neo6_info.fix_type, "2D",
770 get_str_len("2D") + 1);
771 }
772 else if(str_cmp(tokens[GSA_FIX_TYPE_POS], "3") == true) {
773 str_cpy(internal_state.neo6_info.fix_type, "3D",
774 get_str_len("3D") + 1);
775 }
776 }
777
778 msg_part_len = get_str_len(tokens[GSA_PDOP_POS]);
779 if(msg_part_len == 0) {
780 str_cpy(internal_state.neo6_info.pdop, "No data",
781 get_str_len("No data") + 1);
782 }
783 else {
784 str_cpy(internal_state.neo6_info.pdop, tokens[GSA_PDOP_POS],
785 msg_part_len + 1);
786 }
787
788 msg_part_len = get_str_len(tokens[GSA_HDOP_POS]);
789 if(msg_part_len == 0) {
790 str_cpy(internal_state.neo6_info.hdop, "No data",
791 get_str_len("No data") + 1);
792 }
793 else {
794 str_cpy(internal_state.neo6_info.hdop, tokens[GSA_HDOP_POS],
795 msg_part_len + 1);
796 }
797
798 // VDOP: subtract 2 to strip the trailing checksum suffix (*XX)
799 msg_part_len = get_str_len(tokens[GSA_VDOP_POS]) - 2;
800 if(msg_part_len == 0) {
801 str_cpy(internal_state.neo6_info.vdop, "No data",
802 get_str_len("No data") + 1);
803 }
804 else {
805 str_cpy(internal_state.neo6_info.vdop, tokens[GSA_VDOP_POS],
806 msg_part_len);
807 internal_state.neo6_info.vdop[msg_part_len] = '\0';
808 }
809
810 return ERR_OK;
811}
812
826static ERR_te neo6_process_gsv(char **tokens) {
827 uint8_t msg_part_len = get_str_len(tokens[GSV_NUM_SATS_VISIBLE_POS]);
828 if(msg_part_len == 0) {
829 str_cpy(internal_state.neo6_info.num_sats_all, "No data",
830 get_str_len("No data") + 1);
831 }
832 else {
833 str_cpy(internal_state.neo6_info.num_sats_all, tokens[GSV_NUM_SATS_VISIBLE_POS],
834 msg_part_len + 1);
835 }
836
837 return ERR_OK;
838}
839
841
846
864static ERR_te neo6_dumpnmea_handler(uint32_t argc, char **argv) {
865 if(argc != 3) {
866 LOG_ERROR(
867 internal_state.subsys,
868 internal_state.log_level,
869 "neo6_dumpnmea_handler: invalid arguments"
870 );
871
873 }
874
875 if(str_cmp(argv[2], "true") == true) {
876 internal_state.dump_raw_nmea = true;
877 }
878 else if(str_cmp(argv[2], "false") == true) {
879 internal_state.dump_raw_nmea = false;
880 }
881 else {
882 LOG_ERROR(
883 internal_state.subsys,
884 internal_state.log_level,
885 "neo6_dumpnmea_handler: invalid arguments"
886 );
887
889 }
890
891 return ERR_OK;
892}
893
895
909 cbuf_write(&internal_state.usart_data_recv_cbuf, &data, 1);
910}
static struct internal_state_s internal_state
Singleton instance of the SysTick driver internal state.
Circular buffer module public API.
Command subsystem public API.
Common utility module public API.
System-wide error code definitions.
ERR_te cbuf_read(CBUF_HANDLE_ts *cbuf_handle, uint8_t *output_buf_o)
Reads all available data from the circular buffer into an output buffer.
Definition cbuf.c:22
ERR_te cbuf_len(CBUF_HANDLE_ts const *cbuf_handle, uint8_t *len_o)
Returns the number of bytes currently stored in the circular buffer.
Definition cbuf.c:61
ERR_te cbuf_write(CBUF_HANDLE_ts *cbuf_handle, uint8_t *input_buf, uint32_t input_len)
Writes data from an input buffer into the circular buffer.
Definition cbuf.c:41
ERR_te cmd_deregister(CMD_CLIENT_INFO_ts const *cmd_client_info)
Deregisters a client from the command subsystem.
Definition cmd.c:66
ERR_te cmd_register(CMD_CLIENT_INFO_ts *cmd_client_info)
Registers a client with the command subsystem.
Definition cmd.c:51
bool str_cmp(const char *str1, const char *str2)
Compares two null-terminated strings for equality.
Definition common.c:248
int str_cpy(char *str_to, const char *str_from, uint32_t len)
Copies a null-terminated string into a destination buffer.
Definition common.c:333
uint8_t ascii_hex_to_byte(char high, char low)
Converts two ASCII hex characters into a single byte value.
Definition common.c:265
uint32_t get_str_len(char const *str)
Returns the length of a string, excluding the null terminator.
Definition common.c:22
int str_tokenize(char *str, const char *separator, uint16_t max_tokens, char **tokens, uint16_t *num_tokens)
Splits a string into tokens separated by a given delimiter.
Definition common.c:287
@ ENABLE
Definition common.h:100
ERR_te
Standard return type used by all public API functions.
Definition err.h:35
@ ERR_DEINITIALIZATION_FAILURE
Definition err.h:44
@ ERR_UNKNOWN
Definition err.h:37
@ ERR_OK
Definition err.h:36
@ ERR_MODULE_ALREADY_INITIALIZED
Definition err.h:54
@ ERR_INITIALIZATION_FAILURE
Definition err.h:43
@ ERR_DATA_ACQUISITION_FAILURE
Definition err.h:47
@ ERR_INVALID_ARGUMENT
Definition err.h:38
ERR_te init_log(void)
Initializes the logging subsystem.
Definition init.c:25
#define LOG_ERROR(subsys, lvl, fmt,...)
Definition log.h:258
#define LOG_INFO(subsys, lvl, fmt,...)
Definition log.h:255
LOG_LEVEL_te
Log severity levels, in ascending order of severity.
Definition log.h:63
@ LOG_LEVEL_INFO
Definition log.h:64
MODULES_te
Identifies a subsystem for use in logging and CLI output.
Definition modules.h:42
@ MODULES_NEO6
Definition modules.h:45
static ERR_te neo6_dumpnmea_handler(uint32_t argc, char **argv)
CLI handler for the "dumpnmea" command. Enables or disables raw NMEA logging.
Definition neo6.c:864
static ERR_te neo6_process_gga(char **tokens)
Parses a GGA sentence and updates fix status, satellites used, orthometric height,...
Definition neo6.c:678
static ERR_te neo6_process_rmc(char **tokens)
Parses an RMC sentence and updates time, date, latitude, and longitude.
Definition neo6.c:556
static ERR_te neo6_process_gsa(char **tokens)
Parses a GSA sentence and updates fix type, PDOP, HDOP, and VDOP.
Definition neo6.c:757
static ERR_te neo6_calc_checksum(char *msg, uint32_t msg_len, uint8_t *checksum_o)
Computes the NMEA checksum of a sentence by XORing all bytes between '$' and '*'.
Definition neo6.c:458
static ERR_te neo6_process_vtg(char **tokens)
Parses a VTG sentence and updates movement direction and speed.
Definition neo6.c:634
static ERR_te neo6_process_gsv(char **tokens)
Parses a GSV sentence and updates the total satellite count.
Definition neo6.c:826
static ERR_te neo6_process_msg(uint8_t *msg, uint32_t msg_len)
Verifies the checksum of a received NMEA sentence and dispatches it to the appropriate parser.
Definition neo6.c:501
ERR_te neo6_deinit_subsys(void)
Deinitializes the NEO-6 subsystem.
Definition neo6.c:246
ERR_te neo6_run(void)
Processes received NMEA data.
Definition neo6.c:387
ERR_te neo6_start_subsys(void)
Starts the NEO-6 subsystem.
Definition neo6.c:274
ERR_te neo6_init_handle(NEO6_CFG_ts *neo6_cfg, NEO6_HANDLE_ts **neo6_handle_o)
Initializes the NEO-6 hardware handle.
Definition neo6.c:322
ERR_te neo6_stop_subsys(void)
Stops the NEO-6 subsystem.
Definition neo6.c:298
ERR_te neo6_init_subsys(void)
Initializes the NEO-6 subsystem.
Definition neo6.c:205
ERR_te neo6_get_info(NEO6_INFO_ts **neo6_info_o)
Returns a pointer to the internal GPS data structure.
Definition neo6.c:430
struct neo6_handle_s NEO6_HANDLE_ts
Opaque handle representing the NEO-6 hardware instance.
Definition neo6.h:142
void gpio_init(GPIO_CFG_ts *gpio_cfg)
Initializes a GPIO pin according to the given configuration.
GPIO_PIN_te
GPIO pin number within a port (0–15).
GPIO_ALTERNATE_FUNCTION_te
GPIO alternate function mapping (AF0–AF15).
@ GPIO_MODE_ALTERNATE_FUNCTION
void usart6_irq_data_recv_callback(uint8_t data)
USART RXNE interrupt callback. Writes the received byte into the USART receive buffer.
Definition neo6.c:908
void usart_init(USART_CFG_ts *usart_cfg)
Initializes the USART peripheral with the given configuration.
void usart_set_reception(USART_REGDEF_ts *usart_instance, EN_STATUS_te en_status)
Enables or disables the USART receiver (RE bit).
USART_BAUD_RATE_te
USART baud rate in bits per second.
@ USART_INTERRUPT_EN_TRUE
Common initialization public API.
Log subsystem public API.
System module identifier definitions.
#define GSA_VDOP_POS
Definition neo6.c:59
#define GGA_GEOID_SEP_POS
Definition neo6.c:48
#define GSA_HDOP_POS
Definition neo6.c:58
#define GGA_GEOID_SEP_UNIT_POS
Definition neo6.c:49
static CMD_CLIENT_INFO_ts neo6_cmd_client_info
Registration descriptor passed to the command subsystem.
Definition neo6.c:192
#define GGA_ORTH_HEIGHT_UNIT_POS
Definition neo6.c:47
#define VTG_SPEED_OVER_GROUND_POS
Definition neo6.c:33
#define VTG_TMG_T_DEGREE_POS
Definition neo6.c:27
static uint8_t nmea_cbuf_mem[128]
Backing memory for the NMEA sentence accumulation buffer.
Definition neo6.c:83
#define GSA_FIX_TYPE_POS
Definition neo6.c:55
#define GSV_NUM_SATS_VISIBLE_POS
Definition neo6.c:64
#define GGA_ORTH_HEIGHT_POS
Definition neo6.c:46
#define GSA_PDOP_POS
Definition neo6.c:57
#define GGA_QUALITY_FIX_POS
Definition neo6.c:43
#define GGA_NUM_SATS_POS
Definition neo6.c:44
static uint8_t usart_data_recv_cbuf_mem[128]
Backing memory for the USART receive circular buffer.
Definition neo6.c:75
static CMD_INFO_ts neo6_cmds[]
Table of CLI commands registered by the NEO-6 subsystem.
Definition neo6.c:177
NEO-6M GPS module public API.
STM32F401RE GPIO driver public API.
STM32F401RE USART driver public API.
Handle representing a circular buffer instance.
Definition cbuf.h:60
Describes a subsystem client registering with the command module.
Definition cmd.h:92
Describes a single command exposed by a client.
Definition cmd.h:72
Configuration structure for initializing a GPIO pin.
GPIO_REGDEF_ts * port
GPIO_ALTERNATE_FUNCTION_te alternate_function
GPIO_PIN_te pin
GPIO_MODE_te mode
GPIO peripheral register map.
Definition stm32f401re.h:95
Configuration structure for initializing a NEO-6 handle.
Definition neo6.h:110
USART_REGDEF_ts * usart_instance
Definition neo6.h:112
GPIO_REGDEF_ts * rx_gpio_port
Definition neo6.h:118
GPIO_REGDEF_ts * tx_gpio_port
Definition neo6.h:124
USART_BAUD_RATE_te usart_baud_rate
Definition neo6.h:115
GPIO_PIN_te rx_gpio_pin
Definition neo6.h:121
GPIO_ALTERNATE_FUNCTION_te gpio_alternate_function
Definition neo6.h:130
GPIO_PIN_te tx_gpio_pin
Definition neo6.h:127
Holds all GPS data fields parsed from incoming NMEA sentences.
Definition neo6.h:85
Configuration structure for initializing a USART peripheral.
USART_BAUD_RATE_te baud_rate
USART_REGDEF_ts * instance
USART_INTERRUPT_EN_te interrupt_en
USART peripheral register map.
Internal state of the SysTick driver.
MODULES_te subsys
Definition button.c:95
NEO6_HANDLE_ts neo6_handle
Definition neo6.c:128
NEO6_INFO_ts neo6_info
Definition neo6.c:137
CBUF_HANDLE_ts usart_data_recv_cbuf
Definition console.c:60
CBUF_HANDLE_ts nmea_cbuf
Definition neo6.c:134
bool dump_raw_nmea
Definition neo6.c:146
LOG_LEVEL_te log_level
Definition button.c:92
Internal structure representing the NEO-6 hardware instance.
Definition neo6.c:93
USART_REGDEF_ts * usart_instance
Definition neo6.c:95
USART_BAUD_RATE_te usart_baud_rate
Definition neo6.c:98
GPIO_ALTERNATE_FUNCTION_te gpio_alternate_function
Definition neo6.c:113
GPIO_PIN_te tx_gpio_pin
Definition neo6.c:110
GPIO_REGDEF_ts * rx_gpio_port
Definition neo6.c:101
bool initialized
Definition neo6.c:116
GPIO_REGDEF_ts * tx_gpio_port
Definition neo6.c:107
GPIO_PIN_te rx_gpio_pin
Definition neo6.c:104