25 #include <boost/algorithm/string.hpp> 26 #include <boost/assign.hpp> 27 #include <boost/foreach.hpp> 29 #include <dccl/bitset.h> 31 #include "goby/common/logger.h" 32 #include "goby/util/binary.h" 33 #include "goby/util/sci.h" 35 #include "driver_exception.h" 36 #include "mm_driver.h" 42 using goby::util::hex_decode;
43 using goby::util::hex_encode;
45 using google::protobuf::uint32;
49 using goby::common::nmea_time2ptime;
51 const boost::posix_time::time_duration goby::acomms::MMDriver::MODEM_WAIT =
52 boost::posix_time::seconds(5);
53 const boost::posix_time::time_duration goby::acomms::MMDriver::WAIT_AFTER_REBOOT =
54 boost::posix_time::seconds(2);
55 const boost::posix_time::time_duration
56 goby::acomms::MMDriver::HYDROID_GATEWAY_GPS_REQUEST_INTERVAL = boost::posix_time::seconds(30);
57 const std::string goby::acomms::MMDriver::SERIAL_DELIMITER =
"\r";
58 const unsigned goby::acomms::MMDriver::PACKET_FRAME_COUNT[] = {1, 3, 3, 2, 2, 8};
59 const unsigned goby::acomms::MMDriver::PACKET_SIZE[] = {32, 64, 64, 256, 256, 256};
66 : last_write_time_(
goby_time()), waiting_for_modem_(false), startup_done_(false),
67 global_fail_count_(0), present_fail_count_(0), clock_set_(false),
68 last_hydroid_gateway_gps_request_(
goby_time()), is_hydroid_gateway_(false),
69 expected_remaining_caxst_(0), expected_remaining_cacst_(0), expected_ack_destination_(0),
70 local_cccyc_(false), last_keep_alive_time_(0), using_application_acks_(false),
71 application_ack_max_frames_(0), next_frame_(0), serial_fd_(-1)
78 glog.is(DEBUG1) &&
glog << group(glog_out_group()) <<
"Goby Micro-Modem driver starting up." 83 glog.is(DEBUG1) &&
glog << group(glog_out_group())
84 <<
" ... driver is already started, not restarting." << std::endl;
91 if (!cfg.has_line_delimiter())
92 driver_cfg_.set_line_delimiter(SERIAL_DELIMITER);
94 if (!cfg.has_serial_baud())
95 driver_cfg_.set_serial_baud(DEFAULT_BAUD);
98 if (driver_cfg_.HasExtension(micromodem::protobuf::Config::hydroid_gateway_id))
99 set_hydroid_gateway_prefix(
100 driver_cfg_.GetExtension(micromodem::protobuf::Config::hydroid_gateway_id));
102 using_application_acks_ =
103 driver_cfg_.GetExtension(micromodem::protobuf::Config::use_application_acks);
104 application_ack_max_frames_ = 32;
105 if (using_application_acks_)
110 if (driver_cfg_.connection_type() == protobuf::DriverConfig::CONNECTION_SERIAL)
131 nmea.push_back(driver_cfg_.modem_id());
132 nmea.push_back(driver_cfg_.modem_id());
134 append_to_write_queue(nmea);
137 while (!out_.empty())
145 clk_mode_ = micromodem::protobuf::NO_SYNC_TO_PPS_AND_CCCLK_BAD;
156 startup_done_ =
true;
159 void goby::acomms::MMDriver::set_rts(
bool state)
162 if (ioctl(serial_fd_, TIOCMGET, &status) == -1)
164 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
165 <<
"IOCTL failed: " << strerror(errno) << std::endl;
168 glog.is(DEBUG1) &&
glog << group(glog_out_group()) <<
"Setting RTS to " 169 << (state ?
"high" :
"low") << std::endl;
173 status &= ~TIOCM_RTS;
174 if (ioctl(serial_fd_, TIOCMSET, &status) == -1)
176 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
177 <<
"IOCTL failed: " << strerror(errno) << std::endl;
181 bool goby::acomms::MMDriver::query_rts()
184 if (ioctl(serial_fd_, TIOCMGET, &status) == -1)
186 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
187 <<
"IOCTL failed: " << strerror(errno) << std::endl;
189 return (status & TIOCM_RTS);
194 for (
int i = 0, n = cfg.ExtensionSize(micromodem::protobuf::Config::nvram_cfg); i < n; ++i)
195 write_single_cfg(cfg.GetExtension(micromodem::protobuf::Config::nvram_cfg, i));
198 void goby::acomms::MMDriver::initialize_talkers()
200 boost::assign::insert(sentence_id_map_)(
"ACK", ACK)(
"DRQ", DRQ)(
"RXA", RXA)(
"RXD", RXD)(
201 "RXP", RXP)(
"TXD", TXD)(
"TXA", TXA)(
"TXP", TXP)(
"TXF", TXF)(
"CYC", CYC)(
"MPC", MPC)(
202 "MPA", MPA)(
"MPR", MPR)(
"RSP", RSP)(
"MSC", MSC)(
"MSA", MSA)(
"MSR", MSR)(
"EXL", EXL)(
203 "MEC", MEC)(
"MEA", MEA)(
"MER", MER)(
"MUC", MUC)(
"MUA", MUA)(
"MUR", MUR)(
"PDT", PDT)(
204 "PNT", PNT)(
"TTA", TTA)(
"MFD", MFD)(
"CLK", CLK)(
"CFG", CFG)(
"AGC", AGC)(
"BBD", BBD)(
205 "CFR", CFR)(
"CST", CST)(
"MSG", MSG)(
"REV", REV)(
"DQF", DQF)(
"SHF", SHF)(
"MFD", MFD)(
206 "SNR", SNR)(
"DOP", DOP)(
"DBG", DBG)(
"FFL", FFL)(
"FST", FST)(
"ERR", ERR)(
"TOA", TOA)(
207 "XST", XST)(
"TDP", TDP)(
"RDP", RDP)(
"TMS", TMS)(
"TMQ", TMQ)(
"TMG", TMG)(
"SWP", SWP)(
"MSQ",
210 boost::assign::insert(talker_id_map_)(
"CC", CC)(
"CA", CA)(
"SN", SN)(
"GP", GP);
213 boost::assign::insert(description_map_)(
"$CAACK",
"Acknowledgment of a transmitted packet")(
214 "$CADRQ",
"Data request message, modem to host")(
"$CARXA",
215 "Received ASCII message, modem to host")(
216 "$CARXD",
"Received binary message, modem to host")(
217 "$CARXP",
"Incoming packet detected, modem to host")(
218 "$CCTXD",
"Transmit binary data message, host to modem")(
219 "$CCTXA",
"Transmit ASCII data message, host to modem")(
220 "$CATXD",
"Echo back of transmit binary data message")(
221 "$CATXA",
"Echo back of transmit ASCII data message")(
222 "$CATXP",
"Start of packet transmission, modem to host")(
223 "$CATXF",
"End of packet transmission, modem to host")(
224 "$CCCYC",
"Network Cycle Initialization Command")(
225 "$CACYC",
"Echo of Network Cycle Initialization command")(
226 "$CCMPC",
"Mini-Packet Ping command, host to modem")(
227 "$CAMPC",
"Echo of Ping command, modem to host")(
"$CAMPA",
228 "A Ping has been received, modem to host")(
229 "$CAMPR",
"Reply to Ping has been received, modem to host")(
230 "$CCRSP",
"Pinging with an FM sweep")(
"$CARSP",
"Respose to FM sweep ping command")(
231 "$CCMSC",
"Sleep command, host to modem")(
"$CAMSC",
"Echo of Sleep command, modem to host")(
232 "$CAMSA",
"A Sleep was received acoustically, modem to host")(
233 "$CAMSR",
"A Sleep reply was received, modem to host")(
234 "$CCEXL",
"External hardware control command, local modem only")(
235 "$CCMEC",
"External hardware control command, host to modem")(
236 "$CAMEC",
"Echo of hardware control command, modem to host")(
237 "$CAMEA",
"Hardware control command received acoustically")(
238 "$CAMER",
"Hardware control command reply received")(
239 "$CCMUC",
"User Mini-Packet command, host to modem")(
240 "$CAMUC",
"Echo of user Mini-Packet, modem to host")(
241 "$CAMUA",
"Mini-Packet received acoustically, modem to host")(
242 "$CAMUR",
"Reply to Mini-Packet received, modem to host")(
243 "$CCPDT",
"Ping REMUS digital transponder, host to modem")(
244 "$CCPNT",
"Ping narrowband transponder, host to modem")(
245 "$SNTTA",
"Transponder travel times, modem to host")(
246 "$SNMFD",
"Nav matched filter information, modem to host")(
247 "$CCCLK",
"Set clock, host to modem")(
"$CCCFG",
248 "Set NVRAM configuration parameter, host to modem")(
249 "$CACFG",
"Echo of NVRAM configuration parameter, modem to host")(
250 "$CCCFQ",
"Query configuration parameter, host to modem")(
"$CCAGC",
251 "Set automatic gain control")(
252 "$CABBD",
"Dump of baseband data to serial port, modem to host")(
253 "$CCCFR",
"Measure noise level at receiver, host to modem")(
254 "$SNCFR",
"Noise report, modem to host")(
"$CACST",
255 "Communication cycle receive statistics")(
256 "$CAXST",
"Communication cycle transmit statistics")(
257 "$CAMSG",
"Transaction message, modem to host")(
"$CAREV",
258 "Software revision message, modem to host")(
259 "$CADQF",
"Data quality factor information, modem to host")(
260 "$CASHF",
"Shift information, modem to host")(
261 "$CAMFD",
"Comms matched filter information, modem to host")(
262 "$CACLK",
"Time/Date message, modem to host")(
"$CASNR",
263 "SNR statistics on the incoming PSK packet")(
264 "$CADOP",
"Doppler speed message, modem to host")(
265 "$CADBG",
"Low level debug message, modem to host")(
"$CAERR",
266 "Error message, modem to host")(
267 "$CATOA",
"Message from modem to host reporting time of arrival of the previous packet, " 268 "and the synchronous timing mode used to determine that time.")(
269 "$CCTDP",
"Transmit (downlink) data packet with Flexible Data Protocol (Micro-Modem 2)")(
270 "$CCTDP",
"Response to CCTDP for Flexible Data Protocol (Micro-Modem 2)")(
271 "$CARDP",
"Reception of a FDP downlink data packet (Micro-Modem 2)")(
272 "$CCTMS",
"Set the modem clock (Micro-Modem 2)")(
273 "$CATMS",
"Response to set clock command (Micro-Modem 2)")(
274 "$CCTMQ",
"Query modem time command (Micro-Modem 2)")(
275 "$CATMQ",
"Response to time query (CCTMQ) command (Micro-Modem 2)")(
276 "$CATMG",
"Informational message about timing source, printed when timing sources change " 277 "(Micro-Modem 2)")(
"$CCSWP",
"Send an FM sweep")(
278 "$CCMSQ",
"Send a maximal-length sequence");
281 boost::assign::insert(cfg_map_)(
"AGC",
"Turn on automatic gain control")(
282 "AGN",
"Analog Gain (50 is 6 dB, 250 is 30 dB)")(
284 "Always Send Data. Tells the modem to send test data when the user does not provide any.")(
285 "BBD",
"PSK Baseband data dump to serial port")(
286 "BND",
"Frequency Bank (1, 2, 3 for band A, B, or C, 0 for user-defined PSK only band)")(
287 "BR1",
"Baud rate for serial port 1 (3 = 19200)")(
288 "BR2",
"Baud rate for serial port 2 (3 = 19200)")(
"BRN",
"Run bootloader at next revert")(
289 "BSP",
"Boot loader serial port")(
290 "BW0",
"Bandwidth for Band 0 PSK CPR 0-1 Coprocessor power toggle switch 1")(
291 "CRL",
"Cycle init reverb lockout (ms) 50")(
"CST",
"Cycle statistics message 1")(
292 "CTO",
"Cycle init timeout (sec) 10")(
"DBG",
"Enable low-level debug messages 0")(
293 "DGM",
"Diagnostic messaging 0")(
"DOP",
"Whether or not to send the $CADOP message")(
294 "DQF",
"Whether or not to send the $CADQF message")(
"DTH",
295 "Matched filter signal threshold, FSK")(
296 "DTO",
"Data request timeout (sec)")(
"DTP",
"Matched filter signal threshold, PSK")(
297 "ECD",
"Int Delay at end of cycle (ms)")(
"EFF",
"Feedforward taps for the LMS equalizer")(
298 "EFB",
"Feedback taps for the LMS equalizer")(
"FMD",
"PSK FM probe direction,0 up, 1 down")(
299 "FML",
"PSK FM probe length, symbols")(
"FC0",
"Carrier at Band 0 PSK only")(
300 "GPS",
"GPS parser on aux. serial port")(
"HFC",
301 "Hardware flow control on main serial port")(
302 "MCM",
"Enable current mode hydrophone power supply on Rev. C Multi-Channel Analog Board. " 303 "Must be set to 1 for Rev. B Multi-Channel Analog Board.")(
304 "MFD",
"Whether or not to send the MFD messages")(
"IRE",
305 "Print impulse response of FM sweep")(
306 "MFC",
"MFD calibration value (samples)")(
"MFD",
"Whether or not to send the MFD messages")(
307 "MOD",
"0 sends FSK minipacket, 1 sends PSK minipacket")(
308 "MPR",
"Enable power toggling on Multi-Channel Analog Board")(
309 "MSE",
"Print symbol mean squared error (dB) from the LMS equalizer")(
310 "MVM",
"Enable voltage mode hydrophone power supply on Multi-Channel Analog Board")(
311 "NDT",
"Detect threshold for nav detector")(
"NPT",
"Power threshold for nav detector")(
312 "NRL",
"Navigation reverb lockout (ms)")(
"NRV",
"Number of CTOs before hard reboot")(
313 "PAD",
"Power-amp delay (ms)")(
"PCM",
"Passband channel mask")(
314 "POW",
"Detection power threshold (dB) PRL Int Packet reverb lockout (ms)")(
315 "PTH",
"Matched filter detector power threshold")(
"PTO",
"Packet timeout (sec)")(
316 "REV",
"Whether or not to send the $CAREV message")(
317 "SGP",
"Show GPS messages on main serial port")(
318 "RXA",
"Whether or not to send the $CARXA message")(
319 "RXD",
"Whether or not to send the $CARXD message")(
320 "RXP",
"Whether or not to send the $CARXP message")(
"SCG",
"Set clock from GPS")(
321 "SHF",
"Whether or not to send the $CASHF message")(
322 "SNR",
"Turn on SNR stats for PSK comms")(
"SNV",
"Synchronous transmission of packets")(
323 "SRC",
"Default Source Address")(
"TAT",
"Navigation turn-around-time (msec)")(
324 "TOA",
"Display time of arrival of a packet (sec)")(
"TXD",
"Delay before transmit (ms)")(
325 "TXP",
"Turn on start of transmit message")(
"TXF",
"Turn on end of transmit message")(
326 "XST",
"Turn on transmit stats message, CAXST");
329 void goby::acomms::MMDriver::set_hydroid_gateway_prefix(
int id)
331 is_hydroid_gateway_ =
true;
333 hydroid_gateway_gps_request_ =
"#G" + as<std::string>(id) +
"\r\n";
334 hydroid_gateway_modem_prefix_ =
"#M" + as<std::string>(id);
336 glog.is(DEBUG1) &&
glog << group(glog_out_group())
337 <<
"Setting the hydroid_gateway buoy prefix: out=" 338 << hydroid_gateway_modem_prefix_ << std::endl;
341 void goby::acomms::MMDriver::set_clock()
343 glog.is(DEBUG1) &&
glog << group(glog_out_group()) <<
"Setting the Micro-Modem clock." 346 boost::posix_time::ptime p = goby_time();
353 double(p.time_of_day().fractional_seconds()) / p.time_of_day().ticks_per_second();
354 while (frac_sec > 100e-3 || frac_sec < 50e-3)
360 double(p.time_of_day().fractional_seconds()) / p.time_of_day().ticks_per_second();
363 if (revision_.mm_major >= 2)
366 std::stringstream iso_time;
367 boost::posix_time::time_facet* facet =
368 new boost::posix_time::time_facet(
"%Y-%m-%dT%H:%M:%SZ");
369 iso_time.imbue(std::locale(iso_time.getloc(), facet));
370 iso_time << (p + boost::posix_time::seconds(1));
371 nmea.push_back(iso_time.str());
374 append_to_write_queue(nmea);
379 nmea.push_back(
int(p.date().year()));
380 nmea.push_back(
int(p.date().month()));
381 nmea.push_back(
int(p.date().day()));
382 nmea.push_back(
int(p.time_of_day().hours()));
383 nmea.push_back(
int(p.time_of_day().minutes()));
384 nmea.push_back(
int(p.time_of_day().seconds() + 1));
386 append_to_write_queue(nmea);
393 void goby::acomms::MMDriver::write_cfg()
398 if (!is_hydroid_gateway_ && driver_cfg_.GetExtension(micromodem::protobuf::Config::reset_nvram))
399 write_single_cfg(
"ALL,0");
402 write_single_cfg(
"CST,1");
404 for (
int i = 0, n = driver_cfg_.ExtensionSize(micromodem::protobuf::Config::nvram_cfg); i < n;
406 write_single_cfg(driver_cfg_.GetExtension(micromodem::protobuf::Config::nvram_cfg, i));
409 write_single_cfg(
"SRC," + as<std::string>(driver_cfg_.modem_id()));
412 write_single_cfg(
"REV,1");
415 write_single_cfg(
"RXP,1");
418 void goby::acomms::MMDriver::write_single_cfg(
const std::string& s)
423 const std::string::size_type MM1_CFG_LENGTH = 3;
424 if (s.find(
',') == MM1_CFG_LENGTH)
425 nmea.push_back(boost::to_upper_copy(s));
429 append_to_write_queue(nmea);
432 void goby::acomms::MMDriver::query_all_cfg()
434 if (revision_.mm_major >= 2)
437 append_to_write_queue(nmea);
442 append_to_write_queue(nmea);
453 startup_done_ =
false;
468 if (!clock_set_ && out_.empty())
473 if (last_keep_alive_time_ +
474 driver_cfg_.GetExtension(micromodem::protobuf::Config::keep_alive_seconds) <=
478 nmea.push_back(
"SRC");
479 append_to_write_queue(nmea);
480 last_keep_alive_time_ = now;
492 if (is_hydroid_gateway_)
493 in.erase(0, HYDROID_GATEWAY_PREFIX_LENGTH);
499 process_receive(nmea);
501 catch (std::exception& e)
503 glog.is(DEBUG1) &&
glog << group(glog_in_group()) << warn
504 <<
"Failed to handle message: " << e.what() << std::endl;
509 if (is_hydroid_gateway_ &&
510 last_hydroid_gateway_gps_request_ + HYDROID_GATEWAY_GPS_REQUEST_INTERVAL < goby_time())
513 last_hydroid_gateway_gps_request_ = goby_time();
523 transmit_msg_.CopyFrom(msg);
527 glog.is(DEBUG1) &&
glog << group(glog_out_group()) <<
"Beginning to initiate transmission." 533 switch (transmit_msg_.type())
535 case protobuf::ModemTransmission::DATA: cccyc(&transmit_msg_);
break;
536 case protobuf::ModemTransmission::DRIVER_SPECIFIC:
538 switch (transmit_msg_.GetExtension(micromodem::protobuf::type))
540 case micromodem::protobuf::MICROMODEM_MINI_DATA: ccmuc(&transmit_msg_);
break;
541 case micromodem::protobuf::MICROMODEM_FLEXIBLE_DATA:
542 cctdp(&transmit_msg_);
544 case micromodem::protobuf::MICROMODEM_TWO_WAY_PING: ccmpc(transmit_msg_);
break;
545 case micromodem::protobuf::MICROMODEM_REMUS_LBL_RANGING:
546 ccpdt(transmit_msg_);
548 case micromodem::protobuf::MICROMODEM_NARROWBAND_LBL_RANGING:
549 ccpnt(transmit_msg_);
551 case micromodem::protobuf::MICROMODEM_HARDWARE_CONTROL:
552 ccmec(transmit_msg_);
554 case micromodem::protobuf::MICROMODEM_GENERIC_LBL_RANGING:
555 ccpgt(transmit_msg_);
557 case micromodem::protobuf::MICROMODEM_FM_SWEEP: ccswp(transmit_msg_);
break;
558 case micromodem::protobuf::MICROMODEM_M_SEQUENCE: ccmsq(transmit_msg_);
break;
561 glog << group(glog_out_group()) << warn
562 <<
"Not initiating transmission because we were given an invalid " 563 "DRIVER_SPECIFIC transmission type for the Micro-Modem:" 564 << transmit_msg_ << std::endl;
571 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
572 <<
"Not initiating transmission because we were given an " 573 "invalid transmission type for the base Driver:" 574 << transmit_msg_ << std::endl;
581 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
582 <<
"Failed to initiate transmission: " << e.what() << std::endl;
588 glog.is(DEBUG1) &&
glog << group(glog_out_group()) <<
"\tthis is a DATA transmission" 594 msg->set_max_num_frames(PACKET_FRAME_COUNT[msg->rate()]);
595 msg->set_max_frame_bytes(PACKET_SIZE[msg->rate()]);
597 cache_outgoing_data(msg);
600 const bool is_local_cycle = msg->src() == driver_cfg_.modem_id();
601 if (!(is_local_cycle && (msg->frame_size() == 0 || msg->frame(0) ==
"")))
606 nmea.push_back(msg->src());
607 nmea.push_back(msg->dest());
608 nmea.push_back(msg->rate());
611 ? static_cast<int>(msg->ack_requested())
613 nmea.push_back(is_local_cycle ? msg->frame_size()
614 : msg->max_num_frames());
616 append_to_write_queue(nmea);
619 expected_remaining_caxst_ = (msg->rate() == 0 && is_local_cycle) ? 1 : 0;
623 glog.is(DEBUG1) &&
glog << group(glog_out_group())
624 <<
"Not initiating transmission because we have no data to send" 631 glog.is(DEBUG1) &&
glog << group(glog_out_group())
632 <<
"\tthis is a MICROMODEM_MINI_DATA transmission" << std::endl;
634 const int MINI_NUM_FRAMES = 1;
635 const int MINI_PACKET_SIZE = 2;
636 msg->set_max_num_frames(MINI_NUM_FRAMES);
637 msg->set_max_frame_bytes(MINI_PACKET_SIZE);
639 cache_outgoing_data(msg);
641 if (msg->frame_size() > 0 && msg->frame(0).size())
643 glog.is(DEBUG1) &&
glog <<
"Mini-data message: " << *msg << std::endl;
644 msg->mutable_frame(0)->resize(MINI_PACKET_SIZE);
645 glog.is(DEBUG1) &&
glog <<
"Mini-data message after resize: " << *msg << std::endl;
647 if ((msg->frame(0)[0] & 0x1F) != msg->frame(0)[0])
649 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
650 <<
"MINI transmission can only be 13 bits; top three bits " 651 "passed were *not* zeros, so discarding. You should AND " 652 "your two bytes with 0x1FFF to get 13 bits" 654 msg->mutable_frame(0)->at(0) &= 0x1F;
659 nmea.push_back(msg->src());
660 nmea.push_back(msg->dest());
661 nmea.push_back(goby::util::hex_encode(msg->frame(0)));
662 append_to_write_queue(nmea);
666 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
667 <<
"MINI transmission failed: no data provided" << std::endl;
673 glog.is(DEBUG1) &&
glog << group(glog_out_group())
674 <<
"\tthis is a MICROMODEM_FLEXIBLE_DATA transmission" << std::endl;
676 const int FDP_NUM_FRAMES = 1;
677 const int FDP_MAX_PACKET_SIZE = 100;
679 msg->set_max_num_frames(FDP_NUM_FRAMES);
682 if (!msg->has_max_frame_bytes())
683 msg->set_max_frame_bytes(FDP_MAX_PACKET_SIZE);
685 cache_outgoing_data(msg);
687 if (msg->frame_size() > 0 && msg->frame(0).size())
689 glog.is(DEBUG1) &&
glog <<
"FDP data message: " << *msg << std::endl;
693 nmea.push_back(msg->dest());
694 nmea.push_back(msg->rate());
695 if (msg->ack_requested())
697 if (!using_application_acks_)
700 glog <<
"Firmware ACK not yet supported for FDP. Enable application acks " 701 "(use_application_acks: true) for ACK capability." 706 expected_ack_destination_ = msg->dest();
707 frames_waiting_for_ack_.insert(next_frame_++);
714 nmea.push_back(goby::util::hex_encode(msg->frame(0)));
715 append_to_write_queue(nmea);
719 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
720 <<
"FDP transmission failed: no data provided" << std::endl;
726 glog.is(DEBUG1) &&
glog << group(glog_out_group())
727 <<
"\tthis is a MICROMODEM_TWO_WAY_PING transmission" << std::endl;
731 nmea.push_back(msg.src());
732 nmea.push_back(msg.dest());
734 append_to_write_queue(nmea);
739 glog.is(DEBUG1) &&
glog << group(glog_out_group())
740 <<
"\tthis is a MICROMODEM_REMUS_LBL_RANGING transmission" << std::endl;
742 last_lbl_type_ = micromodem::protobuf::MICROMODEM_REMUS_LBL_RANGING;
746 driver_cfg_.GetExtension(micromodem::protobuf::Config::remus_lbl);
748 params.MergeFrom(msg.GetExtension(micromodem::protobuf::remus_lbl));
750 uint32 tat = params.turnaround_ms();
751 if (static_cast<unsigned>(nvram_cfg_[
"TAT"]) != tat)
752 write_single_cfg(
"TAT," + as<std::string>(tat));
757 nmea.push_back(msg.src() % 4 + 1);
761 nmea.push_back(
int((params.lbl_max_range() * 2.0 / ROUGH_SPEED_OF_SOUND) * 1000 + tat));
762 nmea.push_back(params.enable_beacons() >> 0 & 1);
763 nmea.push_back(params.enable_beacons() >> 1 & 1);
764 nmea.push_back(params.enable_beacons() >> 2 & 1);
765 nmea.push_back(params.enable_beacons() >> 3 & 1);
766 append_to_write_queue(nmea);
771 glog.is(DEBUG1) &&
glog << group(glog_out_group())
772 <<
"\tthis is a MICROMODEM_NARROWBAND_LBL_RANGING transmission" 775 last_lbl_type_ = micromodem::protobuf::MICROMODEM_NARROWBAND_LBL_RANGING;
779 driver_cfg_.GetExtension(micromodem::protobuf::Config::narrowband_lbl);
781 params.MergeFrom(msg.GetExtension(micromodem::protobuf::narrowband_lbl));
783 uint32 tat = params.turnaround_ms();
784 if (static_cast<unsigned>(nvram_cfg_[
"TAT"]) != tat)
785 write_single_cfg(
"TAT," + as<std::string>(tat));
789 nmea.push_back(params.transmit_freq());
790 nmea.push_back(params.transmit_ping_ms());
791 nmea.push_back(params.receive_ping_ms());
793 static_cast<int>((params.lbl_max_range() * 2.0 / ROUGH_SPEED_OF_SOUND) * 1000 + tat));
796 const int MAX_NUMBER_RX_BEACONS = 4;
798 int number_rx_freq_provided = std::min(MAX_NUMBER_RX_BEACONS, params.receive_freq_size());
800 for (
int i = 0, n = std::max(MAX_NUMBER_RX_BEACONS, number_rx_freq_provided); i < n; ++i)
802 if (i < number_rx_freq_provided)
803 nmea.push_back(params.receive_freq(i));
808 nmea.push_back(static_cast<int>(params.transmit_flag()));
809 append_to_write_queue(nmea);
814 glog.is(DEBUG1) &&
glog << group(glog_out_group())
815 <<
"\tthis is a MICROMODEM_HARDWARE_CONTROL transmission" << std::endl;
821 nmea.push_back(msg.src());
822 nmea.push_back(msg.dest());
823 nmea.push_back(static_cast<int>(params.line()));
824 nmea.push_back(static_cast<int>(params.mode()));
825 nmea.push_back(static_cast<int>(params.arg()));
827 append_to_write_queue(nmea);
832 glog.is(DEBUG1) &&
glog << group(glog_out_group())
833 <<
"\tthis is a MICROMODEM_GENERIC_LBL_RANGING transmission" 836 last_lbl_type_ = micromodem::protobuf::MICROMODEM_GENERIC_LBL_RANGING;
840 driver_cfg_.GetExtension(micromodem::protobuf::Config::generic_lbl);
842 params.MergeFrom(msg.GetExtension(micromodem::protobuf::generic_lbl));
844 uint32 tat = params.turnaround_ms();
845 if (static_cast<unsigned>(nvram_cfg_[
"TAT"]) != tat)
846 write_single_cfg(
"TAT," + as<std::string>(tat));
851 nmea.push_back(params.transmit_freq());
852 nmea.push_back(params.n_bits());
853 nmea.push_back(params.transmit_seq_code());
855 nmea.push_back(
int((params.lbl_max_range() * 2.0 / ROUGH_SPEED_OF_SOUND) * 1000 + tat));
856 nmea.push_back(params.receive_freq());
859 const int MAX_NUMBER_RX_BEACONS = 4;
860 int number_rx_seq_codes_provided = params.receive_seq_code_size();
861 for (
int i = 0; i < MAX_NUMBER_RX_BEACONS; ++i)
863 if (i < number_rx_seq_codes_provided)
864 nmea.push_back(params.receive_seq_code(i));
869 nmea.push_back(params.bandwidth());
871 append_to_write_queue(nmea);
876 glog.is(DEBUG1) &&
glog << group(glog_out_group())
877 <<
"\tthis is a MICROMODEM_FM_SWEEP transmission" << std::endl;
881 driver_cfg_.GetExtension(micromodem::protobuf::Config::fm_sweep);
882 params.MergeFrom(msg.GetExtension(micromodem::protobuf::fm_sweep));
886 nmea.push_back(static_cast<int>(params.start_freq() * 10));
887 nmea.push_back(static_cast<int>(params.stop_freq() * 10));
888 nmea.push_back(static_cast<int>(std::abs(params.start_freq() - params.stop_freq())));
889 nmea.push_back(static_cast<int>(params.duration_ms() * 10));
890 nmea.push_back(params.number_repetitions());
891 nmea.push_back(static_cast<int>(params.repetition_period_ms() * 10));
893 append_to_write_queue(nmea);
898 glog.is(DEBUG1) &&
glog << group(glog_out_group())
899 <<
"\tthis is a MICROMODEM_M_SEQUENCE transmission" << std::endl;
903 driver_cfg_.GetExtension(micromodem::protobuf::Config::m_sequence);
904 params.MergeFrom(msg.GetExtension(micromodem::protobuf::m_sequence));
908 nmea.push_back(params.seqlen_bits());
909 nmea.push_back(params.number_repetitions());
910 nmea.push_back(params.carrier_cycles_per_chip());
911 nmea.push_back(params.carrier_freq());
913 append_to_write_queue(nmea);
920 void goby::acomms::MMDriver::append_to_write_queue(
const NMEASentence& nmea)
922 out_.push_back(nmea);
926 void goby::acomms::MMDriver::try_send()
933 bool resend = waiting_for_modem_ && (last_write_time_ <= (goby_time() - MODEM_WAIT));
934 if (!waiting_for_modem_)
941 glog << group(glog_out_group()) <<
"resending last command; no serial ack in " 942 << (goby_time() - last_write_time_).total_seconds() <<
" second(s). " << std::endl;
943 ++global_fail_count_;
945 if (global_fail_count_ == MAX_FAILS_BEFORE_DEAD)
948 global_fail_count_ = 0;
950 protobuf::ModemDriverStatus::MODEM_NOT_RESPONDING));
957 increment_present_fail();
963 present_fail_exceeds_retries();
971 raw_msg.set_raw(nmea.message());
972 raw_msg.set_description(description_map_[nmea.front()]);
974 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << hydroid_gateway_modem_prefix_
975 << raw_msg.raw() <<
"\n" 976 <<
"^ " <<
magenta << raw_msg.description() <<
nocolor << std::endl;
980 modem_write(hydroid_gateway_modem_prefix_ + raw_msg.raw() +
"\r\n");
982 waiting_for_modem_ =
true;
983 last_write_time_ = goby_time();
986 void goby::acomms::MMDriver::increment_present_fail()
988 ++present_fail_count_;
989 if (present_fail_count_ >= RETRIES)
991 protobuf::ModemDriverStatus::MODEM_NOT_RESPONDING));
994 void goby::acomms::MMDriver::present_fail_exceeds_retries()
996 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
997 <<
"Micro-Modem did not respond to our command even after " << RETRIES
998 <<
" retries. continuing onwards anyway..." << std::endl;
1002 void goby::acomms::MMDriver::pop_out()
1004 waiting_for_modem_ =
false;
1010 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
1011 <<
"Expected to pop outgoing NMEA message but out_ deque is empty" 1015 present_fail_count_ = 0;
1022 void goby::acomms::MMDriver::process_receive(
const NMEASentence& nmea)
1026 raw_msg.set_raw(nmea.message());
1027 raw_msg.set_description(description_map_[nmea.front()]);
1029 if (sentence_id_map_[nmea.sentence_id()] == CFG)
1030 *raw_msg.mutable_description() +=
": " + cfg_map_[nmea.at(1)];
1032 glog.is(DEBUG1) &&
glog << group(glog_in_group()) << hydroid_gateway_modem_prefix_
1033 << raw_msg.raw() <<
"\n" 1034 <<
"^ " <<
magenta << raw_msg.description() <<
nocolor << std::endl;
1038 global_fail_count_ = 0;
1041 switch (sentence_id_map_[nmea.sentence_id()])
1046 case REV: carev(nmea);
break;
1047 case ERR: caerr(nmea);
break;
1048 case DRQ: cadrq(nmea, transmit_msg_);
break;
1049 case CFG: cacfg(nmea);
break;
1050 case CLK: receive_time(nmea, CLK);
break;
1051 case TMS: receive_time(nmea, TMS);
break;
1053 receive_time(nmea, TMQ);
1059 case CYC: cacyc(nmea, &transmit_msg_);
break;
1060 case XST: caxst(nmea, &transmit_msg_);
break;
1061 case RXD: carxd(nmea, &receive_msg_);
break;
1062 case MSG: camsg(nmea, &receive_msg_);
break;
1063 case CST: cacst(nmea, &receive_msg_);
break;
1064 case MUA: camua(nmea, &receive_msg_);
break;
1065 case RDP: cardp(nmea, &receive_msg_);
break;
1067 caack(nmea, &receive_msg_);
1073 case MPR: campr(nmea, &receive_msg_);
break;
1074 case MPA: campa(nmea, &receive_msg_);
break;
1076 sntta(nmea, &receive_msg_);
1080 case MER: camer(nmea, &receive_msg_);
break;
1088 if (out_.front().sentence_id() ==
"CFG")
1090 if (nmea.size() == 3 && nmea.at(1) == out_.front().at(1) &&
1091 nmea.at(2) == out_.front().at(2))
1094 else if (out_.front().sentence_id() ==
"CFQ" &&
1095 nmea.sentence_id() ==
"CFG")
1099 else if (out_.front().sentence_id() ==
"MSC" &&
1100 (nmea.sentence_id() ==
"REV" || nmea.sentence_id() ==
"MSC"))
1104 else if (out_.front().sentence_id() == nmea.sentence_id())
1114 if (as<int32>(nmea[2]) != driver_cfg_.modem_id())
1116 if (as<unsigned>(nmea[1]) != expected_ack_destination_)
1120 uint32 frame = as<uint32>(nmea[3]) - 1;
1122 handle_ack(as<uint32>(nmea[1]), as<uint32>(nmea[2]), frame, m);
1125 if (!nvram_cfg_[
"CST"])
1126 signal_receive_and_clear(m);
1132 if (frames_waiting_for_ack_.count(frame))
1134 m->set_time(goby_time<uint64>());
1137 m->set_type(protobuf::ModemTransmission::ACK);
1138 m->add_acked_frame(frame);
1140 frames_waiting_for_ack_.erase(frame);
1142 glog.is(DEBUG1) &&
glog << group(glog_in_group()) <<
"Received ACK from " << m->src()
1143 <<
" for frame " << frame << std::endl;
1147 glog.is(DEBUG1) &&
glog << group(glog_in_group()) <<
"Received ACK for Micro-Modem frame " 1148 << frame + 1 <<
" (Goby frame " << frame
1149 <<
") that we were not expecting." << std::endl;
1159 if (revision_.mm_major == 2 && revision_.mm_minor == 0 && revision_.mm_patch < 18307)
1165 if (as<int32>(nmea[dest_field]) != driver_cfg_.modem_id())
1168 m->set_time(goby_time<uint64>());
1170 m->set_src(as<uint32>(nmea[src_field]));
1171 m->set_dest(as<uint32>(nmea[dest_field]));
1172 m->set_type(protobuf::ModemTransmission::DRIVER_SPECIFIC);
1173 m->SetExtension(micromodem::protobuf::type,
1174 micromodem::protobuf::MICROMODEM_HARDWARE_CONTROL_REPLY);
1177 m->MutableExtension(micromodem::protobuf::hw_ctl);
1179 hw_ctl->set_line(nmea.as<micromodem::protobuf::HardwareLine>(3));
1180 hw_ctl->set_mode(nmea.as<micromodem::protobuf::HardwareControlMode>(4));
1181 hw_ctl->set_arg(nmea.as<micromodem::protobuf::HardwareControlArgument>(5));
1184 if (!nvram_cfg_[
"CST"])
1185 signal_receive_and_clear(m);
1188 void goby::acomms::MMDriver::cadrq(
const NMEASentence& nmea_in,
1196 int frame = as<int>(nmea_in[6]) - 1;
1198 if (frame < m.frame_size() && !m.frame(frame).empty())
1201 nmea_out.push_back(m.src());
1202 nmea_out.push_back(m.dest());
1203 nmea_out.push_back(
int(m.ack_requested()));
1205 int max_bytes = nmea_in.as<
int>(5);
1207 hex_encode(m.frame(frame) + std::string(max_bytes - m.frame(frame).size(),
'\0')));
1210 if (m.ack_requested())
1212 expected_ack_destination_ = m.dest();
1213 frames_waiting_for_ack_.insert(next_frame_++);
1219 nmea_out.push_back(nmea_in[2]);
1220 nmea_out.push_back(nmea_in[3]);
1221 nmea_out.push_back(nmea_in[4]);
1222 nmea_out.push_back(
"");
1224 append_to_write_queue(nmea_out);
1230 if ((nmea.as<std::string>(1) ==
"BAD_CRC" || nmea.as<std::string>(1) ==
"Bad CRC") &&
1231 !frames_waiting_to_receive_.empty())
1233 m->AddExtension(micromodem::protobuf::frame_with_bad_crc, m->frame_size());
1237 frames_waiting_to_receive_.erase(frames_waiting_to_receive_.begin());
1240 if (frames_waiting_to_receive_.empty() && !nvram_cfg_[
"CST"])
1241 signal_receive_and_clear(m);
1243 glog.is(DEBUG1) &&
glog << group(glog_in_group()) << warn <<
"Received message with bad CRC" 1251 unsigned frame = as<uint32>(nmea[4]) - 1;
1254 m->set_time(goby_time<uint64>());
1255 m->set_src(as<uint32>(nmea[1]));
1256 m->set_dest(as<uint32>(nmea[2]));
1257 m->set_type(protobuf::ModemTransmission::DATA);
1258 m->set_ack_requested(as<bool>(nmea[3]));
1261 if (!nmea[5].empty())
1263 if (static_cast<unsigned>(m->frame_size()) != frame)
1265 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
1266 <<
"frame count mismatch: (Micro-Modem reports): " << frame
1267 <<
", (goby expects): " << m->frame_size() << std::endl;
1270 m->add_frame(hex_decode(nmea[5]));
1271 glog.is(DEBUG1) &&
glog << group(glog_in_group()) <<
"Received " 1272 << m->frame(m->frame_size() - 1).size() <<
" byte DATA frame " 1273 << frame <<
" from " << m->src() << std::endl;
1276 frames_waiting_to_receive_.erase(frame);
1279 if (frames_waiting_to_receive_.empty() && !nvram_cfg_[
"CST"])
1280 signal_receive_and_clear(m);
1287 m->set_time(goby_time<uint64>());
1288 m->set_src(as<uint32>(nmea[1]));
1289 m->set_dest(as<uint32>(nmea[2]));
1290 m->set_type(protobuf::ModemTransmission::DRIVER_SPECIFIC);
1291 m->SetExtension(micromodem::protobuf::type, micromodem::protobuf::MICROMODEM_MINI_DATA);
1293 m->add_frame(goby::util::hex_decode(nmea[3]));
1295 glog.is(DEBUG1) &&
glog << group(glog_in_group())
1296 <<
"Received MICROMODEM_MINI_DATA packet from " << m->src()
1300 if (!nvram_cfg_[
"CST"])
1301 signal_receive_and_clear(m);
1317 m->set_time(goby_time<uint64>());
1318 m->set_src(as<uint32>(nmea[SRC]));
1319 m->set_dest(as<uint32>(nmea[DEST]));
1320 m->set_rate(as<uint32>(nmea[RATE]));
1321 m->set_type(protobuf::ModemTransmission::DRIVER_SPECIFIC);
1322 m->SetExtension(micromodem::protobuf::type, micromodem::protobuf::MICROMODEM_FLEXIBLE_DATA);
1324 std::vector<std::string> frames, frames_data;
1326 if (!nmea[DATA_MINI].empty())
1327 boost::split(frames, nmea[DATA_MINI], boost::is_any_of(
";"));
1328 if (!nmea[DATA].empty())
1329 boost::split(frames_data, nmea[DATA], boost::is_any_of(
";"));
1330 frames.insert(frames.end(), frames_data.begin(), frames_data.end());
1332 bool bad_frame =
false;
1333 std::string frame_hex;
1334 const int num_fields = 3;
1335 for (
int f = 0, n = frames.size() / num_fields; f < n; ++f)
1345 if (!goby::util::as<bool>(frames[f * num_fields + CRCCHECK]))
1352 frame_hex += frames[f * num_fields + DATA];
1358 m->AddExtension(micromodem::protobuf::frame_with_bad_crc, 0);
1363 m->add_frame(goby::util::hex_decode(frame_hex));
1364 if (using_application_acks_)
1365 process_incoming_app_ack(m);
1368 glog.is(DEBUG1) &&
glog << group(glog_in_group())
1369 <<
"Received MICROMODEM_FLEXIBLE_DATA packet from " << m->src()
1373 if (!nvram_cfg_[
"CST"])
1374 signal_receive_and_clear(m);
1382 dccl_.decode(m->mutable_frame(0), &acks);
1383 glog.is(DEBUG1) &&
glog << group(glog_in_group()) <<
"Received ACKS " << acks.DebugString()
1387 if (m->dest() == driver_cfg_.modem_id() && acks.ack_requested())
1389 frames_to_ack_[m->src()].insert(acks.frame_start());
1393 for (
int i = 0, n = acks.part_size(); i < n; ++i)
1395 if (acks.part(i).ack_dest() == driver_cfg_.modem_id())
1397 for (
int j = 0, o = application_ack_max_frames_; j < o; ++j)
1399 if (acks.part(i).acked_frames() & (1ul << j))
1402 handle_ack(m->src(), acks.part(i).ack_dest(), j, &msg);
1411 void goby::acomms::MMDriver::cacfg(
const NMEASentence& nmea)
1413 nvram_cfg_[nmea[1]] = nmea.as<
int>(2);
1416 void goby::acomms::MMDriver::receive_time(
const NMEASentence& nmea, SentenceIDs sentence_id)
1418 if (out_.empty() || (sentence_id == CLK && out_.front().sentence_id() !=
"CLK") ||
1419 (sentence_id == TMS && out_.front().sentence_id() !=
"TMS") ||
1420 (sentence_id == TMQ && out_.front().sentence_id() !=
"TMQ"))
1425 ptime expected = goby_time();
1428 if (sentence_id == CLK)
1430 reported = ptime(date(nmea.as<
int>(1), nmea.as<
int>(2), nmea.as<
int>(3)),
1431 time_duration(nmea.as<
int>(4), nmea.as<
int>(5), nmea.as<
int>(6), 0));
1433 else if (sentence_id == TMS || sentence_id == TMQ)
1436 if (sentence_id == TMS)
1438 else if (sentence_id == TMQ)
1441 std::string t = nmea.at(time_field).substr(0, nmea.at(time_field).size() - 1);
1442 boost::posix_time::time_input_facet* tif =
new boost::posix_time::time_input_facet;
1443 tif->set_iso_extended_format();
1444 std::istringstream iso_time(t);
1445 iso_time.imbue(std::locale(std::locale::classic(), tif));
1446 iso_time >> reported;
1452 boost::posix_time::time_duration t_diff = (reported - expected);
1456 if (abs(
int(t_diff.total_milliseconds())) <
1457 driver_cfg_.GetExtension(micromodem::protobuf::Config::allowed_skew_ms))
1459 glog.is(DEBUG1) &&
glog << group(glog_out_group()) <<
"Micro-Modem clock acceptably set." 1466 glog << group(glog_out_group())
1467 <<
"Time is not within allowed skew, setting Micro-Modem clock again." 1476 m->AddExtension(micromodem::protobuf::transmit_stat);
1480 xst->set_version(nmea.as<
int>(1) > 19700000 ? 0 : nmea.as<
int>(1));
1484 int version_offset = 0;
1485 if (xst->version() == 0)
1489 else if (xst->version() == 6)
1494 xst->set_date(nmea.as<std::string>(1 + version_offset));
1495 xst->set_time(nmea.as<std::string>(2 + version_offset));
1497 micromodem::protobuf::ClockMode clock_mode =
1498 micromodem::protobuf::ClockMode_IsValid(nmea.as<
int>(3 + version_offset))
1499 ? nmea.as<micromodem::protobuf::ClockMode>(3 + version_offset)
1500 : micromodem::protobuf::INVALID_CLOCK_MODE;
1502 xst->set_clock_mode(clock_mode);
1504 micromodem::protobuf::TransmitMode transmit_mode =
1505 micromodem::protobuf::TransmitMode_IsValid(nmea.as<
int>(4 + version_offset))
1506 ? nmea.as<micromodem::protobuf::TransmitMode>(4 + version_offset)
1507 : micromodem::protobuf::INVALID_TRANSMIT_MODE;
1509 xst->set_mode(transmit_mode);
1511 xst->set_probe_length(nmea.as<
int32>(5 + version_offset));
1512 xst->set_bandwidth(nmea.as<
int32>(6 + version_offset));
1513 xst->set_carrier_freq(nmea.as<
int32>(7 + version_offset));
1514 xst->set_rate(nmea.as<
int32>(8 + version_offset));
1515 xst->set_source(nmea.as<
int32>(9 + version_offset));
1516 xst->set_dest(nmea.as<
int32>(10 + version_offset));
1517 xst->set_ack_requested(nmea.as<
bool>(11 + version_offset));
1518 xst->set_number_frames_expected(nmea.as<
int32>(12 + version_offset));
1519 xst->set_number_frames_sent(nmea.as<
int32>(13 + version_offset));
1521 micromodem::protobuf::PacketType packet_type =
1522 micromodem::protobuf::PacketType_IsValid(nmea.as<
int>(14 + version_offset))
1523 ? nmea.as<micromodem::protobuf::PacketType>(14 + version_offset)
1524 : micromodem::protobuf::PACKET_TYPE_UNKNOWN;
1526 xst->set_packet_type(packet_type);
1527 xst->set_number_bytes(nmea.as<
int32>(15 + version_offset));
1529 clk_mode_ = xst->clock_mode();
1531 catch (std::out_of_range& e)
1533 glog.is(DEBUG1) &&
glog << group(glog_in_group()) << warn
1534 <<
"$CAXST message shorter than expected" << std::endl;
1537 if (expected_remaining_caxst_ == 0)
1544 --expected_remaining_caxst_;
1550 m->set_time(goby_time<uint64>());
1554 m->set_src(as<uint32>(nmea[1]));
1555 m->set_dest(as<uint32>(nmea[2]));
1558 m->MutableExtension(micromodem::protobuf::ranging_reply);
1560 if (nmea.size() > 3)
1561 ranging_reply->add_one_way_travel_time(as<double>(nmea[3]));
1563 m->set_type(protobuf::ModemTransmission::DRIVER_SPECIFIC);
1564 m->SetExtension(micromodem::protobuf::type, micromodem::protobuf::MICROMODEM_TWO_WAY_PING);
1567 glog << group(glog_in_group()) <<
"Received MICROMODEM_TWO_WAY_PING response from " 1568 << m->src() <<
", 1-way travel time: " 1569 << ranging_reply->one_way_travel_time(ranging_reply->one_way_travel_time_size() - 1)
1570 <<
"s" << std::endl;
1573 if (!nvram_cfg_[
"CST"])
1574 signal_receive_and_clear(m);
1579 m->set_time(goby_time<uint64>());
1582 m->set_src(as<uint32>(nmea[1]));
1583 m->set_dest(as<uint32>(nmea[2]));
1585 m->set_type(protobuf::ModemTransmission::DRIVER_SPECIFIC);
1586 m->SetExtension(micromodem::protobuf::type, micromodem::protobuf::MICROMODEM_TWO_WAY_PING);
1589 if (!nvram_cfg_[
"CST"])
1590 signal_receive_and_clear(m);
1598 m->MutableExtension(micromodem::protobuf::ranging_reply);
1600 ranging_reply->add_one_way_travel_time(as<double>(nmea[1]));
1601 ranging_reply->add_one_way_travel_time(as<double>(nmea[2]));
1602 ranging_reply->add_one_way_travel_time(as<double>(nmea[3]));
1603 ranging_reply->add_one_way_travel_time(as<double>(nmea[4]));
1605 m->set_type(protobuf::ModemTransmission::DRIVER_SPECIFIC);
1606 m->SetExtension(micromodem::protobuf::type, last_lbl_type_);
1608 m->set_src(driver_cfg_.modem_id());
1609 m->set_time(as<uint64>(nmea_time2ptime(nmea[5])));
1610 m->set_time_source(protobuf::ModemTransmission::MODEM_TIME);
1612 if (last_lbl_type_ == micromodem::protobuf::MICROMODEM_REMUS_LBL_RANGING)
1613 glog.is(DEBUG1) &&
glog << group(glog_in_group())
1614 <<
"Received MICROMODEM_REMUS_LBL_RANGING response " << std::endl;
1615 else if (last_lbl_type_ == micromodem::protobuf::MICROMODEM_NARROWBAND_LBL_RANGING)
1616 glog.is(DEBUG1) &&
glog << group(glog_in_group())
1617 <<
"Received MICROMODEM_NARROWBAND_LBL_RANGING response " 1621 signal_receive_and_clear(m);
1624 void goby::acomms::MMDriver::carev(
const NMEASentence& nmea)
1626 if (nmea[2] ==
"INIT")
1628 glog.is(DEBUG1) &&
glog << group(glog_in_group()) <<
"Micro-Modem rebooted." << std::endl;
1630 sleep(WAIT_AFTER_REBOOT.total_seconds());
1633 else if (nmea[2] ==
"AUV")
1635 std::vector<std::string> rev_parts;
1636 boost::split(rev_parts, nmea[3], boost::is_any_of(
"."));
1637 if (rev_parts.size() == 3 || rev_parts.size() == 4)
1639 revision_.mm_major = as<int>(rev_parts[0]);
1640 revision_.mm_minor = as<int>(rev_parts[1]);
1641 revision_.mm_patch = as<int>(rev_parts[2]);
1642 if (rev_parts.size() == 4)
1644 revision_.mm_patch *= 100;
1645 revision_.mm_patch += as<int>(rev_parts[3]);
1648 glog.is(DEBUG1) &&
glog << group(glog_in_group()) <<
"Revision: " << revision_.mm_major
1649 <<
"." << revision_.mm_minor <<
"." << revision_.mm_patch
1654 glog.is(WARN) &&
glog << group(glog_in_group()) <<
"Bad revision string: " << nmea[3]
1660 void goby::acomms::MMDriver::caerr(
const NMEASentence& nmea)
1662 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
1663 <<
"Micro-Modem reports error: " << nmea.message() << std::endl;
1666 if (nmea.at(2) ==
"NMEA")
1668 waiting_for_modem_ =
false;
1672 increment_present_fail();
1676 present_fail_exceeds_retries();
1684 if (as<int32>(nmea[2]) == driver_cfg_.modem_id())
1691 if (!nvram_cfg_[
"XST"])
1694 msg->set_time(goby_time<uint64>());
1696 msg->set_src(as<uint32>(nmea[2]));
1697 msg->set_dest(as<uint32>(nmea[3]));
1698 msg->set_rate(as<uint32>(nmea[4]));
1699 msg->set_max_num_frames(as<uint32>(nmea[6]));
1700 msg->set_max_frame_bytes(PACKET_SIZE[msg->rate()]);
1702 cache_outgoing_data(msg);
1706 local_cccyc_ =
false;
1712 unsigned rate = as<uint32>(nmea[4]);
1713 if (local_cccyc_ && rate != 0)
1717 local_cccyc_ =
false;
1721 unsigned num_frames = as<uint32>(nmea[6]);
1722 if (!frames_waiting_to_receive_.empty())
1724 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn <<
"flushing " 1725 << frames_waiting_to_receive_.size()
1726 <<
" expected frames that were never received." << std::endl;
1727 frames_waiting_to_receive_.clear();
1730 for (
unsigned i = 0; i < num_frames; ++i) frames_waiting_to_receive_.insert(i);
1734 expected_remaining_cacst_ = (as<int32>(nmea[4]) == 0 && !local_cccyc_) ? 1 : 0;
1736 local_cccyc_ =
false;
1742 if (msg->src() == driver_cfg_.modem_id())
1744 if ((!using_application_acks_ ||
1745 (using_application_acks_ &&
1746 (next_frame_ + (
int)msg->max_num_frames() > application_ack_max_frames_))))
1748 if (!frames_waiting_for_ack_.empty())
1750 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn <<
"flushing " 1751 << frames_waiting_for_ack_.size()
1752 <<
" expected acknowledgments that were never received." 1754 frames_waiting_for_ack_.clear();
1757 expected_ack_destination_ = 0;
1761 if (using_application_acks_)
1762 process_outgoing_app_ack(msg);
1770 if (msg->frame_size())
1773 glog << group(glog_out_group())
1774 <<
"Must use data request callback when using application acknowledgments" 1782 for (std::map<
unsigned, std::set<unsigned> >::const_iterator it = frames_to_ack_.begin(),
1783 end = frames_to_ack_.end();
1787 acks_part.set_ack_dest(it->first);
1790 for (std::set<unsigned>::const_iterator jt = it->second.begin(),
1791 jend = it->second.end();
1793 acked_frames |= (1ul << *jt);
1795 acks_part.set_acked_frames(acked_frames);
1797 frames_to_ack_.clear();
1799 acks.set_frame_start(next_frame_);
1800 msg->set_frame_start(next_frame_);
1803 unsigned ack_size = dccl_.size(acks);
1806 msg->add_frame()->resize(ack_size, 0);
1812 acks.set_ack_requested(msg->ack_requested());
1813 std::string ack_bytes;
1814 dccl_.encode(&ack_bytes, acks);
1816 msg->mutable_frame(0)->replace(0, ack_size, ack_bytes);
1819 if (acks.part_size() == 0 && msg->frame_size() == 1 && msg->frame(0).size() == ack_size)
1821 else if (msg->dest() ==
1829 if (message.src() < 0)
1830 throw(
"ModemTransmission::src is invalid: " + as<std::string>(message.src()));
1831 else if (message.dest() < 0)
1832 throw(
"ModemTransmission::dest is invalid: " + as<std::string>(message.dest()));
1833 else if (message.rate() < 0 || message.rate() > 5)
1834 throw(
"ModemTransmission::rate is invalid: " + as<std::string>(message.rate()));
1840 m->AddExtension(micromodem::protobuf::receive_stat);
1842 cst->set_version(nmea.as<
int>(1) < 6 ? 0 : nmea.as<
int>(1));
1846 int version_offset = 0;
1847 if (cst->version() == 0)
1851 else if (cst->version() == 6)
1856 micromodem::protobuf::ReceiveMode mode =
1857 micromodem::protobuf::ReceiveMode_IsValid(nmea.as<
int>(1 + version_offset))
1858 ? nmea.as<micromodem::protobuf::ReceiveMode>(1 + version_offset)
1859 : micromodem::protobuf::INVALID_RECEIVE_MODE;
1861 cst->set_mode(mode);
1862 cst->set_time(as<uint64>(nmea_time2ptime(nmea.as<std::string>(2 + version_offset))));
1864 micromodem::protobuf::ClockMode clock_mode =
1865 micromodem::protobuf::ClockMode_IsValid(nmea.as<
int>(3 + version_offset))
1866 ? nmea.as<micromodem::protobuf::ClockMode>(3 + version_offset)
1867 : micromodem::protobuf::INVALID_CLOCK_MODE;
1869 cst->set_clock_mode(clock_mode);
1870 cst->set_mfd_peak(nmea.as<
double>(4 + version_offset));
1871 cst->set_mfd_power(nmea.as<
double>(5 + version_offset));
1872 cst->set_mfd_ratio(nmea.as<
double>(6 + version_offset));
1873 cst->set_spl(nmea.as<
double>(7 + version_offset));
1874 cst->set_shf_agn(nmea.as<
double>(8 + version_offset));
1875 cst->set_shf_ainpshift(nmea.as<
double>(9 + version_offset));
1876 cst->set_shf_ainshift(nmea.as<
double>(10 + version_offset));
1877 cst->set_shf_mfdshift(nmea.as<
double>(11 + version_offset));
1878 cst->set_shf_p2bshift(nmea.as<
double>(12 + version_offset));
1879 cst->set_rate(nmea.as<
int32>(13 + version_offset));
1880 cst->set_source(nmea.as<
int32>(14 + version_offset));
1881 cst->set_dest(nmea.as<
int32>(15 + version_offset));
1883 micromodem::protobuf::PSKErrorCode psk_error_code =
1884 micromodem::protobuf::PSKErrorCode_IsValid(nmea.as<
int>(16 + version_offset))
1885 ? nmea.as<micromodem::protobuf::PSKErrorCode>(16 + version_offset)
1886 : micromodem::protobuf::INVALID_PSK_ERROR_CODE;
1888 cst->set_psk_error_code(psk_error_code);
1890 micromodem::protobuf::PacketType packet_type =
1891 micromodem::protobuf::PacketType_IsValid(nmea.as<
int>(17 + version_offset))
1892 ? nmea.as<micromodem::protobuf::PacketType>(17 + version_offset)
1893 : micromodem::protobuf::PACKET_TYPE_UNKNOWN;
1895 cst->set_packet_type(packet_type);
1896 cst->set_number_frames(nmea.as<
int32>(18 + version_offset));
1897 cst->set_number_bad_frames(nmea.as<
int32>(19 + version_offset));
1898 cst->set_snr_rss(nmea.as<
double>(20 + version_offset));
1899 cst->set_snr_in(nmea.as<
double>(21 + version_offset));
1900 cst->set_snr_out(nmea.as<
double>(22 + version_offset));
1901 cst->set_snr_symbols(nmea.as<
double>(23 + version_offset));
1902 cst->set_mse_equalizer(nmea.as<
double>(24 + version_offset));
1903 cst->set_data_quality_factor(nmea.as<
int32>(25 + version_offset));
1904 cst->set_doppler(nmea.as<
double>(26 + version_offset));
1905 cst->set_stddev_noise(nmea.as<
double>(27 + version_offset));
1906 cst->set_carrier_freq(nmea.as<
double>(28 + version_offset));
1907 cst->set_bandwidth(nmea.as<
double>(29 + version_offset));
1909 catch (std::out_of_range& e)
1911 glog.is(DEBUG1) &&
glog << group(glog_in_group()) << warn
1912 <<
"$CACST message shorter than expected" << std::endl;
1921 clk_mode_ = cst->clock_mode();
1922 if (clk_mode_ == micromodem::protobuf::SYNC_TO_PPS_AND_CCCLK_GOOD ||
1923 clk_mode_ == micromodem::protobuf::SYNC_TO_PPS_AND_CCCLK_BAD)
1926 m->MutableExtension(micromodem::protobuf::ranging_reply);
1927 boost::posix_time::ptime toa = as<boost::posix_time::ptime>(cst->time());
1929 double(toa.time_of_day().fractional_seconds()) / toa.time_of_day().ticks_per_second();
1931 ranging_reply->add_one_way_travel_time(frac_sec);
1933 ranging_reply->set_ambiguity(micromodem::protobuf::RangingReply::OWTT_SECOND_AMBIGUOUS);
1935 ranging_reply->set_receiver_clk_mode(clk_mode_);
1936 ranging_reply->set_is_one_way_synchronous(
true);
1939 if (cst->has_time())
1941 m->set_time(cst->time());
1942 m->set_time_source(protobuf::ModemTransmission::MODEM_TIME);
1946 if (expected_remaining_cacst_ == 0)
1947 signal_receive_and_clear(m);
1949 --expected_remaining_cacst_;
1963 catch (std::exception& e)
1970 void goby::acomms::MMDriver::set_silent(
bool silent)
1973 write_single_cfg(
"SRC,0");
1975 write_single_cfg(
"SRC," + as<std::string>(driver_cfg_.modem_id()));
provides a basic client for line by line text based communications over a 8N1 tty (such as an RS-232 ...
Contains functions for adding color to Terminal window streams.
void modem_write(const std::string &out)
write a line to the serial port.
void shutdown()
Stops the driver.
MMDriver()
Default constructor.
void modem_start(const protobuf::DriverConfig &cfg)
start the physical connection to the modem (serial port, TCP, etc.). must be called before ModemDrive...
google::protobuf::uint32 uint32
an unsigned 32 bit integer
void handle_initiate_transmission(const protobuf::ModemTransmission &m)
See ModemDriverBase::handle_initiate_transmission()
boost::signals2::signal< void(const protobuf::ModemRaw &msg)> signal_raw_incoming
Called after any message is received from the modem by the driver. Used by the MACManager for auto-di...
boost::signals2::signal< void(const protobuf::ModemTransmission &message)> signal_transmit_result
Called when a transmission is completed.
std::ostream & magenta(std::ostream &os)
All text following this manipulator is magenta (e.g. std::cout << magenta << "text";) ...
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.
boost::signals2::signal< void(const protobuf::ModemTransmission &message)> signal_receive
Called when a binary data transmission is received from the modem.
const int BROADCAST_ID
special modem id for the broadcast destination - no one is assigned this address. Analogous to 192...
void do_work()
See ModemDriverBase::do_work()
util::LineBasedInterface & modem()
use for direct access to the modem
common::FlexOstream glog
Access the Goby logger through this object.
boost::signals2::signal< void(const protobuf::ModemRaw &msg)> signal_raw_outgoing
Called after any message is sent from the driver to the modem. Useful for higher level analysis and d...
bool modem_read(std::string *in)
read a line from the serial port, including end-of-line character(s)
void startup(const protobuf::DriverConfig &cfg)
Starts the driver.
boost::signals2::signal< void(protobuf::ModemTransmission *msg)> signal_data_request
Called when the modem or modem driver needs data to send. The returned data should be stored in Modem...
double ptime2unix_double(boost::posix_time::ptime given_time)
convert from boost date_time ptime to the number of seconds (including fractional) since 1/1/1970 0:0...
void modem_close()
closes the serial port. Use modem_start to reopen the port.
boost::signals2::signal< void(protobuf::ModemTransmission *msg_request)> signal_modify_transmission
Called before the modem driver begins processing a transmission. This allows a third party to modify ...
std::ostream & nocolor(std::ostream &os)
All text following this manipulator is uncolored (e.g. std::cout << green << "green" << nocolor << "u...
google::protobuf::int32 int32
a signed 32 bit integer
void update_cfg(const protobuf::DriverConfig &cfg)
Update configuration while running (not required to be implemented)
const int QUERY_DESTINATION_ID
special modem id used internally to goby-acomms for indicating that the MAC layer (amac) is agnostic ...