Goby3  3.1.5a
2024.05.23
mm_driver.h
Go to the documentation of this file.
1 // Copyright 2009-2021:
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 // Community contributors (see AUTHORS file)
5 // File authors:
6 // Toby Schneider <toby@gobysoft.org>
7 // Russ Webber <russ@rw.id.au>
8 //
9 //
10 // This file is part of the Goby Underwater Autonomy Project Libraries
11 // ("The Goby Libraries").
12 //
13 // The Goby Libraries are free software: you can redistribute them and/or modify
14 // them under the terms of the GNU Lesser General Public License as published by
15 // the Free Software Foundation, either version 2.1 of the License, or
16 // (at your option) any later version.
17 //
18 // The Goby Libraries are distributed in the hope that they will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU Lesser General Public License for more details.
22 //
23 // You should have received a copy of the GNU Lesser General Public License
24 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
25 
26 #ifndef GOBY_ACOMMS_MODEMDRIVER_MM_DRIVER_H
27 #define GOBY_ACOMMS_MODEMDRIVER_MM_DRIVER_H
28 
29 #include <cstdint> // for uint32_t
30 #include <deque> // for deque
31 #include <map> // for map
32 #include <memory> // for unique_ptr
33 #include <mutex> // for mutex
34 #include <set> // for set
35 #include <string> // for string
36 
37 #include "driver_base.h" // for ModemDriverBase
38 #include "goby/acomms/protobuf/driver_base.pb.h" // for DriverConfig
39 #include "goby/acomms/protobuf/mm_driver.pb.h" // for Config, MessageT...
40 #include "goby/acomms/protobuf/modem_message.pb.h" // for ModemTransmission
41 #include "goby/time/system_clock.h" // for SystemClock, Sys...
42 #include "goby/util/linebasedcomms/nmea_sentence.h" // for NMEASentence
43 
44 namespace dccl
45 {
46 class Codec;
47 } // namespace dccl
48 
49 namespace goby
50 {
51 namespace acomms
52 {
56 class MMDriver : public ModemDriverBase
57 {
58  public:
60  MMDriver();
62  ~MMDriver() override;
63 
67  void startup(const protobuf::DriverConfig& cfg) override;
68 
69  void update_cfg(const protobuf::DriverConfig& cfg) override;
70 
72  void shutdown() override;
73 
75  void do_work() override;
76 
79 
81  int clk_mode() { return clk_mode_; }
82 
83  bool is_started() const { return startup_done_; }
84 
85  static unsigned packet_frame_count(int rate) { return PACKET_FRAME_COUNT[rate]; }
86 
87  static unsigned packet_size(int rate) { return PACKET_SIZE[rate]; }
88 
89  void set_silent(bool silent);
90  void write_single_cfg(const std::string& s); // write a single NVRAM value
91 
92  private:
93  enum SentenceIDs
94  {
95  SENTENCE_NOT_DEFINED = 0,
96  ACK,
97  DRQ,
98  RXA,
99  RXD,
100  RXP,
101  TXD,
102  TXA,
103  TXP,
104  TXF,
105  CYC,
106  MPC,
107  MPA,
108  MPR,
109  RSP,
110  MSC,
111  MSA,
112  MSR,
113  EXL,
114  MEC,
115  MEA,
116  MER,
117  MUC,
118  MUA,
119  MUR,
120  PDT,
121  PNT,
122  TTA,
123  MFD,
124  CLK,
125  CFG,
126  AGC,
127  BBD,
128  CFR,
129  CST,
130  MSG,
131  REV,
132  DQF,
133  SHF,
134  SNR,
135  DOP,
136  DBG,
137  FFL,
138  FST,
139  ERR,
140  TOA,
141  XST,
142  RDP,
143  TDP,
144  TMS,
145  TMQ,
146  TMG,
147  SWP,
148  MSQ
149  };
150 
151  // startup
152  void initialize_talkers(); // insert strings into sentence_id_map_, etc for later use
153  void set_clock(); // set the modem clock from the system (goby) clock
154  void write_cfg(); // write the NVRAM configuration values to the modem
155  void query_all_cfg(); // query the current NVRAM configuration of the modem
156  void set_hydroid_gateway_prefix(int id); // if using the hydroid gateway, set its id number
157 
158  // output
159 
160  void cccyc(protobuf::ModemTransmission* msg);
161  void ccmuc(protobuf::ModemTransmission* msg);
162  void cctdp(protobuf::ModemTransmission* msg);
163  void ccmpc(const protobuf::ModemTransmission& msg);
164  void ccpdt(const protobuf::ModemTransmission& msg);
165  void ccpnt(const protobuf::ModemTransmission& msg);
166  void ccmec(const protobuf::ModemTransmission& msg);
167  void ccpgt(const protobuf::ModemTransmission& msg);
168  void ccswp(const protobuf::ModemTransmission& msg);
169  void ccmsq(const protobuf::ModemTransmission& msg);
170 
171  void try_send(); // try to send another NMEA message to the modem
172  void pop_out(); // pop the NMEA send deque upon successful NMEA acknowledgment
173  void cache_outgoing_data(protobuf::ModemTransmission* msg); // cache data upon a CCCYC
174  void append_to_write_queue(const util::NMEASentence& nmea); // add a message
175  void mm_write(const util::NMEASentence&
176  nmea); // actually write a message (appends hydroid prefix if needed)
177  void increment_present_fail();
178  void present_fail_exceeds_retries();
179 
180  // input
181  void process_receive(
182  const util::NMEASentence& nmea); // parse a receive message and call proper method
183 
184  // data cycle
185  void cacyc(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $CACYC
186  void carxd(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $CARXD
187  void camsg(const util::NMEASentence& nmea, protobuf::ModemTransmission* m);
188 
189  void caack(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $CAACK
190  void handle_ack(std::uint32_t src, std::uint32_t dest, std::uint32_t frame,
191  protobuf::ModemTransmission* m);
192 
193  // mini packet
194  void camua(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $CAMUA
195 
196  // flexible data protocol
197  void cardp(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $CARDP
198 
199  // ranging (pings)
200  void campr(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $CAMPR
201  void campa(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $CAMPA
202  void sntta(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $SNTTA
203 
204  // hardware control
205  void camer(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $CAMER
206 
207  // local modem
208  void caxst(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $CAXST
209  void cacst(const util::NMEASentence& nmea, protobuf::ModemTransmission* msg); // $CACST
210  void carev(const util::NMEASentence& nmea); // $CAREV
211  void caerr(const util::NMEASentence& nmea); // $CAERR
212  void cacfg(const util::NMEASentence& nmea);
213  void receive_time(const util::NMEASentence& nmea, SentenceIDs sentence_id); // $CACLK
214  void catms(const util::NMEASentence& nmea); // $CATMS
215  void cadrq(const util::NMEASentence& nmea, const protobuf::ModemTransmission& m); // $CADRQ
216 
217  void validate_transmission_start(const protobuf::ModemTransmission& message);
218 
219  void signal_receive_and_clear(protobuf::ModemTransmission* message);
220 
221  // application acks
222  void process_incoming_app_ack(protobuf::ModemTransmission* m);
223  void process_outgoing_app_ack(protobuf::ModemTransmission* msg);
224 
225  // Serial port methods
226  void set_rts(bool state);
227 
228  const micromodem::protobuf::Config& mm_driver_cfg() const
229  {
230  return driver_cfg_.GetExtension(micromodem::protobuf::config);
231  }
232 
233  // doxygen
234 
236  // \example acomms/chat/chat.cpp
237 
238  private:
239  // for the serial connection ($CCCFG,BR1,3)
240  enum
241  {
242  DEFAULT_BAUD = 19200
243  };
244  // failures before closing port and throwing exception
245  enum
246  {
247  MAX_FAILS_BEFORE_DEAD = 5
248  };
249  // how many retries on a given message
250  enum
251  {
252  RETRIES = 3
253  };
254  enum
255  {
256  ROUGH_SPEED_OF_SOUND = 1500
257  }; // m/s
258 
259  // time to wait for modem to respond
260  static const goby::time::SystemClock::duration MODEM_WAIT;
261  // time to wait for modem to respond
262  static const goby::time::SystemClock::duration MULTI_REPLY_WAIT;
263  // time to wait after modem reboot
264  static const goby::time::SystemClock::duration WAIT_AFTER_REBOOT;
265 
266  static const std::string SERIAL_DELIMITER;
267  // number of frames for a given packet type
268  static const unsigned PACKET_FRAME_COUNT[];
269  // size of packet (in bytes) for a given modem rate
270  static const unsigned PACKET_SIZE[];
271 
272  // all startup configuration (DriverConfig defined in acomms_driver_base.proto and extended in acomms_mm_driver.proto)
273  protobuf::DriverConfig driver_cfg_;
274 
275  // deque for outgoing messages to the modem, we queue them up and send
276  // as the modem acknowledges them
277  std::deque<util::NMEASentence> out_;
278 
279  // time of the last message written. we timeout and resend after MODEM_WAIT seconds
280  goby::time::SystemClock::time_point last_write_time_;
281 
282  // are we waiting for a command ack (CA) from the modem or can we send another output?
283  bool waiting_for_modem_{false};
284 
285  // are we waiting for a multi-message reply
286  bool waiting_for_multimsg_{false};
287 
288  // set after the startup routines finish once. we can't startup on instantiation because
289  // the base class sets some of our references (from the MOOS file)
290  bool startup_done_{false};
291 
292  // keeps track of number of failures and exits after reaching MAX_FAILS, assuming modem dead
293  unsigned global_fail_count_{0};
294 
295  // keeps track of number of failures on the present talker and moves on to the next talker
296  // if exceeded
297  unsigned present_fail_count_{0};
298 
299  // keeps track of clock mode, necessary for synchronous navigation
301 
302  // has the clock been properly set. we must reset the clock after reboot ($CAREV,INIT)
303  bool clock_set_{false};
304 
305  enum TalkerIDs
306  {
307  TALKER_NOT_DEFINED = 0,
308  CA,
309  CC,
310  SN,
311  GP
312  };
313 
314  std::map<std::string, TalkerIDs> talker_id_map_;
315  std::map<std::string, SentenceIDs> sentence_id_map_;
316  std::map<std::string, std::string> description_map_;
317  std::map<std::string, std::string> cfg_map_;
318 
319  //
320  // stuff to deal with the non-standard Hydroid gateway buoy
321  //
322 
323  // length of #G1 or #M1
324  enum
325  {
326  HYDROID_GATEWAY_PREFIX_LENGTH = 3
327  };
328  // time between requests to the hydroid gateway buoy gps
329  static const goby::time::SystemClock::duration HYDROID_GATEWAY_GPS_REQUEST_INTERVAL;
330  goby::time::SystemClock::time_point last_hydroid_gateway_gps_request_;
331  bool is_hydroid_gateway_{false};
332  std::string hydroid_gateway_modem_prefix_;
333  std::string hydroid_gateway_gps_request_;
334 
335  // NVRAM parameters like SRC, DTO, PTO, etc.
336  std::map<std::string, int> nvram_cfg_;
337 
338  protobuf::ModemTransmission transmit_msg_;
339  unsigned expected_remaining_caxst_{
340  0}; // used to determine how many CAXST to aggregate (so that bost rate 0 transmissions [CYC and TXD] are provided as a single logical unit)
341 
342  protobuf::ModemTransmission receive_msg_;
343  unsigned expected_remaining_cacst_{
344  0}; // used to determine how many CACST to aggregate (so that rate 0 transmissions [CYC and RXD] are provided as a single logical unit)
345 
346  // keep track of which frames we've sent and are awaiting acks for. This
347  // way we have a chance of intercepting unexpected behavior of the modem
348  // relating to ACKs
349  std::set<unsigned> frames_waiting_for_ack_;
350 
351  // keep track of where we sent the message to be acked to work around a bug in
352  // the MM1 firmware that reports third-party acks with a destination of the local modem
353  unsigned expected_ack_destination_{0};
354 
355  std::set<unsigned> frames_waiting_to_receive_;
356 
357  // true if we initiated the last cycle ($CCCYC) (and thereby cache data for it)?
358  // false if a third party initiated the last cycle
359  bool local_cccyc_{false};
360 
362 
363  goby::time::SystemClock::time_point last_keep_alive_time_;
364 
365  // time of the last multi-message reply received. we timeout and pop after MULTI_REPLY_WAIT seconds
366  goby::time::SystemClock::time_point last_multimsg_rx_time_;
367 
368  struct MMRevision
369  {
370  MMRevision() = default;
371  int mm_major{0};
372  int mm_minor{0};
373  int mm_patch{0};
374  };
375  MMRevision revision_;
376 
377  bool using_application_acks_{false};
378  int application_ack_max_frames_{0};
379 
380  // ids we are providing acks for, normally just our modem_id()
381  std::set<unsigned> application_ack_ids_;
382 
383  int next_frame_{0};
384 
385  // modem id to frames
386  std::map<unsigned, std::set<unsigned> > frames_to_ack_;
387 
388  std::unique_ptr<dccl::Codec> dccl_;
389  // DCCL requires full memory barrier...
390  static std::mutex dccl_mutex_;
391 };
392 } // namespace acomms
393 } // namespace goby
394 #endif
goby::acomms::protobuf::DriverConfig
Definition: driver_base.pb.h:123
goby::acomms::protobuf::ModemTransmission
Definition: modem_message.pb.h:166
goby::acomms::MMDriver::is_started
bool is_started() const
Definition: mm_driver.h:83
goby::acomms::MMDriver::handle_initiate_transmission
void handle_initiate_transmission(const protobuf::ModemTransmission &m) override
See ModemDriverBase::handle_initiate_transmission()
system_clock.h
goby
The global namespace for the Goby project.
Definition: acomms_constants.h:33
dccl
Definition: mm_driver.h:44
goby::acomms::MMDriver::update_cfg
void update_cfg(const protobuf::DriverConfig &cfg) override
Update configuration while running (not required to be implemented)
goby::util::logger::mutex
std::recursive_mutex mutex
modem_message.pb.h
goby::acomms::MMDriver::packet_size
static unsigned packet_size(int rate)
Definition: mm_driver.h:87
nmea_sentence.h
goby::time::SystemClock::duration
std::chrono::microseconds duration
Duration type.
Definition: system_clock.h:52
goby::acomms::MMDriver::write_single_cfg
void write_single_cfg(const std::string &s)
driver_base.h
driver_base.pb.h
goby::acomms::MMDriver::MMDriver
MMDriver()
Default constructor.
goby::time::SystemClock::time_point
std::chrono::time_point< SystemClock > time_point
Definition: system_clock.h:55
goby::acomms::MMDriver::packet_frame_count
static unsigned packet_frame_count(int rate)
Definition: mm_driver.h:85
goby::msg
extern ::google::protobuf::internal::ExtensionIdentifier< ::google::protobuf::MessageOptions, ::google::protobuf::internal::MessageTypeTraits< ::goby::GobyMessageOptions >, 11, false > msg
Definition: option_extensions.pb.h:1327
goby::acomms::MMDriver::~MMDriver
~MMDriver() override
Destructor.
goby::acomms::micromodem::protobuf::ClockMode
ClockMode
Definition: mm_driver.pb.h:155
goby::acomms::micromodem::protobuf::config
extern ::google::protobuf::internal::ExtensionIdentifier< ::goby::acomms::protobuf::DriverConfig, ::google::protobuf::internal::MessageTypeTraits< ::goby::acomms::micromodem::protobuf::Config >, 11, false > config
Definition: mm_driver.pb.h:3391
goby::acomms::ModemDriverBase
provides an abstract base class for acoustic modem drivers. This is subclassed by the various drivers...
Definition: driver_base.h:58
goby::acomms::micromodem::protobuf::TransmissionType
TransmissionType
Definition: mm_driver.pb.h:269
goby::util::NMEASentence
Definition: nmea_sentence.h:51
goby::acomms::MMDriver::startup
void startup(const protobuf::DriverConfig &cfg) override
Starts the driver.
goby::acomms::MMDriver::clk_mode
int clk_mode()
Current clock mode of the modem, necessary for synchronous navigation.
Definition: mm_driver.h:81
mm_driver.pb.h
goby::acomms::MMDriver::do_work
void do_work() override
See ModemDriverBase::do_work()
goby::acomms::MMDriver
provides an API to the WHOI Micro-Modem driver
Definition: mm_driver.h:56
goby::acomms::MMDriver::shutdown
void shutdown() override
Stops the driver.
goby::acomms::MMDriver::set_silent
void set_silent(bool silent)