23 #include <boost/units/base_units/imperial/foot.hpp> 24 #include <boost/units/base_units/metric/knot.hpp> 26 #include "goby/common/logger.h" 27 #include "goby/util/as.h" 28 #include "goby/util/binary.h" 29 #include "goby/util/linebasedcomms/nmea_sentence.h" 31 #include "iver_driver.h" 38 const int allowed_skew = 10;
50 serial_(iver_config_.serial_port(), iver_config_.serial_baud(),
"\r\n"),
51 frontseat_providing_data_(false), last_frontseat_data_time_(0),
52 frontseat_state_(
gpb::FRONTSEAT_NOT_CONNECTED),
53 reported_mission_mode_(
gpb::IverState::IVER_MODE_UNKNOWN)
55 goby::util::NMEASentence::enforce_talker_length =
false;
59 if (iver_config_.has_ntp_serial_port())
67 void IverFrontSeat::loop()
72 write(request_data.message());
75 frontseat_providing_data_ =
false;
78 while (ntp_serial_ && ntp_serial_->readline(&in))
79 {
glog.is(DEBUG2) &&
glog <<
"NTP says: " << in << std::endl; } }
81 void IverFrontSeat::try_receive()
85 while (serial_.readline(&in))
92 catch (std::exception& e)
94 glog.is(DEBUG1) &&
glog << warn <<
"Failed to handle message: " << e.what()
100 void IverFrontSeat::process_receive(
const std::string& s)
104 signal_raw_from_frontseat(raw_msg);
111 if (nmea.sentence_id() ==
"RMC")
130 if (nmea.size() < RMC_SIZE)
134 ntp_serial_->write(nmea.message_cr_nl());
136 else if (nmea.at(0) ==
"$OSI")
155 MAGNETICDECLINATION = 11,
156 CURRENTMISSIONNAME = 12,
157 REMAININGMISSIONTIME = 13,
163 status_.mutable_global_fix()->set_lat_with_units(nmea.as<
double>(LATITUDE) *
164 boost::units::degree::degrees);
165 status_.mutable_global_fix()->set_lon_with_units(nmea.as<
double>(LONGITUDE) *
166 boost::units::degree::degrees);
168 static const boost::units::metric::knot_base_unit::unit_type knots;
169 status_.set_speed_with_units(nmea.as<
double>(SPEED) * knots);
171 std::string mode_str = nmea.at(MODE);
172 if (mode_str.size() >= 1 && gpb::IverState::IverMissionMode_IsValid(mode_str[0]))
174 reported_mission_mode_ =
static_cast<gpb::IverState::IverMissionMode
>(mode_str[0]);
176 glog <<
"Iver mission mode: " 177 << gpb::IverState::IverMissionMode_Name(reported_mission_mode_)
182 glog.is(WARN) &&
glog <<
"[Parser]: Invalid mode string [" << mode_str <<
"]" 184 reported_mission_mode_ = gpb::IverState::IVER_MODE_UNKNOWN;
187 switch (reported_mission_mode_)
189 case gpb::IverState::IVER_MODE_UNKNOWN:
190 case gpb::IverState::IVER_MODE_STOPPED:
191 frontseat_state_ = gpb::FRONTSEAT_IDLE;
194 case gpb::IverState::IVER_MODE_PARKING:
195 frontseat_state_ = gpb::FRONTSEAT_IN_CONTROL;
199 case gpb::IverState::IVER_MODE_NORMAL:
200 case gpb::IverState::IVER_MODE_MANUAL_OVERRIDE:
201 case gpb::IverState::IVER_MODE_MANUAL_PARKING:
202 case gpb::IverState::IVER_MODE_SERVO_MODE:
203 case gpb::IverState::IVER_MODE_MISSION_MODE:
205 frontseat_state_ = gpb::FRONTSEAT_ACCEPTING_COMMANDS;
209 static const boost::units::imperial::foot_base_unit::unit_type feet;
210 status_.mutable_global_fix()->set_depth_with_units(
211 nmea.as<
double>(COR_DFS) * feet);
212 status_.mutable_global_fix()->set_altitude_with_units(
213 nmea.as<
double>(ALTIMETER) * feet);
214 status_.mutable_pose()->set_heading_with_units(
215 nmea.as<
double>(TRUEHEADING) * boost::units::degree::degrees);
218 gpb::IverState& iver_state = *fs_data.MutableExtension(gpb::iver_state);
219 iver_state.set_mode(reported_mission_mode_);
220 signal_data_from_frontseat(fs_data);
222 else if (nmea.at(0).substr(0, 2) ==
"$C")
235 std::vector<std::string> cfields;
236 boost::split(cfields, nmea.at(0), boost::is_any_of(
"CPRTD"));
238 status_.mutable_pose()->set_roll_with_units(goby::util::as<double>(cfields.at(ROLL)) *
239 boost::units::degree::degrees);
240 status_.mutable_pose()->set_pitch_with_units(goby::util::as<double>(cfields.at(PITCH)) *
241 boost::units::degree::degrees);
244 compute_missing(&status_);
246 data.mutable_node_status()->CopyFrom(status_);
247 signal_data_from_frontseat(data);
248 frontseat_providing_data_ =
true;
253 glog.is(DEBUG1) &&
glog <<
"[Parser]: Ignoring sentence: " << s << std::endl;
258 glog.is(WARN) &&
glog <<
"[Parser]: Invalid NMEA sentence: " << e.what() << std::endl;
264 if (command.HasExtension(gpb::iver_command))
267 gpb::IverExtraCommands::IverCommand type = iver_command.command();
270 case gpb::IverExtraCommands::UNKNOWN_COMMAND:
break;
271 case gpb::IverExtraCommands::START_MISSION:
272 if (iver_command.has_mission() && !iver_command.mission().empty())
275 const int ignore_gps = 0;
276 const int ignore_sounder = 0;
277 const int ignore_pressure_transducer = 0;
278 const int mission_type = 0;
279 const std::string srp_mission =
"";
280 nmea.push_back(ignore_gps);
281 nmea.push_back(ignore_sounder);
282 nmea.push_back(ignore_pressure_transducer);
283 nmea.push_back(mission_type);
284 nmea.push_back(iver_command.mission());
285 nmea.push_back(srp_mission);
286 write(nmea.message());
290 glog.is(DEBUG1) &&
glog <<
"Refusing to start empty mission" << std::endl;
293 case gpb::IverExtraCommands::STOP_MISSION:
297 write(nmea.message());
303 if(command.has_desired_course())
308 double heading = command.desired_course().heading_with_units() / boost::units::degree::degrees;
309 while(heading >= 360) heading -= 360;
310 while(heading < 0) heading += 360;
312 nmea.push_back(tenths_precision_str(heading));
313 using boost::units::quantity;
314 typedef boost::units::imperial::foot_base_unit::unit_type feet;
317 if (iver_config_.remote_helm_version_major() < 5)
318 nmea.push_back(tenths_precision_str(
319 command.desired_course().depth_with_units<quantity<feet> >().value()));
322 tenths_precision_str(command.desired_course()
323 .depth_with_units<quantity<boost::units::si::length> >()
326 nmea.push_back(tenths_precision_str(iver_config_.max_pitch_angle_degrees()));
327 typedef boost::units::metric::knot_base_unit::unit_type knots;
328 nmea.push_back(tenths_precision_str(command.desired_course().speed_with_units<quantity<knots> >().value()));
329 const int time_out = 5;
330 nmea.push_back(time_out);
332 write(nmea.message());
341 void IverFrontSeat::send_raw_to_frontseat(
const gpb::FrontSeatRaw& data) { write(data.raw()); }
343 bool IverFrontSeat::frontseat_providing_data()
const {
return frontseat_providing_data_; }
345 goby::moos::protobuf::FrontSeatState IverFrontSeat::frontseat_state()
const 347 return frontseat_state_;
350 void IverFrontSeat::write(
const std::string& s)
354 signal_raw_to_frontseat(raw_msg);
356 serial_.write(s +
"\r\n");
359 boost::units::quantity<boost::units::si::time> IverFrontSeat::nmea_time_to_seconds(
double nmea_time,
362 namespace si = boost::units::si;
366 ptime unix_epoch(date(1970, 1, 1), time_duration(0, 0, 0));
368 int hours = nmea_time / 10000;
369 nmea_time -= hours * 10000;
370 int minutes = nmea_time / 100;
371 nmea_time -= minutes * 100;
372 int seconds = nmea_time;
373 long micro_s = (nmea_time - seconds) * 1e6;
375 int day = 0, month = 0, year = 0;
377 if (nmea_date > 999999)
379 day = nmea_date / 100000;
380 nmea_date -= day * 100000;
381 month = nmea_date / 1000;
382 nmea_date -= month * 1000;
387 day = nmea_date / 10000;
388 nmea_date -= day * 10000;
389 month = nmea_date / 100;
390 nmea_date -= month * 100;
396 ptime given_time(date(year + 2000, month, day),
397 time_duration(hours, minutes, seconds) + microseconds(micro_s));
399 if (given_time == not_a_date_time)
401 return -1 * si::seconds;
405 date_duration date_diff = given_time.date() - date(1970, 1, 1);
406 time_duration time_diff = given_time.time_of_day();
408 return (date_diff.days() * 24 * 3600 + time_diff.total_seconds() +
409 static_cast<double>(time_diff.fractional_seconds()) /
410 time_duration::ticks_per_second()) *
414 catch (std::exception& e)
416 glog.is(DEBUG1) &&
glog <<
"Invalid time: " << e.what() << std::endl;
417 return -1 * si::seconds;
421 boost::units::quantity<boost::units::degree::plane_angle>
422 IverFrontSeat::nmea_geo_to_degrees(
double nmea_geo,
char hemi)
425 double deg_int = std::floor(nmea_geo / 1e2);
426 double deg_frac = (nmea_geo - (deg_int * 1e2)) / 60;
428 double deg = std::numeric_limits<double>::quiet_NaN();
430 if (hemi ==
'N' || hemi ==
'E')
431 deg = (deg_int + deg_frac);
432 else if (hemi ==
'S' || hemi ==
'W')
433 deg = -(deg_int + deg_frac);
435 return deg * boost::units::degree::degrees;
provides a basic client for line by line text based communications over a 8N1 tty (such as an RS-232 ...
double goby_time< double >()
Returns current UTC time as seconds and fractional seconds since 1970-01-01 00:00:00.
ReturnType goby_time()
Returns current UTC time as a boost::posix_time::ptime.
common::FlexOstream glog
Access the Goby logger through this object.