Goby3 3.4.0
2026.04.13
Loading...
Searching...
No Matches
mm_driver.h
Go to the documentation of this file.
1// Copyright 2009-2026:
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
44namespace dccl
45{
46class Codec;
47} // namespace dccl
48
49namespace goby
50{
51namespace acomms
52{
57{
58 public:
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,
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 // goby3_example_driver_simple
237 // \example acomms/chat/chat.cpp
238
239 private:
240 // for the serial connection ($CCCFG,BR1,3)
241 enum
242 {
243 DEFAULT_BAUD = 19200
244 };
245 // failures before closing port and throwing exception
246 enum
247 {
248 MAX_FAILS_BEFORE_DEAD = 5
249 };
250 // how many retries on a given message
251 enum
252 {
253 RETRIES = 3
254 };
255 enum
256 {
257 ROUGH_SPEED_OF_SOUND = 1500
258 }; // m/s
259
260 // time to wait for modem to respond
261 static const goby::time::SystemClock::duration MODEM_WAIT;
262 // time to wait for modem to respond
263 static const goby::time::SystemClock::duration MULTI_REPLY_WAIT;
264 // time to wait after modem reboot
265 static const goby::time::SystemClock::duration WAIT_AFTER_REBOOT;
266
267 static const std::string SERIAL_DELIMITER;
268 // number of frames for a given packet type
269 static const unsigned PACKET_FRAME_COUNT[];
270 // size of packet (in bytes) for a given modem rate
271 static const unsigned PACKET_SIZE[];
272
273 // all startup configuration (DriverConfig defined in acomms_driver_base.proto and extended in acomms_mm_driver.proto)
274 protobuf::DriverConfig driver_cfg_;
275
276 // deque for outgoing messages to the modem, we queue them up and send
277 // as the modem acknowledges them
278 std::deque<util::NMEASentence> out_;
279
280 // time of the last message written. we timeout and resend after MODEM_WAIT seconds
282
283 // are we waiting for a command ack (CA) from the modem or can we send another output?
284 bool waiting_for_modem_{false};
285
286 // are we waiting for a multi-message reply
287 bool waiting_for_multimsg_{false};
288
289 // set after the startup routines finish once. we can't startup on instantiation because
290 // the base class sets some of our references (from the MOOS file)
291 bool startup_done_{false};
292
293 // keeps track of number of failures and exits after reaching MAX_FAILS, assuming modem dead
294 unsigned global_fail_count_{0};
295
296 // keeps track of number of failures on the present talker and moves on to the next talker
297 // if exceeded
298 unsigned present_fail_count_{0};
299
300 // keeps track of clock mode, necessary for synchronous navigation
302
303 // has the clock been properly set. we must reset the clock after reboot ($CAREV,INIT)
304 bool clock_set_{false};
305
306 enum TalkerIDs
307 {
308 TALKER_NOT_DEFINED = 0,
309 CA,
310 CC,
311 SN,
312 GP
313 };
314
315 std::map<std::string, TalkerIDs> talker_id_map_;
316 std::map<std::string, SentenceIDs> sentence_id_map_;
317 std::map<std::string, std::string> description_map_;
318 std::map<std::string, std::string> cfg_map_;
319
320 //
321 // stuff to deal with the non-standard Hydroid gateway buoy
322 //
323
324 // length of #G1 or #M1
325 enum
326 {
327 HYDROID_GATEWAY_PREFIX_LENGTH = 3
328 };
329 // time between requests to the hydroid gateway buoy gps
330 static const goby::time::SystemClock::duration HYDROID_GATEWAY_GPS_REQUEST_INTERVAL;
331 goby::time::SystemClock::time_point last_hydroid_gateway_gps_request_;
332 bool is_hydroid_gateway_{false};
333 std::string hydroid_gateway_modem_prefix_;
334 std::string hydroid_gateway_gps_request_;
335
336 // NVRAM parameters like SRC, DTO, PTO, etc.
337 std::map<std::string, int> nvram_cfg_;
338
339 protobuf::ModemTransmission transmit_msg_;
340 unsigned expected_remaining_caxst_{
341 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)
342
343 protobuf::ModemTransmission receive_msg_;
344 unsigned expected_remaining_cacst_{
345 0}; // used to determine how many CACST to aggregate (so that rate 0 transmissions [CYC and RXD] are provided as a single logical unit)
346
347 // keep track of which frames we've sent and are awaiting acks for. This
348 // way we have a chance of intercepting unexpected behavior of the modem
349 // relating to ACKs
350 std::set<unsigned> frames_waiting_for_ack_;
351
352 // keep track of where we sent the message to be acked to work around a bug in
353 // the MM1 firmware that reports third-party acks with a destination of the local modem
354 unsigned expected_ack_destination_{0};
355
356 std::set<unsigned> frames_waiting_to_receive_;
357
358 // true if we initiated the last cycle ($CCCYC) (and thereby cache data for it)?
359 // false if a third party initiated the last cycle
360 bool local_cccyc_{false};
361
363
364 goby::time::SystemClock::time_point last_keep_alive_time_;
365
366 // time of the last multi-message reply received. we timeout and pop after MULTI_REPLY_WAIT seconds
367 goby::time::SystemClock::time_point last_multimsg_rx_time_;
368
369 struct MMRevision
370 {
371 MMRevision() = default;
372 int mm_major{0};
373 int mm_minor{0};
374 int mm_patch{0};
375 };
376 MMRevision revision_;
377
378 bool using_application_acks_{false};
379 int application_ack_max_frames_{0};
380
381 // ids we are providing acks for, normally just our modem_id()
382 std::set<unsigned> application_ack_ids_;
383
384 int next_frame_{0};
385
386 // modem id to frames
387 std::map<unsigned, std::set<unsigned> > frames_to_ack_;
388
389 std::unique_ptr<dccl::Codec> dccl_;
390 // DCCL requires full memory barrier...
391 static std::mutex dccl_mutex_;
392};
393} // namespace acomms
394} // namespace goby
395#endif
provides an API to the WHOI Micro-Modem driver
Definition mm_driver.h:57
void startup(const protobuf::DriverConfig &cfg) override
Starts the driver.
void handle_initiate_transmission(const protobuf::ModemTransmission &m) override
See ModemDriverBase::handle_initiate_transmission()
void do_work() override
See ModemDriverBase::do_work()
bool is_started() const
Definition mm_driver.h:83
static unsigned packet_frame_count(int rate)
Definition mm_driver.h:85
void update_cfg(const protobuf::DriverConfig &cfg) override
Update configuration while running (not required to be implemented)
int clk_mode()
Current clock mode of the modem, necessary for synchronous navigation.
Definition mm_driver.h:81
void write_single_cfg(const std::string &s)
void set_silent(bool silent)
MMDriver()
Default constructor.
~MMDriver() override
Destructor.
static unsigned packet_size(int rate)
Definition mm_driver.h:87
void shutdown() override
Stops the driver.
provides an abstract base class for acoustic modem drivers. This is subclassed by the various drivers...
Definition driver_base.h:58
_proto_TypeTraits::Singular::ConstType GetExtension(const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< DriverConfig, _proto_TypeTraits, _field_type, _is_packed > &id) const
extern ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< ::goby::acomms::protobuf::DriverConfig, ::PROTOBUF_NAMESPACE_ID::internal::MessageTypeTraits< ::goby::acomms::micromodem::protobuf::Config >, 11, false > config
The global namespace for the Goby project.
extern ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< ::PROTOBUF_NAMESPACE_ID::MessageOptions, ::PROTOBUF_NAMESPACE_ID::internal::MessageTypeTraits< ::goby::GobyMessageOptions >, 11, false > msg
std::chrono::time_point< SystemClock > time_point
std::chrono::microseconds duration
Duration type.