FOUND Coverage Report


src/
File: providers/converters.hpp
Date: 2026-03-24 21:41:51
Lines:
84/84
100.0%
Functions:
9/9
100.0%
Branches:
114/114
100.0%

Line Branch Exec Source
1 #ifndef SRC_PROVIDERS_CONVERTERS_HPP_
2 #define SRC_PROVIDERS_CONVERTERS_HPP_
3
4 #include <stb_image/stb_image.h>
5
6 #include <string>
7 #include <memory>
8 #include <fstream>
9 #include <sstream>
10 #include <iomanip>
11 #include <ctime>
12
13 #include "common/logging.hpp"
14 #include "common/time/time.hpp"
15 #include "common/spatial/attitude-utils.hpp"
16 #include "common/style.hpp"
17 #include "common/decimal.hpp"
18 #include "datafile/datafile.hpp"
19 #include "datafile/serialization.hpp"
20
21 // NOTE: Throwing exceptions is allowed in this file, as these functions
22 // must successfully parse data for any pipeline to function properly.
23 // If they fail, the pipeline should not be run, and an exception should be thrown.
24 // This is preferable than continuing with invalid data and outputting an unintended
25 // result.
26
27 constexpr int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
28
29 namespace found {
30
31 /**
32 * Converts a string to an unsigned char
33 *
34 * @param str The string to convert
35 *
36 * @return The unsigned char the string
37 * represents
38 *
39 * @pre str represents a number between 0
40 * and 255
41 */
42 3 inline unsigned char strtouc(const std::string &str) {
43 3 return static_cast<unsigned char>(std::strtoul(str.c_str(), nullptr, 10));
44 }
45
46 6 inline size_t strtosize(const std::string &str) {
47 6 return static_cast<size_t>(atoi(str.c_str()));
48 }
49
50 /**
51 * Converts a string to a decimal
52 *
53 * @param str The string to convert
54 *
55 * @return The decimal numeber this string
56 * represents
57 *
58 * @pre The str must actually represent a decimal
59 */
60 104 inline decimal strtodecimal(const std::string &str) {
61 104 return STR_TO_DECIMAL(str);
62 }
63
64 /**
65 * Converts a string to euler angles.
66 *
67 * @param str The string to convert
68 *
69 * @return An EulerAngle corresponding
70 * to the string, if the str is valid
71 *
72 * @pre The string must have 3 decimal
73 * values seperated by commas or spaces
74 */
75 26 inline EulerAngles strtoea(const std::string &str) {
76
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 15 times.
26 char delimiter = str.find(" ") != std::string::npos ? ' ' : ',';
77 26 decimal result[3];
78
79 26 size_t start = 0;
80 26 size_t end = str.find(delimiter);
81 26 size_t index = 0;
82
83
4/4
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 51 times.
✓ Branch 3 taken 1 times.
77 while (index != 2 && end != std::string::npos) {
84
2/2
✓ Branch 2 taken 51 times.
✓ Branch 5 taken 51 times.
51 result[index++] = strtodecimal(str.substr(start, end - start));
85 51 start = end + 1;
86 51 end = str.find(delimiter, start);
87 }
88
89
2/2
✓ Branch 2 taken 26 times.
✓ Branch 5 taken 26 times.
26 result[index++] = strtodecimal(str.substr(start));
90
91
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 26 times.
27 while (index != 3) result[index++] = 0;
92
93 52 return EulerAngles(DegToRad(result[0]), DegToRad(result[1]), DegToRad(result[2]));
94 }
95
96 /**
97 * Converts the string to a bool
98 *
99 * @param str The string to convert
100 *
101 * @return true iff the string represents
102 * a true value
103 */
104 10 inline bool strtobool(const std::string &str) {
105
6/6
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 3 times.
10 return str.size() != 0 && str != "0" && str != "false";
106 }
107
108 /**
109 * Converts a string to an image
110 *
111 * @param str The string to convert
112 *
113 * @return The image that the string represents
114 *
115 * @note This function uses stb_image.h to load the image
116 *
117 * @throw std::runtime_error if the image cannot be loaded
118 */
119 20 inline Image strtoimage(const std::string &str) {
120 Image image;
121 20 image.image = stbi_load(str.c_str(), &image.width, &image.height, &image.channels, 0);
122
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 19 times.
20 if (!image.image) {
123
5/5
✓ Branch 3 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 14 taken 1 times.
✓ Branch 17 taken 1 times.
1 throw std::runtime_error("Could not load image " + str + ": " + stbi_failure_reason());
124 }
125 19 return image;
126 }
127
128 /**
129 * Converts a string to time
130 *
131 * @param str The string to convert
132 *
133 * @return The time from epoch that the string represents (epochs in nanoseconds)
134 */
135 29 inline DateTime strtodatetime(const std::string &str) {
136 29 std::tm tm = {};
137
1/1
✓ Branch 2 taken 29 times.
29 std::istringstream ss(str);
138
139
1/1
✓ Branch 2 taken 29 times.
29 ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
140
3/3
✓ Branch 1 taken 29 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 25 times.
29 if (ss.fail()) {
141
2/2
✓ Branch 3 taken 4 times.
✓ Branch 6 taken 4 times.
4 throw std::invalid_argument("Invalid datetime format: " + str);
142 }
143
144 // Store original values for validation
145 25 uint64_t year = static_cast<uint64_t>(tm.tm_year + 1900);
146 25 uint64_t month = static_cast<uint64_t>(tm.tm_mon + 1);
147 25 uint64_t day = static_cast<uint64_t>(tm.tm_mday);
148 25 uint64_t hour = static_cast<uint64_t>(tm.tm_hour);
149 25 uint64_t minute = static_cast<uint64_t>(tm.tm_min);
150 25 uint64_t second = static_cast<uint64_t>(tm.tm_sec);
151
152
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 24 times.
25 if (second > 59) {
153
2/2
✓ Branch 3 taken 1 times.
✓ Branch 6 taken 1 times.
1 throw std::invalid_argument("Invalid second in datetime: " + str);
154 }
155
156 // Validate day of month (considering leap years)
157
6/6
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 17 times.
24 bool is_leap_year = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
158 24 uint64_t max_days = static_cast<uint64_t>(days_in_month[month - 1]);
159
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 5 times.
24 if (is_leap_year && month == 2) {
160 2 max_days = 29;
161 }
162
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 21 times.
24 if (day > max_days) {
163
2/2
✓ Branch 3 taken 3 times.
✓ Branch 6 taken 3 times.
3 throw std::invalid_argument("Invalid day in datetime: " + str);
164 }
165
166 21 std::time_t t = timegm(&tm);
167
168 21 uint64_t nanosecond = 0;
169 21 std::string nanos_str;
170
11/11
✓ Branch 1 taken 21 times.
✓ Branch 4 taken 21 times.
✓ Branch 6 taken 14 times.
✓ Branch 7 taken 7 times.
✓ Branch 9 taken 13 times.
✓ Branch 10 taken 1 times.
✓ Branch 12 taken 13 times.
✓ Branch 14 taken 12 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 12 times.
✓ Branch 17 taken 9 times.
21 if (std::getline(ss, nanos_str) && nanos_str.size() > 1 && nanos_str[0] == '.') {
171
1/1
✓ Branch 2 taken 12 times.
12 nanos_str = nanos_str.substr(1);
172
1/1
✓ Branch 1 taken 12 times.
12 nanos_str.resize(9, '0'); // pad or truncate to 9 digits
173
1/1
✓ Branch 1 taken 11 times.
12 nanosecond = static_cast<uint64_t>(std::stoul(nanos_str));
174 }
175
176 // Convert to nanoseconds: seconds * NS_PER_SEC + nanoseconds
177 20 uint64_t epochs_ns = static_cast<uint64_t>(t) * NS_PER_SEC + nanosecond;
178
179 return {
180 epochs_ns,
181 year,
182 month,
183 day,
184 hour,
185 minute,
186 second,
187 nanosecond
188 20 };
189 30 }
190
191 /**
192 *
193 */
194 22 inline DataFile strtodf(const std::string &str) {
195
1/1
✓ Branch 2 taken 22 times.
22 std::ifstream stream(str);
196
1/1
✓ Branch 1 taken 20 times.
42 return deserializeDataFile(stream, str);
197 22 }
198
199 /**
200 * Converts a string to a vector of location records
201 *
202 * @param str The string to convert
203 *
204 * @return The vector of location records that the string represents
205 *
206 * @pre str must refer to a file, either the Data File, or a space
207 * delinated file with the following format on each line:
208 * Timestamp(int) PositionX(decimal) PositionY(decimal) PositionZ(decimal)
209 */
210 8 inline LocationRecords strtolr(const std::string &str) {
211
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
8 if (str.size() >= 6) {
212
4/4
✓ Branch 3 taken 7 times.
✓ Branch 6 taken 7 times.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 5 times.
7 if (str.substr(str.size() - 6) == ".found") {
213
8/8
✓ Branch 3 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 13 taken 2 times.
✓ Branch 16 taken 2 times.
✓ Branch 20 taken 2 times.
✓ Branch 23 taken 2 times.
✓ Branch 26 taken 2 times.
✓ Branch 29 taken 2 times.
6 LOG_INFO("Getting Position Data from Data File (*.found)");
214
1/1
✓ Branch 2 taken 1 times.
2 DataFile data = strtodf(str);
215
1/1
✓ Branch 4 taken 1 times.
3 return LocationRecords(data.positions.get(), data.positions.get() + data.header.num_positions);
216 1 }
217 }
218
219
8/8
✓ Branch 3 taken 6 times.
✓ Branch 10 taken 6 times.
✓ Branch 13 taken 6 times.
✓ Branch 16 taken 6 times.
✓ Branch 20 taken 6 times.
✓ Branch 23 taken 6 times.
✓ Branch 26 taken 6 times.
✓ Branch 29 taken 6 times.
18 LOG_INFO("Getting Position Data from non-Data File (not *.found)");
220 6 LocationRecords records;
221
1/1
✓ Branch 2 taken 6 times.
6 std::ifstream file(str);
222
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
6 if (!file.is_open()) {
223
2/2
✓ Branch 3 taken 3 times.
✓ Branch 6 taken 3 times.
3 throw std::runtime_error("Could not open file " + str);
224 }
225
226 3 std::string line;
227
4/4
✓ Branch 1 taken 11 times.
✓ Branch 4 taken 11 times.
✓ Branch 6 taken 9 times.
✓ Branch 7 taken 2 times.
11 while (std::getline(file, line)) {
228
1/1
✓ Branch 2 taken 9 times.
9 std::istringstream iss(line);
229 9 LocationRecord record;
230
7/7
✓ Branch 1 taken 9 times.
✓ Branch 4 taken 9 times.
✓ Branch 7 taken 9 times.
✓ Branch 10 taken 9 times.
✓ Branch 13 taken 9 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 8 times.
9 if (!(iss >> record.timestamp >> record.position.x >> record.position.y >> record.position.z)) {
231
1/1
✓ Branch 1 taken 1 times.
1 file.close();
232
4/4
✓ Branch 5 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 14 taken 1 times.
1 throw std::runtime_error("Invalid format for file " + str + ": " + line);
233 }
234
1/1
✓ Branch 1 taken 8 times.
8 records.push_back(record);
235 9 }
236
237
1/1
✓ Branch 1 taken 2 times.
2 file.close();
238 2 return records;
239 11 }
240
241 } // namespace found
242
243 #endif // SRC_PROVIDERS_CONVERTERS_HPP_
244