Vanetza
 
Loading...
Searching...
No Matches
nmea.cpp
1#include "nmea.hpp"
2#include "wgs84point.hpp"
3#include <boost/date_time/gregorian/gregorian.hpp>
4#include <boost/date_time/posix_time/posix_time.hpp>
5#include <boost/format.hpp>
6#include <cassert>
7#include <cmath>
8#include <iomanip>
9#include <sstream>
10
11namespace vanetza
12{
13namespace nmea
14{
15
16using namespace vanetza::units;
17
18/**
19 * Print latitude data in BBBB.BBBB, b format.
20 * BBBB.BBBB are degrees and minutes (ddmm.mm)
21 * b is N or S
22 */
23void print_latitude(std::ostream& os, const Wgs84Point& point)
24{
25 double degrees = point.lat.value();
26 double minutes = std::modf(std::abs(degrees), &degrees) * 60.0;
27 os << boost::format("%02d%07.4f") % degrees % minutes;
28 os << "," << (point.lat.value() >= 0.0 ? "N" : "S");
29}
30
31/**
32 * Print longitude data in LLLL.LLLL, l format.
33 * LLLL.LLLL are degrees and minutes (ddmm.mm)
34 * l is E or W
35 */
36void print_longitude(std::ostream& os, const Wgs84Point& point)
37{
38 double degrees = point.lon.value();
39 double minutes = std::modf(std::abs(degrees), &degrees) * 60.0;
40 os << boost::format("%02d%07.4f") % degrees % minutes;
41 os << "," << (point.lon.value() >= 0.0 ? "E" : "W");
42}
43
44namespace detail
45{
46
47struct latitude
48{
49 latitude(const Wgs84Point& p) : point(p) {}
50 friend std::ostream& operator<<(std::ostream& os, const latitude& lat)
51 {
52 print_latitude(os, lat.point);
53 return os;
54 }
55
56 const Wgs84Point& point;
57};
58
59struct longitude
60{
61 longitude(const Wgs84Point& p) : point(p) {}
62 friend std::ostream& operator<<(std::ostream& os, const longitude& lon)
63 {
64 print_longitude(os, lon.point);
65 return os;
66 }
67
68 const Wgs84Point& point;
69};
70
71} // namespace detail
72
73detail::latitude latitude(const Wgs84Point& p) { return detail::latitude(p); }
74detail::longitude longitude(const Wgs84Point& p) { return detail::longitude(p); }
75
76/**
77 * Finish NMEA sentence with *XX where XX is the calculated checksum
78 * \param smsg NMEA sentence with leading $ but without trailing *XX
79 * \return finished NMEA sentence
80 */
81std::string finish(std::stringstream& smsg)
82{
83 std::string msg = smsg.str();
84 unsigned sum = checksum(++msg.begin(), msg.end());
85 msg += boost::str(boost::format("*%02X") % static_cast<unsigned>(sum));
86 return msg;
87}
88
89std::string gprmc(const time& ptime, const Wgs84Point& wgs84,
90 NauticalVelocity ground_speed, TrueNorth heading)
91{
92 /**
93 * Magnetic declination for central europe is about 1 degree east (2010), see this map for reference:
94 * http://upload.wikimedia.org/wikipedia/commons/d/dd/World_Magnetic_Model_Main_Field_Declination_D_2010.png
95 */
96 const double magnetic_angle = 1.0;
97 const char magnetic_direction = 'E';
98
99 std::stringstream smsg;
100 smsg << std::uppercase << std::fixed;
101 auto* tfacet = new boost::posix_time::time_facet("%H%M%S");
102 auto* dfacet = new boost::gregorian::date_facet("%d%m%y");
103 smsg.imbue(std::locale(smsg.getloc(), tfacet));
104 smsg.imbue(std::locale(smsg.getloc(), dfacet));
105
106 smsg << "$GPRMC,";
107 smsg << ptime << ","; // HHMMSS
108 smsg << static_cast<char>(RMCStatus::Valid) << ",";
109 smsg << latitude(wgs84) << "," << longitude(wgs84) << ",";
110 smsg << std::setprecision(1) << ground_speed.value() << ","; // GG.G
111 smsg << std::setprecision(1) << std::fmod(heading.value(), 360.0) << ","; // RR.R
112 smsg << ptime.date() << ","; // DDMMYY
113 smsg << std::setprecision(1) << magnetic_angle << "," << magnetic_direction << ","; // M.M, E/W
114 smsg << static_cast<char>(FAAMode::Autonomous);
115
116 return finish(smsg);
117}
118
119std::string gpgga(const time& ptime, const Wgs84Point& wgs84, Quality quality, Length hdop)
120{
121 const unsigned satellites = 6; // Arbitrary number of used GPS satellites
122 const double height = 0.0; // SUMO map is flat
123 const double separation = 0.0; // Geoidal separation, can it be calculated?
124
125 std::stringstream smsg;
126 smsg << std::uppercase << std::fixed;
127 auto* facet = new boost::posix_time::time_facet("%H%M%s");
128 smsg.imbue(std::locale(smsg.getloc(), facet));
129
130 smsg << "$GPGGA,";
131 smsg << ptime << ","; // HHMMSS.ss
132 smsg << latitude(wgs84) << "," << longitude(wgs84) << ",";
133 smsg << static_cast<std::underlying_type<Quality>::type>(quality) << ","; // Q
134 smsg << std::setw(2) << std::setfill('0') << satellites << ","; // NN
135 smsg << std::setprecision(1) << hdop.value() << ","; // D.D
136 smsg << std::setprecision(1) << height << ",M,"; // H.H, h
137 smsg << std::setprecision(1) << separation << ",M,"; // G.G, g
138 smsg << ","; // AA, RRRR (both optional)
139
140 return finish(smsg);
141}
142
143uint8_t checksum(std::string::const_iterator begin, std::string::const_iterator end)
144{
145 assert(begin <= end);
146 uint8_t sum = 0;
147 for (; begin != end; ++begin) {
148 sum ^= *begin;
149 }
150 return sum;
151}
152
153} // namespace nmea
154} // namespace vanetza
155