24 #ifndef GOBY_MIDDLEWARE_AIS_H
25 #define GOBY_MIDDLEWARE_AIS_H
29 #include <boost/algorithm/string.hpp>
30 #include <boost/circular_buffer.hpp>
31 #include <boost/units/cmath.hpp>
44 AISConverter(
int mmsi,
int history_length = 2) : mmsi_(mmsi), status_reports_(history_length)
46 if (history_length < 2)
47 throw(std::runtime_error(
"History length must be >= 2"));
53 if (status_reports_.empty() ||
54 status.SerializeAsString() != status_reports_.back().SerializeAsString())
55 status_reports_.push_back(
status);
58 bool empty() {
return status_reports_.empty(); }
60 std::pair<goby::util::ais::protobuf::Position, goby::util::ais::protobuf::Voyage>
64 using boost::units::quantity;
68 if (status_reports_.size() == 0)
69 throw(std::runtime_error(
"No status reports"));
74 pos.set_message_id(18);
77 if (
status.global_fix().has_lat())
78 pos.set_lat_with_units(
status.global_fix().lat_with_units());
79 if (
status.global_fix().has_lon())
80 pos.set_lon_with_units(
status.global_fix().lon_with_units());
81 if (
status.pose().has_heading())
82 pos.set_true_heading_with_units(
status.pose().heading_with_units());
84 std::vector<quantity<si::velocity>> sogs;
85 std::vector<double> cogs_cos;
86 std::vector<double> cogs_sin;
88 auto ninety_degrees(90. * boost::units::degree::degrees);
92 status_reports_.front().global_fix().lon_with_units()});
93 for (
int i = 1, n = status_reports_.size(); i < n; ++i)
95 auto& status0 = status_reports_[i - 1];
96 auto& status1 = status_reports_[i];
99 {status0.global_fix().lat_with_units(), status0.global_fix().lon_with_units()});
100 auto xy1 = geo.convert(
101 {status1.global_fix().lat_with_units(), status1.global_fix().lon_with_units()});
103 auto dy = xy1.y - xy0.y;
104 auto dx = xy1.x - xy0.x;
105 auto dt = status1.time_with_units() - status0.time_with_units();
107 decltype(ninety_degrees) cog_angle(boost::units::atan2(dy, dx));
109 sogs.push_back(boost::units::sqrt(dy * dy + dx * dx) / dt);
110 cogs_cos.push_back(boost::units::cos(cog_angle));
111 cogs_sin.push_back(boost::units::sin(cog_angle));
114 std::accumulate(sogs.begin(), sogs.end(), 0. * boost::units::si::meters_per_second);
117 std::accumulate(cogs_cos.begin(), cogs_cos.end(), 0.0) / cogs_cos.size();
119 std::accumulate(cogs_sin.begin(), cogs_sin.end(), 0.0) / cogs_sin.size();
121 if (
status.speed().has_over_ground())
122 pos.set_speed_over_ground_with_units(
status.speed().over_ground_with_units());
124 pos.set_speed_over_ground_with_units(sog_sum /
125 quantity<si::dimensionless>(sogs.size()));
127 decltype(ninety_degrees) cog_heading_mean(
128 boost::units::atan2(quantity<si::dimensionless>(cogs_sin_mean),
129 quantity<si::dimensionless>(cogs_cos_mean)));
131 pos.set_course_over_ground_with_units(ninety_degrees - cog_heading_mean);
134 voy.set_message_id(24);
136 voy.set_name(boost::to_upper_copy(
status.name()));
137 voy.set_type(Voyage::TYPE__OTHER);
139 return std::make_pair(pos, voy);
144 boost::circular_buffer<goby::middleware::frontseat::protobuf::NodeStatus> status_reports_;