1 /*
2  * Copyright (c) 2023 Trackunit Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 
9 #include <string.h>
10 #include <errno.h>
11 #include <stdlib.h>
12 
13 #include "gnss_parse.h"
14 
15 #define GNSS_PARSE_NANO_KNOTS_IN_MMS               (1943840LL)
16 #define GNSS_PARSE_NANO                            (1000000000LL)
17 #define GNSS_PARSE_MICRO                           (1000000LL)
18 #define GNSS_PARSE_MILLI                           (1000LL)
19 
gnss_parse_dec_to_nano(const char * str,int64_t * nano)20 int gnss_parse_dec_to_nano(const char *str, int64_t *nano)
21 {
22 	int64_t sum = 0;
23 	int8_t decimal = -1;
24 	int8_t pos = 0;
25 	int8_t start = 0;
26 	int64_t increment;
27 
28 	__ASSERT(str != NULL, "str argument must be provided");
29 	__ASSERT(nano != NULL, "nano argument must be provided");
30 
31 	/* Find decimal */
32 	while (str[pos] != '\0') {
33 		/* Verify if char is decimal */
34 		if (str[pos] == '.') {
35 			decimal = pos;
36 			break;
37 		}
38 
39 		/* Advance position */
40 		pos++;
41 	}
42 
43 	/* Determine starting position based on decimal location */
44 	pos = decimal < 0 ? pos - 1 : decimal - 1;
45 
46 	/* Skip sign if it exists */
47 	start = str[0] == '-' ? 1 : 0;
48 
49 	/* Add whole value to sum */
50 	increment = GNSS_PARSE_NANO;
51 	while (start <= pos) {
52 		/* Verify char is decimal */
53 		if (str[pos] < '0' || str[pos] > '9') {
54 			return -EINVAL;
55 		}
56 
57 		/* Add value to sum */
58 		sum += (str[pos] - '0') * increment;
59 
60 		/* Update increment */
61 		increment *= 10;
62 
63 		/* Degrement position */
64 		pos--;
65 	}
66 
67 	/* Check if decimal was found */
68 	if (decimal < 0) {
69 		/* Set sign of sum */
70 		sum = start == 1 ? -sum : sum;
71 
72 		*nano = sum;
73 		return 0;
74 	}
75 
76 	/* Convert decimal part to nano fractions and add it to sum */
77 	pos = decimal + 1;
78 	increment = GNSS_PARSE_NANO / 10LL;
79 	while (str[pos] != '\0') {
80 		/* Verify char is decimal */
81 		if (str[pos] < '0' || str[pos] > '9') {
82 			return -EINVAL;
83 		}
84 
85 		/* Add value to micro_degrees */
86 		sum += (str[pos] - '0') * increment;
87 
88 		/* Update unit */
89 		increment /= 10;
90 
91 		/* Increment position */
92 		pos++;
93 	}
94 
95 	/* Set sign of sum */
96 	sum = start == 1 ? -sum : sum;
97 
98 	*nano = sum;
99 	return 0;
100 }
101 
gnss_parse_dec_to_micro(const char * str,uint64_t * micro)102 int gnss_parse_dec_to_micro(const char *str, uint64_t *micro)
103 {
104 	int ret;
105 
106 	__ASSERT(str != NULL, "str argument must be provided");
107 	__ASSERT(micro != NULL, "micro argument must be provided");
108 
109 	ret = gnss_parse_dec_to_nano(str, micro);
110 	if (ret < 0) {
111 		return ret;
112 	}
113 
114 	*micro = (*micro) / GNSS_PARSE_MILLI;
115 	return 0;
116 }
117 
118 
gnss_parse_dec_to_milli(const char * str,int64_t * milli)119 int gnss_parse_dec_to_milli(const char *str, int64_t *milli)
120 {
121 	int ret;
122 
123 	__ASSERT(str != NULL, "str argument must be provided");
124 	__ASSERT(milli != NULL, "milli argument must be provided");
125 
126 	ret = gnss_parse_dec_to_nano(str, milli);
127 	if (ret < 0) {
128 		return ret;
129 	}
130 
131 	(*milli) = (*milli) / GNSS_PARSE_MICRO;
132 	return 0;
133 }
134 
gnss_parse_atoi(const char * str,uint8_t base,int32_t * integer)135 int gnss_parse_atoi(const char *str, uint8_t base, int32_t *integer)
136 {
137 	char *end;
138 
139 	__ASSERT(str != NULL, "str argument must be provided");
140 	__ASSERT(integer != NULL, "integer argument must be provided");
141 
142 	*integer = (int32_t)strtol(str, &end, (int)base);
143 
144 	if ('\0' != (*end)) {
145 		return -EINVAL;
146 	}
147 
148 	return 0;
149 }
150