0.5.1
Loading...
Searching...
No Matches
NmeaFile.cpp
Go to the documentation of this file.
1// This file is part of INSTINCT, the INS Toolkit for Integrated
2// Navigation Concepts and Training by the Institute of Navigation of
3// the University of Stuttgart, Germany.
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9#include "NmeaFile.hpp"
10
11#include "util/Logger.hpp"
15#include "util/StringUtil.hpp"
16
19
21 : Node(typeStatic())
22{
23 LOG_TRACE("{}: called", name);
24
25 _hasConfig = true;
26 _guiConfigDefaultWindowSize = { 517, 87 };
27
29}
30
32{
33 LOG_TRACE("{}: called", nameId());
34}
35
37{
38 return "NmeaFile";
39}
40
41std::string NAV::NmeaFile::type() const
42{
43 return typeStatic();
44}
45
47{
48 return "Data Provider";
49}
50
52{
53 if (auto res = FileReader::guiConfig(".*", { ".*" }, size_t(id), nameId()))
54 {
55 LOG_DEBUG("{}: Path changed to {}", nameId(), _path);
57 if (res == FileReader::PATH_CHANGED)
58 {
60 }
61 else
62 {
64 }
65 }
66}
67
68[[nodiscard]] json NAV::NmeaFile::save() const
69{
70 LOG_TRACE("{}: called", nameId());
71
72 json j;
73
74 j["FileReader"] = FileReader::save();
75
76 return j;
77}
78
80{
81 LOG_TRACE("{}: called", nameId());
82
83 if (j.contains("FileReader"))
84 {
85 FileReader::restore(j.at("FileReader"));
86 }
87}
88
90{
91 LOG_TRACE("{}: called", nameId());
92
94}
95
97{
98 LOG_TRACE("{}: called", nameId());
99
101}
102
104{
106
107 _hasValidDate = false;
108 _oldSoD = -1.0;
109
110 return true;
111}
112
113bool NAV::NmeaFile::setDateFromZDA(const std::string& line)
114{
115 // decode ZDA string according to http://www.nmea.de/nmea0183datensaetze.html#zda
116
117 // 0 1 2 3 4 5 6
118 // | | | | | | |
119 // $--ZDA,hhmmss.ss,xx,xx,xxxx,xx,xx*hh<CR><LF>
120 std::vector<std::string> splittedString = str::split(line, ",");
121 if (splittedString.size() == 7)
122 {
123 std::size_t pos_star = splittedString.back().find('*');
124 if (pos_star != std::string::npos)
125 {
126 int64_t crc = std::strtol(splittedString[6].substr(pos_star + 1).c_str(), nullptr, 16);
127 // checksum calculation similar to https://gist.github.com/devendranaga/fce8e166f4335fa777650493cb9246db
128 int64_t mycrc = 0;
129 pos_star = line.find('*');
130 for (unsigned int i = 1; i < pos_star; i++)
131 {
132 mycrc ^= line.at(i);
133 }
134 if (mycrc == crc)
135 {
136 _currentDate.day = std::stoi(splittedString[2]);
137 _currentDate.month = std::stoi(splittedString[3]);
138 _currentDate.year = std::stoi(splittedString[4]);
139
140 _hasValidDate = true;
141 return true;
142 }
143 }
144 }
145 return false;
146}
147
148bool NAV::NmeaFile::setDateFromRMC(const std::string& line)
149{
150 // decode RMC string according to http://www.nmea.de/nmea0183datensaetze.html#rmc
151
152 // 0 1 2 3 4 5 6 7 8 9 10 11
153 // | | | | | | | | | | | |
154 // $--RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxx,x.x,a*hh<CR><LF>
155 std::vector<std::string> splittedString = str::split(line, ",");
156 if (splittedString.size() == 12)
157 {
158 std::size_t pos_star = splittedString[11].find('*');
159 if (pos_star != std::string::npos)
160 {
161 int64_t crc = std::strtol(splittedString[11].substr(pos_star + 1).c_str(), nullptr, 16);
162 // checksum calculation similar to https://gist.github.com/devendranaga/fce8e166f4335fa777650493cb9246db
163 int64_t mycrc = 0;
164 pos_star = line.find('*');
165 for (unsigned int i = 1; i < pos_star; i++)
166 {
167 mycrc ^= line.at(i);
168 }
169 if (mycrc == crc)
170 {
171 _currentDate.day = std::stoi(splittedString[9].substr(0, 2));
172 _currentDate.month = std::stoi(splittedString[9].substr(2, 2));
173 _currentDate.year = std::stoi(splittedString[9].substr(4, 2));
174 if (_currentDate.year > 60)
175 {
176 _currentDate.year += 1900;
177 }
178 else
179 {
180 _currentDate.year += 2000;
181 }
182
183 _hasValidDate = true;
184 return true;
185 }
186 }
187 }
188 return false;
189}
190
191std::shared_ptr<const NAV::NodeData> NAV::NmeaFile::pollData()
192{
193 auto obs = std::make_shared<PosVel>();
194
195 // Read line
196 std::string line;
197
198 std::vector<std::string> splittedData;
199
200 int hour = 0;
201 int minute = 0;
202 double second = 0.0;
203
204 double lat_rad = 0.0;
205 double lon_rad = 0.0;
206 double hgt = 0.0;
207
208 while (true)
209 {
210 getline(line);
211
212 if (eof())
213 {
214 return nullptr; // when done with file reading
215 }
216
217 // Remove any starting non text characters
218 line.erase(line.begin(), std::ranges::find_if(line, [](int ch) { return std::isgraph(ch); }));
219
220 splittedData = str::split(line, ",");
221
222 if (splittedData[0].starts_with("$"))
223 {
224 if (_hasValidDate && splittedData[0].substr(3, 3) == "GGA")
225 {
226 // decode GGA stream according to http://www.nmea.de/nmea0183datensaetze.html#gga
227
228 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
229 // | | | | | | | | | | | | | | |
230 // $--GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh<CR><LF>
231 if (splittedData.size() != 15) { continue; }
232
233 std::size_t pos_star = splittedData[14].find('*');
234 int64_t crc = std::strtol(splittedData[14].substr(pos_star + 1).c_str(), nullptr, 16);
235 // checksum calculation similar to https://gist.github.com/devendranaga/fce8e166f4335fa777650493cb9246db
236 int64_t mycrc = 0;
237 pos_star = line.find('*');
238 for (unsigned int i = 1; i < pos_star; i++)
239 {
240 mycrc ^= line.at(i);
241 }
242 if (mycrc == crc)
243 {
244 hour = std::stoi(splittedData[1].substr(0, 2));
245 minute = std::stoi(splittedData[1].substr(2, 2));
246 second = std::stod(splittedData[1].substr(4));
247 double newSOD = hour * 24 * 60.0 + minute * 60.0 + second;
248
249 // only continue if second of day > than previous one
250 if (newSOD < _oldSoD)
251 {
252 _oldSoD = newSOD; // store current second of day for next call of this routine
253 _hasValidDate = false; // force wait until next ZDA stream
254 continue;
255 }
256
257 double lat1 = std::stod(splittedData[2].substr(0, 2));
258 double lat2 = std::stod(splittedData[2].substr(2));
259
260 lat_rad = (lat1 + lat2 / 60.0) / 180.0 * M_PI; // convert to radian
261
262 if (splittedData[3] == "S") // flip sign if south latitude
263 {
264 lat_rad *= -1.0;
265 }
266
267 double lon1 = std::stoi(splittedData[4].substr(0, 3));
268 double lon2 = std::stod(splittedData[4].substr(3));
269
270 lon_rad = (lon1 + lon2 / 60.0) / 180.0 * M_PI; // convert to radian
271
272 if (splittedData[5] == "W") // flip sign if west longitude
273 {
274 lon_rad *= -1.0;
275 }
276
277 hgt = std::stod(splittedData[9]) + std::stod(splittedData[11]); // ellipsoidal height = height above geoid + geoid height
278
279 break;
280 }
281 }
282 else if (splittedData[0].substr(3, 3) == "ZDA")
283 {
284 if (setDateFromZDA(line))
285 {
286 _oldSoD = -1;
287 }
288 continue;
289 }
290 else if (splittedData[0].substr(3, 3) == "RMC")
291 {
292 if (setDateFromRMC(line))
293 {
294 _oldSoD = -1;
295 }
296 continue;
297 }
298 }
299 }
300
301 Eigen::Vector3d lla_pos{ lat_rad, lon_rad, hgt };
302 Eigen::Vector3d n_vel{ std::nan(""), std::nan(""), std::nan("") }; // GGA streams don't contain velocity, thus set this one invalid
303 obs->insTime = InsTime(_currentDate.year, _currentDate.month, _currentDate.day,
304 hour, minute, second,
305 UTC); // per definition, time tags in GGA NMEA streams are in UTC
306
307 obs->setPosition_lla(lla_pos);
308
309 obs->setVelocity_n(n_vel);
310
312 return obs;
313}
314
319
321{
322 // Empty because NMEA does not have a header
323}
Transformation collection.
Save/Load the Nodes.
nlohmann::json json
json namespace
Utility class for logging to console and file.
#define LOG_DEBUG
Debug information. Should not be called on functions which receive observations (spamming)
Definition Logger.hpp:67
#define LOG_TRACE
Detailled info to trace the execution of the program. Should not be called on functions which receive...
Definition Logger.hpp:65
File Reader for NMEA files.
Position and Velocity Storage Class.
Utility functions for working with std::strings.
Keeps track of the current real/simulation time.
bool initialize()
Initialize the file reader.
void restore(const json &j)
Restores the node from a json object.
std::string _path
Path to the file.
FileType
File Type Enumeration.
@ ASCII
Ascii text data.
auto eof() const
Check whether the end of file is reached.
@ PATH_CHANGED
The path changed and exists.
GuiResult guiConfig(const char *vFilters, const std::vector< std::string > &extensions, size_t id, const std::string &nameId)
ImGui config.
void resetReader()
Moves the read cursor to the start.
auto & getline(std::string &str)
Reads a line from the filestream.
json save() const
Saves the node into a json object.
void deinitialize()
Deinitialize the file reader.
The class is responsible for all time-related tasks.
Definition InsTime.hpp:710
bool setDateFromZDA(const std::string &line)
Set date info from ZDA steam.
Definition NmeaFile.cpp:113
double _oldSoD
Second of day (SOD) from last GGA stream. This variable is used to check if SOD is increasing,...
Definition NmeaFile.hpp:85
bool _hasValidDate
Checks whether a ZDA or RMC time tag was read so that UTC can be reconstructed together with the GGA ...
Definition NmeaFile.hpp:82
void restore(const json &j) override
Restores the node from a json object.
Definition NmeaFile.cpp:79
void deinitialize() override
Deinitialize the node.
Definition NmeaFile.cpp:96
bool initialize() override
Initialize the node.
Definition NmeaFile.cpp:89
static std::string typeStatic()
String representation of the Class Type.
Definition NmeaFile.cpp:36
struct NAV::NmeaFile::@035207343112113160312011275043304125120067020170 _currentDate
Current date.
NmeaFile()
Default constructor.
Definition NmeaFile.cpp:20
json save() const override
Saves the node into a json object.
Definition NmeaFile.cpp:68
~NmeaFile() override
Destructor.
Definition NmeaFile.cpp:31
std::shared_ptr< const NodeData > pollData()
Polls data from the file.
Definition NmeaFile.cpp:191
static constexpr size_t OUTPUT_PORT_INDEX_NMEA_POS_OBS
Flow (PosVel)
Definition NmeaFile.hpp:62
void guiConfig() override
ImGui config window which is shown on double click.
Definition NmeaFile.cpp:51
bool resetNode() override
Resets the node. Moves the read cursor to the start.
Definition NmeaFile.cpp:103
static std::string category()
String representation of the Class Category.
Definition NmeaFile.cpp:46
bool setDateFromRMC(const std::string &line)
Set date info from RMC steam.
Definition NmeaFile.cpp:148
std::string type() const override
String representation of the Class Type.
Definition NmeaFile.cpp:41
void readHeader() override
Read the Header of the file.
Definition NmeaFile.cpp:320
FileType determineFileType() override
Determines the type of the file.
Definition NmeaFile.cpp:315
bool doDeinitialize(bool wait=false)
Asks the node worker to deinitialize the node.
Definition Node.cpp:465
ImVec2 _guiConfigDefaultWindowSize
Definition Node.hpp:522
Node(std::string name)
Constructor.
Definition Node.cpp:29
OutputPin * CreateOutputPin(const char *name, Pin::Type pinType, const std::vector< std::string > &dataIdentifier, OutputPin::PinData data=static_cast< void * >(nullptr), int idx=-1)
Create an Output Pin object.
Definition Node.cpp:278
std::string nameId() const
Node name and id.
Definition Node.cpp:323
std::string name
Name of the Node.
Definition Node.hpp:507
bool doReinitialize(bool wait=false)
Asks the node worker to reinitialize the node.
Definition Node.cpp:420
void invokeCallbacks(size_t portIndex, const std::shared_ptr< const NodeData > &data)
Calls all registered callbacks on the specified output port.
Definition Node.cpp:179
bool _hasConfig
Flag if the config window should be shown.
Definition Node.hpp:525
static std::string type()
Returns the type of the data class.
Definition PosVel.hpp:27
void ApplyChanges()
Signals that there have been changes to the flow.
static std::vector< std::string > split(const std::string &str, const std::string &delimiter)
Splits a string into parts at a delimiter.
@ UTC
Coordinated Universal Time.
@ Flow
NodeData Trigger.
Definition Pin.hpp:52