0.4.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
18namespace nm = NAV::NodeManager;
21
23 : Node(typeStatic())
24{
25 LOG_TRACE("{}: called", name);
26
27 _hasConfig = true;
28 _guiConfigDefaultWindowSize = { 517, 87 };
29
31}
32
34{
35 LOG_TRACE("{}: called", nameId());
36}
37
39{
40 return "NmeaFile";
41}
42
43std::string NAV::NmeaFile::type() const
44{
45 return typeStatic();
46}
47
49{
50 return "Data Provider";
51}
52
54{
55 if (auto res = FileReader::guiConfig(".*", { ".*" }, size_t(id), nameId()))
56 {
57 LOG_DEBUG("{}: Path changed to {}", nameId(), _path);
59 if (res == FileReader::PATH_CHANGED)
60 {
62 }
63 else
64 {
66 }
67 }
68}
69
70[[nodiscard]] json NAV::NmeaFile::save() const
71{
72 LOG_TRACE("{}: called", nameId());
73
74 json j;
75
76 j["FileReader"] = FileReader::save();
77
78 return j;
79}
80
82{
83 LOG_TRACE("{}: called", nameId());
84
85 if (j.contains("FileReader"))
86 {
87 FileReader::restore(j.at("FileReader"));
88 }
89}
90
92{
93 LOG_TRACE("{}: called", nameId());
94
96}
97
99{
100 LOG_TRACE("{}: called", nameId());
101
103}
104
106{
108
109 _hasValidDate = false;
110 _oldSoD = -1.0;
111
112 return true;
113}
114
115bool NAV::NmeaFile::setDateFromZDA(const std::string& line)
116{
117 // decode ZDA string according to http://www.nmea.de/nmea0183datensaetze.html#zda
118
119 // 0 1 2 3 4 5 6
120 // | | | | | | |
121 // $--ZDA,hhmmss.ss,xx,xx,xxxx,xx,xx*hh<CR><LF>
122 std::vector<std::string> splittedString = str::split(line, ",");
123 if (splittedString.size() == 7)
124 {
125 std::size_t pos_star = splittedString.back().find('*');
126 if (pos_star != std::string::npos)
127 {
128 int64_t crc = std::strtol(splittedString[6].substr(pos_star + 1).c_str(), nullptr, 16);
129 // checksum calculation similar to https://gist.github.com/devendranaga/fce8e166f4335fa777650493cb9246db
130 int64_t mycrc = 0;
131 pos_star = line.find('*');
132 for (unsigned int i = 1; i < pos_star; i++)
133 {
134 mycrc ^= line.at(i);
135 }
136 if (mycrc == crc)
137 {
138 _currentDate.day = std::stoi(splittedString[2]);
139 _currentDate.month = std::stoi(splittedString[3]);
140 _currentDate.year = std::stoi(splittedString[4]);
141
142 _hasValidDate = true;
143 return true;
144 }
145 }
146 }
147 return false;
148}
149
150bool NAV::NmeaFile::setDateFromRMC(const std::string& line)
151{
152 // decode RMC string according to http://www.nmea.de/nmea0183datensaetze.html#rmc
153
154 // 0 1 2 3 4 5 6 7 8 9 10 11
155 // | | | | | | | | | | | |
156 // $--RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxx,x.x,a*hh<CR><LF>
157 std::vector<std::string> splittedString = str::split(line, ",");
158 if (splittedString.size() == 12)
159 {
160 std::size_t pos_star = splittedString[11].find('*');
161 if (pos_star != std::string::npos)
162 {
163 int64_t crc = std::strtol(splittedString[11].substr(pos_star + 1).c_str(), nullptr, 16);
164 // checksum calculation similar to https://gist.github.com/devendranaga/fce8e166f4335fa777650493cb9246db
165 int64_t mycrc = 0;
166 pos_star = line.find('*');
167 for (unsigned int i = 1; i < pos_star; i++)
168 {
169 mycrc ^= line.at(i);
170 }
171 if (mycrc == crc)
172 {
173 _currentDate.day = std::stoi(splittedString[9].substr(0, 2));
174 _currentDate.month = std::stoi(splittedString[9].substr(2, 2));
175 _currentDate.year = std::stoi(splittedString[9].substr(4, 2));
176 if (_currentDate.year > 60)
177 {
178 _currentDate.year += 1900;
179 }
180 else
181 {
182 _currentDate.year += 2000;
183 }
184
185 _hasValidDate = true;
186 return true;
187 }
188 }
189 }
190 return false;
191}
192
193std::shared_ptr<const NAV::NodeData> NAV::NmeaFile::pollData()
194{
195 auto obs = std::make_shared<PosVel>();
196
197 // Read line
198 std::string line;
199
200 std::vector<std::string> splittedData;
201
202 int hour = 0;
203 int minute = 0;
204 double second = 0.0;
205
206 double lat_rad = 0.0;
207 double lon_rad = 0.0;
208 double hgt = 0.0;
209
210 while (true)
211 {
212 getline(line);
213
214 if (eof())
215 {
216 return nullptr; // when done with file reading
217 }
218
219 // Remove any starting non text characters
220 line.erase(line.begin(), std::ranges::find_if(line, [](int ch) { return std::isgraph(ch); }));
221
222 splittedData = str::split(line, ",");
223
224 if (splittedData[0].substr(0, 1) == "$")
225 {
226 if (_hasValidDate && splittedData[0].substr(3, 3) == "GGA")
227 {
228 // decode GGA stream according to http://www.nmea.de/nmea0183datensaetze.html#gga
229
230 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
231 // | | | | | | | | | | | | | | |
232 // $--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>
233 if (splittedData.size() != 15) { continue; }
234
235 std::size_t pos_star = splittedData[14].find('*');
236 int64_t crc = std::strtol(splittedData[14].substr(pos_star + 1).c_str(), nullptr, 16);
237 // checksum calculation similar to https://gist.github.com/devendranaga/fce8e166f4335fa777650493cb9246db
238 int64_t mycrc = 0;
239 pos_star = line.find('*');
240 for (unsigned int i = 1; i < pos_star; i++)
241 {
242 mycrc ^= line.at(i);
243 }
244 if (mycrc == crc)
245 {
246 hour = std::stoi(splittedData[1].substr(0, 2));
247 minute = std::stoi(splittedData[1].substr(2, 2));
248 second = std::stod(splittedData[1].substr(4));
249 double newSOD = hour * 24 * 60.0 + minute * 60.0 + second;
250
251 // only continue if second of day > than previous one
252 if (newSOD < _oldSoD)
253 {
254 _oldSoD = newSOD; // store current second of day for next call of this routine
255 _hasValidDate = false; // force wait until next ZDA stream
256 continue;
257 }
258
259 double lat1 = std::stod(splittedData[2].substr(0, 2));
260 double lat2 = std::stod(splittedData[2].substr(2));
261
262 lat_rad = (lat1 + lat2 / 60.0) / 180.0 * M_PI; // convert to radian
263
264 if (splittedData[3] == "S") // flip sign if south latitude
265 {
266 lat_rad *= -1.0;
267 }
268
269 double lon1 = std::stoi(splittedData[4].substr(0, 3));
270 double lon2 = std::stod(splittedData[4].substr(3));
271
272 lon_rad = (lon1 + lon2 / 60.0) / 180.0 * M_PI; // convert to radian
273
274 if (splittedData[5] == "W") // flip sign if west longitude
275 {
276 lon_rad *= -1.0;
277 }
278
279 hgt = std::stod(splittedData[9]) + std::stod(splittedData[11]); // ellipsoidal height = height above geoid + geoid height
280
281 break;
282 }
283 }
284 else if (splittedData[0].substr(3, 3) == "ZDA")
285 {
286 if (setDateFromZDA(line))
287 {
288 _oldSoD = -1;
289 }
290 continue;
291 }
292 else if (splittedData[0].substr(3, 3) == "RMC")
293 {
294 if (setDateFromRMC(line))
295 {
296 _oldSoD = -1;
297 }
298 continue;
299 }
300 }
301 }
302
303 Eigen::Vector3d lla_pos{ lat_rad, lon_rad, hgt };
304 Eigen::Vector3d n_vel{ std::nan(""), std::nan(""), std::nan("") }; // GGA streams don't contain velocity, thus set this one invalid
305 obs->insTime = InsTime(_currentDate.year, _currentDate.month, _currentDate.day,
306 hour, minute, second,
307 UTC); // per definition, time tags in GGA NMEA streams are in UTC
308
309 obs->setPosition_lla(lla_pos);
310
311 obs->setVelocity_n(n_vel);
312
314 return obs;
315}
316
321
323{
324 // Empty because NMEA does not have a header
325}
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.
Manages all Nodes.
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:115
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:81
void deinitialize() override
Deinitialize the node.
Definition NmeaFile.cpp:98
bool initialize() override
Initialize the node.
Definition NmeaFile.cpp:91
static std::string typeStatic()
String representation of the Class Type.
Definition NmeaFile.cpp:38
struct NAV::NmeaFile::@035207343112113160312011275043304125120067020170 _currentDate
Current date.
NmeaFile()
Default constructor.
Definition NmeaFile.cpp:22
json save() const override
Saves the node into a json object.
Definition NmeaFile.cpp:70
~NmeaFile() override
Destructor.
Definition NmeaFile.cpp:33
std::shared_ptr< const NodeData > pollData()
Polls data from the file.
Definition NmeaFile.cpp:193
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:53
bool resetNode() override
Resets the node. Moves the read cursor to the start.
Definition NmeaFile.cpp:105
static std::string category()
String representation of the Class Category.
Definition NmeaFile.cpp:48
bool setDateFromRMC(const std::string &line)
Set date info from RMC steam.
Definition NmeaFile.cpp:150
std::string type() const override
String representation of the Class Type.
Definition NmeaFile.cpp:43
void readHeader() override
Read the Header of the file.
Definition NmeaFile.cpp:322
FileType determineFileType() override
Determines the type of the file.
Definition NmeaFile.cpp:317
bool doDeinitialize(bool wait=false)
Asks the node worker to deinitialize the node.
Definition Node.cpp:395
ImVec2 _guiConfigDefaultWindowSize
Definition Node.hpp:410
Node(std::string name)
Constructor.
Definition Node.cpp:30
std::string nameId() const
Node name and id.
Definition Node.cpp:253
std::string name
Name of the Node.
Definition Node.hpp:395
bool doReinitialize(bool wait=false)
Asks the node worker to reinitialize the node.
Definition Node.cpp:350
void invokeCallbacks(size_t portIndex, const std::shared_ptr< const NodeData > &data)
Calls all registered callbacks on the specified output port.
Definition Node.cpp:180
bool _hasConfig
Flag if the config window should be shown.
Definition Node.hpp:413
static std::string type()
Returns the type of the data class.
Definition PosVel.hpp:27
OutputPin * CreateOutputPin(Node *node, 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.
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