Goby3  3.1.4
2024.02.22
goby-acomms: modemdriver (Driver to interact with modem firmware)

Abstract class: ModemDriverBase

goby::acomms::ModemDriverBase defines the core functionality for an acoustic modem. It provides

Interacting with the goby::acomms::ModemDriverBase

To use the goby::acomms::ModemDriverBase, you need to create one of its implementations such as goby::acomms::MMDriver (WHOI Micro-Modem).

goby::acomms::ModemDriverBase* driver = new goby::acomms::MMDriver;

You will also need to configure the driver. At the very least this involves a serial port, baud, and modem ID (integer MAC address for the modem).

goby::acomms::protobuf::DriverConfig cfg;
cfg.set_serial_port("/dev/ttyS0");
cfg.set_modem_id(3);

Most modems will have specific other configuration that is required. For example the WHOI Micro-Modem NVRAM is set using three character strings followed by a number. This modem-specific configuration is stored as Protobuf extensions to goby::acomms::protobuf::DriverConfig, such as goby::acomms::micromodem::protobuf::config. If we were using the WHOI Micro-Modem and wanted to add an NVRAM configuration value we could write

cfg.MutableExtension(goby::acomms::micromodem::protobuf::config).add_nvram_cfg("DQF,1");

We need to connect any signals we are interested in. At a minimum this is goby::acomms::ModemDriverBase::signal_receive:

goby::acomms::connect(&driver->signal_receive, &handle_data_receive);

where handle_data_receive has the signature:

void handle_data_receive(const goby::acomms::protobuf::ModemTransmission& data_msg);

Next, we start up the driver with our configuration:

driver->startup(cfg);

We need to call goby::acomms::ModemDriverBase::do_work() on some reasonable frequency (greater than 5 Hz; 10 Hz is probably good). Whenever we need to transmit something, we can either directly call goby::acomms::ModemDriverBase::handle_initiate_transmission or connect goby::acomms::MACManager to do so for us on some TDMA cycle.

Protobuf Message goby::acomms::protobuf::ModemTransmission

The goby::acomms::protobuf::ModemTransmission message is used for all outgoing (sending) and incoming (receiving) messages. The message itself only contains the subset of modem functionality that every modem is expected to support (point-to-point transmission of datagrams).

All other functionality is provided by extensions to ModemTransmission such as those in mm_driver.proto for the WHOI Micro-Modem. These extensions provide access to additional features of the WHOI Micro-Modem (such as LBL ranging, two-way pings, and comprehensive receive statistics).

By making use of the Protobuf extensions in this way, Goby can both support unique features of a given modem while at that same time remaining general and agnostic to which modem is used when the features are shared (primarily data transfer).

Writing a new driver

All of goby-acomms is designed to be agnostic of which physical modem is used. Different modems can be supported by subclassing goby::acomms::ModemDriverBase. You should check that a driver for your modem does not yet exist before attempting to create your own.

These are the requirements of the acoustic modem:

  • it communicates using a line based text duplex connection using either serial or TCP (either client or server). NMEA0183 and AT (Hayes) protocols fulfill this requirement, for example. You can also write a driver that uses a different communication transport by implementing it directly in the driver rather than using the functionality in goby::acomms::DriverBase.
  • it is capable of sending and verifying the accuracy (using a cyclic redundancy check or similar error checking) of fixed size datagrams (note that modems capable of variable sized datagrams also fit into this category).

Optionally, it can also support

  • Acoustic acknowledgment of proper message receipt.
  • Ranging to another acoustic modem or LBL beacons using time of flight measurements
  • User selectable bit rates

The steps to writing a new driver include:

  • Fully understand the basic usage of the new acoustic modem manually using minicom or other terminal emulator. Have a copy of the modem software interface manual handy.
  • Figure out what type of configuration the modem will need. For example, the WHOI Micro-Modem is configured using string values (e.g. "SNV,1"). Extend goby::acomms::protobuf::DriverConfig to accomodate these configuration options. You will need to claim a group of extension field numbers that do not overlap with any of the drivers. The WHOI Micro-Modem driver goby::acomms::MMDriver uses extension field numbers 1000-1100 (see mm_driver.proto). You can read more about extensions in the official Google Protobuf documentation here: https://developers.google.com/protocol-buffers/docs/proto. For example, if I was writing a new driver for the ABC Modem that needs to be configured using a few boolean flags, I might create a new message abc_driver.proto, make a note in driver_base.proto claiming extension number 1201.
  • Subclass goby::acomms::ModemDriverBase and overload the pure virtual methods. Your interface should look like this:
namespace goby
{
namespace acomms
{
namespace protobuf
{
class ModemTransmission;
} // namespace protobuf
class ABCDriver : public ModemDriverBase
{
public:
void startup(const protobuf::DriverConfig& cfg) override;
void shutdown() override;
void do_work() override;
void handle_initiate_transmission(const protobuf::ModemTransmission& m) override;
private:
protobuf::DriverConfig driver_cfg_; // configuration given to you at launch
// rest is up to you!
};
} // namespace acomms
} // namespace goby

en

  • Fill in the methods. You are responsible for emitting the goby::acomms::ModemDriverBase signals at the appropriate times. Read on and all should be clear.
goby::acomms::ABCDriver::ABCDriver()
{
// other initialization you can do before you have your goby::acomms::DriverConfig configuration object
}
  • At startup() you get your configuration from the application (e.g. pAcommsHandler)
void goby::acomms::ABCDriver::startup(const protobuf::DriverConfig& cfg)
{
driver_cfg_ = cfg;
// check `driver_cfg_` to your satisfaction and then start the modem physical interface
if (!driver_cfg_.has_serial_baud())
driver_cfg_.set_serial_baud(DEFAULT_BAUD);
glog.is(DEBUG1) && glog << group(glog_out_group())
<< "ABCDriver configuration good. Starting modem..." << std::endl;
ModemDriverBase::modem_start(driver_cfg_);
// set your local modem id (MAC address)
{
std::stringstream raw;
raw << "CONF,MAC:" << driver_cfg_.modem_id() << "\r\n";
signal_and_write(raw.str());
}
// now set our special configuration values
{
std::stringstream raw;
raw << "CONF,FOO:"
<< driver_cfg_.GetExtension(goby::acomms::abc::protobuf::config).enable_foo() << "\r\n";
signal_and_write(raw.str());
}
{
std::stringstream raw;
raw << "CONF,BAR:"
<< driver_cfg_.GetExtension(goby::acomms::abc::protobuf::config).enable_bar() << "\r\n";
signal_and_write(raw.str());
}
} // startup
  • At shutdown() you should make yourself ready to startup() again if necessary and stop the modem:
    {
    // put the modem in a low power state?
    // ...
    ModemDriverBase::modem_close();
    } // shutdown
  • handle_initiate_transmission() is called when you are expected to initiate a transmission. It may contain data (in the ModemTransmission::frame field). If not, you are required to request data using the goby::acomms::ModemDriverBase::signal_data_request signal. Once you have data, you are responsible for sending it. I think a bit of code will make this clearer:
    const protobuf::ModemTransmission& orig_msg)
    {
    // copy so we can modify
    protobuf::ModemTransmission msg = orig_msg;
    // rate() can be 0 (lowest), 1, 2, 3, 4, or 5 (lowest). Map these integers onto real bit-rates
    // in a meaningful way (on the WHOI Micro-Modem 0 ~= 80 bps, 5 ~= 5000 bps).
    glog.is(DEBUG1) && glog << group(glog_out_group()) << "We were asked to transmit from "
    << msg.src() << " to " << msg.dest() << " at bitrate code "
    << msg.rate() << std::endl;
    // let's say ABC modem uses 500 byte packet
    msg.set_max_frame_bytes(500);
    // no data given to us, let's ask for some
    if (msg.frame_size() == 0)
    ModemDriverBase::signal_data_request(&msg);
    glog.is(DEBUG1) && glog << group(glog_out_group()) << "Sending these data now: " << msg.frame(0)
    << std::endl;
    // let's say we can send at three bitrates with ABC modem: map these onto 0-5
    const unsigned BITRATE[] = {100, 1000, 10000, 10000, 10000, 10000};
    // I'm making up a syntax for the wire protocol...
    std::stringstream raw;
    raw << "SEND,TO:" << msg.dest() << ",FROM:" << msg.src() << ",HEX:" << hex_encode(msg.frame(0))
    << ",BITRATE:" << BITRATE[msg.rate()] << ",ACK:TRUE"
    << "\r\n";
    // let anyone who is interested know
    signal_and_write(raw.str());
    } // handle_initiate_transmission
  • Finally, you can use do_work() to do continuous work. You can count on it being called at 5 Hz or more (in pAcommsHandler, it is called on the MOOS AppTick). Here's where you want to read the modem incoming stream.
    {
    std::string in;
    while (modem_read(&in))
    {
    std::map<std::string, std::string> parsed;
    // breaks `in`: "RECV,TO:3,FROM:6,HEX:ABCD015910"
    // into `parsed`: "KEY"=>"RECV", "TO"=>"3", "FROM"=>"6", "HEX"=>"ABCD015910"
    try
    {
    boost::trim(in); // get whitespace off from either end
    parse_in(in, &parsed);
    // let others know about the raw feed
    protobuf::ModemRaw raw;
    raw.set_raw(in);
    ModemDriverBase::signal_raw_incoming(raw);
    protobuf::ModemTransmission msg;
    msg.set_src(goby::util::as<std::int32_t>(parsed["FROM"]));
    msg.set_dest(goby::util::as<std::int32_t>(parsed["TO"]));
    msg.set_time_with_units(time::SystemClock::now<time::MicroTime>());
    glog.is(DEBUG1) && glog << group(glog_in_group()) << in << std::endl;
    if (parsed["KEY"] == "RECV")
    {
    msg.set_type(protobuf::ModemTransmission::DATA);
    msg.add_frame(hex_decode(parsed["HEX"]));
    glog.is(DEBUG1) && glog << group(glog_in_group()) << "received: " << msg
    << std::endl;
    }
    else if (parsed["KEY"] == "ACKN")
    {
    msg.set_type(protobuf::ModemTransmission::ACK);
    }
    ModemDriverBase::signal_receive(msg);
    }
    catch (std::exception& e)
    {
    glog.is(WARN) && glog << "Bad line: " << in << std::endl;
    glog.is(WARN) && glog << "Exception: " << e.what() << std::endl;
    }
    }
    } // do_work

The full ABC Modem example driver exists in acomms/modemdriver/abc_driver.h and acomms/modemdriver/abc_driver.cpp. A simulator for the ABC Modem exists that uses TCP to mimic a very basic set of modem commands (send data and acknowledgment). To use the ABC Modem using the driver_simple example, run this set of commands (socat is available in most package managers or at http://www.dest-unreach.org/socat/):

1. run abc_modem_simulator running on same port (as TCP server)
> abc_modem_simulator 54321
2. create fake tty terminals connected to TCP as client to port 54321
> socat -d -d -v pty,raw,echo=0,link=/tmp/ttyFAKE1 TCP:localhost:54321
> socat -d -d -v pty,raw,echo=0,link=/tmp/ttyFAKE2 TCP:localhost:54321
3. start up driver_simple
> driver_simple /tmp/ttyFAKE1 1 ABCDriver
// wait a few seconds to avoid collisions
> driver_simple /tmp/ttyFAKE2 2 ABCDriver

Notes:

WHOI Micro-Modem Driver: MMDriver

Supported Functionality

The goby::acomms::MMDriver extends the goby::acomms::ModemDriverBase for the WHOI Micro-Modem acoustic modem. It is tested to work with revision 0.94.0.00 of the Micro-Modem 1 and revision 2.0.16421 of the Micro-Modem 2 firmware, but is known to work with older firmware (at least 0.92.0.85). It is likely to work properly with newer firmware, and any problems while using newer Micro-Modem firmware should be filed as a bug in Goby. The following features of the WHOI Micro-Modem are implemented, which comprise the majority of the Micro-Modem functionality:

  • FSK (rate 0) data transmission
  • PSK (rates 1,2,3,4,5) data transmission
  • Narrowband transponder LBL ping
  • REMUS transponder LBL ping
  • User mini-packet 13 bit data transmission
  • Two way ping
  • Flexible Data Protocol (Micro-Modem 2 only)
  • Transmit FM sweep
  • Transmit M-sequence

Micro-Modem NMEA to Goby ModemTransmission mapping

Mapping between modem_message.proto and mm_driver.proto messages and NMEA fields (see the MicroModem users guide at https://acomms.whoi.edu/micro-modem/software-interface/ for NMEA fields of the WHOI Micro-Modem):

Modem to Control Computer ($CA / $SN):

NMEA talker Mapping
$CACYC If we did not send $CCCYC, buffer data for $CADRQ by augmenting the provided ModemTransmission and calling signal_data_request:
goby::acomms::protobuf::ModemTransmission.time() = goby::util::goby_time<uint64>()
goby::acomms::protobuf::ModemTransmission.src() = ADR1
goby::acomms::protobuf::ModemTransmission.dest() = ADR2
goby::acomms::protobuf::ModemTransmission.rate() = Packet Type
goby::acomms::protobuf::ModemTransmission.max_frame_bytes() = 32 for Packet Type == 0, 64 for Packet Type == 2, 256 for Packet Type == 3 or 5
goby::acomms::protobuf::ModemTransmission.max_num_frames() = 1 for Packet Type == 0, 3 for Packet Type == 2, 2 for Packet Type == 3 or 8 for Packet Type == 5
$CARXD only for the first $CARXD for a given packet (should match with the rest though):
goby::acomms::protobuf::ModemTransmission.time() = goby::util::goby_time<uint64>()
goby::acomms::protobuf::ModemTransmission.type() = goby::acomms::protobuf::ModemTransmission::DATA
goby::acomms::protobuf::ModemTransmission.src() = SRC
goby::acomms::protobuf::ModemTransmission.dest() = DEST
goby::acomms::protobuf::ModemTransmission.ack_requested() = ACK
for each $CARXD:
goby::acomms::protobuf::ModemTransmission.frame(F#-1) = goby::util::hex_decode(HH...HH)
$CAMSG Used only to detect BAD_CRC frames ($CAMSG,BAD_CRC...). (in extension goby::acomms::micromodem::protobuf::Transmission::frame_with_bad_crc)
goby::acomms::micromodem::protobuf::frame_with_bad_crc(n) = Frame with BAD CRC (assumed next frame after last good frame). n is an integer 0,1,2,... indicating the nth reported BAD_CRC frame for this packet. (not the frame number)
$CAACK goby::acomms::protobuf::ModemTransmission.time() = goby::util::goby_time<uint64>()
goby::acomms::protobuf::ModemTransmission.src() = SRC
goby::acomms::protobuf::ModemTransmission.dest() = DEST
(first CAACK for a given packet) goby::acomms::protobuf::ModemTransmission.acked_frame(0) = Frame#-1 (Goby starts counting at frame 0, WHOI starts with frame 1)
(second CAACK for a given packet) goby::acomms::protobuf::ModemTransmission.acked_frame(1) = Frame#-1
(third CAACK for a given packet) goby::acomms::protobuf::ModemTransmission.acked_frame(2) = Frame#-1
...
$CAMUA goby::acomms::protobuf::ModemTransmission.type() = goby::acomms::protobuf::ModemTransmission::DRIVER_SPECIFIC
extension goby::acomms::micromodem::protobuf::Transmission::type = micromodem::protobuf::MICROMODEM_MINI_DATA
goby::acomms::protobuf::ModemTransmission.time() = goby::util::goby_time<uint64>()
goby::acomms::protobuf::ModemTransmission.src() = SRC
goby::acomms::protobuf::ModemTransmission.dest() = DEST
goby::acomms::protobuf::ModemTransmission.frame(0) = goby::util::hex_decode(HHHH)
$CAMPR goby::acomms::protobuf::ModemTransmission.time() = goby::util::goby_time<uint64>()
goby::acomms::protobuf::ModemTransmission.dest() = SRC (SRC and DEST flipped to be SRC and DEST of $CCMPC)
goby::acomms::protobuf::ModemTransmission.src() = DEST
goby::acomms::protobuf::ModemTransmission.type() = goby::acomms::protobuf::ModemTransmission::DRIVER_SPECIFIC
extension goby::acomms::micromodem::protobuf::Transmission::type = goby::acomms::micromodem::protobuf::MICROMODEM_TWO_WAY_PING
(in extension goby::acomms::micromodem::Transmission::protobuf::ranging_reply)
goby::acomms::micromodem::protobuf::RangingReply::one_way_travel_time(0) = Travel Time
$CAMPA goby::acomms::protobuf::ModemTransmission.time() = goby::util::goby_time<uint64>()
goby::acomms::protobuf::ModemTransmission.src() = SRC
goby::acomms::protobuf::ModemTransmission.dest() = DEST
goby::acomms::protobuf::ModemTransmission.type() = goby::acomms::protobuf::ModemTransmission::DRIVER_SPECIFIC
extension goby::acomms::micromodem::protobuf::Transmission::type = goby::acomms::micromodem::protobuf::MICROMODEM_TWO_WAY_PING
$SNTTA goby::acomms::protobuf::ModemTransmission.time() = hhmmsss.ss (converted to microseconds since 1970-01-01 00:00:00 UTC)
goby::acomms::protobuf::ModemTransmission.time_source() = goby::acomms::protobuf::MODEM_TIME
goby::acomms::protobuf::ModemTransmission.type() = goby::acomms::protobuf::ModemTransmission::DRIVER_SPECIFIC
extension goby::acomms::micromodem::protobuf::Transmission::type = micromodem::protobuf::MICROMODEM_REMUS_LBL_RANGING or micromodem::protobuf::MICROMODEM_NARROWBAND_LBL_RANGING (depending on which LBL type was last initiated)
goby::acomms::protobuf::ModemTransmission.src() = modem ID
(in extension goby::acomms::micromodem::protobuf::Transmission::ranging_reply)
goby::acomms::micromodem::protobuf::RangingReply.one_way_travel_time(0) = TA
goby::acomms::micromodem::protobuf::RangingReply.one_way_travel_time(1) = TB
goby::acomms::micromodem::protobuf::RangingReply.one_way_travel_time(2) = TC
goby::acomms::micromodem::protobuf::RangingReply.one_way_travel_time(3) = TD
$CAXST maps onto extension goby::acomms::micromodem::protobuf::Transmission::transmit_stat of type goby::acomms::micromodem::protobuf::TransmitStatistics. The two $CAXST messages (CYC and data) for a rate 0 FH-FSK transmission are grouped and reported at once.
$CACST maps onto extension goby::acomms::micromodem::protobuf::Transmission::receive_stat of type micromodem::protobuf::ReceiveStatistics. The two $CACST messages for a rate 0 FH-FSK transmission are grouped and reported at once. Note that this message contains the one way time of flight for synchronous ranging (used instead of $CATOA).
Also sets (which will overwrite goby_time() set previously):
goby::acomms::protobuf::ModemTransmission.time() = TOA time (converted to microseconds since 1970-01-01 00:00:00 UTC)
goby::acomms::protobuf::ModemTransmission.time_source() = goby::acomms::protobuf::MODEM_TIME
$CAREV Not translated into any of the modem_message.proto messages. Monitored to detect excessive clock skew (between Micro-Modem clock and system clock) or reboot (INIT)
$CAERR Not translated into any of the modem_message.proto messages. Reported to goby::glog.
$CACFG NVRAM setting stored internally.
$CACLK Checked against system clock and if skew is unacceptable another $CCCLK will be sent.
$CADRQ Data request is anticipated from the $CCCYC or $CACYC and buffered. Thus it is not translated into any of the Protobuf messages.
$CARDP goby::acomms::protobuf::ModemTransmission.type() = goby::acomms::protobuf::ModemTransmission::DRIVER_SPECIFIC
extension goby::acomms::micromodem::protobuf::Transmission::type = micromodem::protobuf::MICROMODEM_FLEXIBLE_DATA
goby::acomms::protobuf::ModemTransmission.src() = src
goby::acomms::protobuf::ModemTransmission.dest() = dest
goby::acomms::protobuf::ModemTransmission.rate() = rate
goby::acomms::protobuf::ModemTransmission::frame(0) = goby::util::hex_decode(df1+df2+df3...dfN) where "+" means concatenate, unless any frame fails the CRC check, in which case this field is set to the empty string.
micromodem::protobuf::frame_with_bad_crc(0) = 0 indicated that Goby frame 0 is bad, if any sub-frame in the FDP has a bad CRC

Control Computer to Modem ($CC):

$CCTXD SRC = goby::acomms::protobuf::ModemTransmission..src()
DEST = goby::acomms::protobuf::ModemTransmission.dest()
A = goby::acomms::protobuf::ModemTransmission.ack_requested()
HH...HH = goby::acomms::hex_encode(goby::acomms::protobuf::ModemTransmission::frame(n)), which n is an integer 0,1,2,... corresponding to the Goby frame that this $CCTXD belongs to.
$CCCYC Augment the ModemTransmission:
goby::acomms::protobuf::ModemTransmission.max_frame_bytes() = 32 for Packet Type == 0, 64 for Packet Type == 2, 256 for Packet Type == 3 or 5
goby::acomms::protobuf::ModemTransmission.max_num_frames() = 1 for Packet Type == 0, 3 for Packet Type == 2, 2 for Packet Type == 3 or 8 for Packet Type == 5
If ADR1 == modem ID and frame_size() < max_frame_size(), buffer data for later $CADRQ by passing the ModemTransmission to signal_data_request
CMD = 0 (deprecated field)
ADR1 = goby::acomms::protobuf::ModemTransmission.src()
ADR2 = goby::acomms::protobuf::ModemTransmission.dest()
Packet Type = goby::acomms::protobuf::ModemTransmission.rate()
ACK = if ADR1 == modem ID then goby::acomms::protobuf::ModemTransmission.ack_requested() else 1
Nframes = goby::acomms::protobuf::ModemTransmission.max_num_frames()

$CCCLK Not translated from any of the modem_message.proto messages. (taken from the system time)
$CCCFG Not translated from any of the modem_message.proto messages. (taken from values passed to the extension goby::acomms::micromodem::protobuf::Config::nvram_cfg of goby::acomms::protobuf::DriverConfig). If the extension goby::acomms::micromodem::protobuf::Config::reset_nvram is set to true, $CCCFG,ALL,0 will be sent before any other $CCCFG values.)
$CCCFQ Not translated from any of the modem_message.proto messages. $CCCFQ,ALL sent at startup.
$CCMPC goby::acomms::micromodem::protobuf::MICROMODEM_TWO_WAY_PING == extension goby::acomms::micromodem::protobuf::Transmission::type
SRC = goby::acomms::protobuf::ModemTransmission.src()
DEST = goby::acomms::protobuf::ModemTransmission.dest()
$CCPDT goby::acomms::micromodem::protobuf::protobuf::MICROMODEM_REMUS_LBL_RANGING == extension goby::acomms::micromodem::protobuf::Transmission::type
micromodem::protobuf::REMUSLBLParams type used to determine the parameters of the LBL ping. The object provided with configuration (micromodem::protobuf::Config::remus_lbl) is merged with the object provided with the ModemTransmission (micromodem::protobuf::remus_lbl) with the latter taking priority on fields that a set in both objects:
GRP = 1
CHANNEL = modem ID % 4 + 1 (use four consecutive modem IDs if you need multiple vehicles pinging)
SF = 0
STO = 0
Timeout = goby::acomms::micromodem::protobuf::REMUSLBLParams::lbl_max_range() m *2/ 1500 m/s * 1000 ms/s + goby::acomms::micromodem::protobuf::REMUSLBLParams::turnaround_ms()
goby::acomms::micromodem::protobuf::REMUSLBLParams::enable_beacons() is a set of four bit flags where the least significant bit is AF enable, most significant bit is DF enable. Thus b1111 == 0x0F enables all beacons
AF = goby::acomms::micromodem::protobuf::REMUSLBLParams::enable_beacons() >> 0 & 1
BF = goby::acomms::micromodem::protobuf::REMUSLBLParams::enable_beacons() >> 1 & 1
CF = goby::acomms::micromodem::protobuf::REMUSLBLParams::enable_beacons() >> 2 & 1
DF = goby::acomms::micromodem::protobuf::REMUSLBLParams::enable_beacons() >> 3 & 1
$CCPNT

goby::acomms::micromodem::protobuf::protobuf::MICROMODEM_NARROWBAND_LBL_RANGING == extension goby::acomms::micromodem::protobuf::Transmission::type
goby::acomms::micromodem::protobuf::NarrowBandLBLParams type used to determine the parameters of the LBL ping. The object provided with configuration (goby::acomms::micromodem::protobuf::Config::narrowband_lbl) is merged with the object provided with the ModemTransmission (goby::acomms::micromodem::protobuf::narrowband_lbl) with the latter taking priority on fields that a set in both objects:

Ftx = goby::acomms::micromodem::protobuf::NarrowBandLBLParams::transmit_freq()
Ttx = goby::acomms::micromodem::protobuf::NarrowBandLBLParams::transmit_ping_ms()
Trx = goby::acomms::micromodem::protobuf::NarrowBandLBLParams::receive_ping_ms()
Timeout = goby::acomms::micromodem::protobuf::NarrowBandLBLParams::lbl_max_range() m * 2/ 1500 m/s * 1000 ms/s + goby::acomms::micromodem::protobuf::NarrowBandLBLParams::turnaround_ms()
FA = goby::acomms::micromodem::protobuf::NarrowBandLBLParams::receive_freq(0) or 0 if receive_freq_size() < 1
FB = goby::acomms::micromodem::protobuf::NarrowBandLBLParams::receive_freq(1) or 0 if receive_freq_size() < 2
FC = goby::acomms::micromodem::protobuf::NarrowBandLBLParams::receive_freq(2) or 0 if receive_freq_size() < 3
FD = goby::acomms::micromodem::protobuf::NarrowBandLBLParams::receive_freq(3) or 0 if receive_freq_size() < 4
Tflag = micromodem::protobuf::NarrowBandLBLParams::transmit_flag()

$CCMUC SRC = goby::acomms::protobuf::ModemTransmission.src()
DEST = goby::acomms::protobuf::ModemTransmission.dest()
HHHH = goby::acomms::hex_encode(goby::acomms::protobuf::ModemTransmission::frame(0)) & 0x1F
$CCTDP dest = goby::acomms::protobuf::ModemTransmission.dest()
rate = goby::acomms::protobuf::ModemTransmission.rate()
ack = 0 (not yet supported by the Micro-Modem 2)
reserved = 0
hexdata = goby::acomms::hex_encode(goby::acomms::protobuf::ModemTransmission::frame(0))

Sequence diagrams for various Micro-Modem features using Goby

FSK (rate 0) data transmission

PSK (rate 2 shown, others are similar) data transmission

Narrowband transponder LBL ping

REMUS transponder LBL ping

User mini-packet 13 bit data transmission

Two way ping

Flexible Data Protocol (Micro-Modem 2)

UDP Multicast Driver

The goby::acomms::UDPMulticastDriver provides an easy localhost testing interface as it implements ModemDriverBase for an Internet Protocol (IP) User Datagram Protocol (UDP) multicast transport.

For example, configure any number of modems running on a multicast enabled network:

modem_id: 1
driver_type: DRIVER_UDP_MULTICAST
[goby.acomms.udp_multicast.protobuf.config] {
listen_address: "0.0.0.0"
multicast_address: "239.142.0.10"
multicast_port: 50031
max_frame_size: 1400
}

UDP Driver

The goby::acomms::UDPDriver is similar to the goby::acomms::UDPMulticastDriver but rather uses unicast UDP packets to an explicitly configured list of remote destinations. This is better suited when routing is involved.

Configuration merely involves setting the local udp port, and at least one remote endpoint (ip address, port, and modem id). For example, for a pair of modems (ids 1 and 2) running on localhost:

Modem 1

modem_id: 1
driver_type: DRIVER_UDP
[goby.acomms.udp.protobuf.config] {
local {
port: 50001
}
remote {
modem_id: 2
ip: "127.0.0.1"
port: 50002
}
max_frame_size: 1400
}

Modem 2

modem_id: 2
driver_type: DRIVER_UDP
[goby.acomms.udp.protobuf.config] {
local {
port: 50002
}
remote {
modem_id: 1
ip: "127.0.0.1"
port: 50001
}
max_frame_size: 1400
}

Iridium Drivers

The goby::acomms::IridiumDriver was designed and testing on the Iridium 9523 for both RUDICS and short burst data (SBD). It may also work on other Iridium RUDICS and/or SBD enabled devices. It is intended to be used in companion with the goby::acomms::IridiumShoreDriver to handle DirectIP data and RUDICS in-bound (mobile-originated or MO) calls. Making calls from the shore station (mobile-terminated or MT) is not well supported by Iridium, and is thus not supported in Goby.

Benthos Driver

The Benthos ATM900 series of acoustic modems is supported by the goby::acomms::BenthosATM900Driver using the Benthos CLAM shell and AT commands.

An example configuration might look like:

modem_id: 1
driver_type: DRIVER_BENTHOS_ATM900
connection_type: CONNECTION_SERIAL
serial_port: "/dev/ttyS0"
[goby.acomms.benthos.protobuf.config] {
factory_reset: false
start_timeout: 20
max_frame_size: 128
config: "@TxPower=8"
}
goby::util::FlexOstream::is
bool is(goby::util::logger::Verbosity verbosity)
goby
The global namespace for the Goby project.
Definition: acomms_constants.h:33
goby::acomms::ABCDriver::do_work
void do_work() override
Allows the modem driver to do its work.
goby::acomms::abc::protobuf::config
extern ::google::protobuf::internal::ExtensionIdentifier< ::goby::acomms::protobuf::DriverConfig, ::google::protobuf::internal::MessageTypeTraits< ::goby::acomms::abc::protobuf::Config >, 11, false > config
Definition: abc_driver.pb.h:203
goby::util::logger::WARN
@ WARN
Definition: flex_ostreambuf.h:74
goby::util::e
constexpr T e
Definition: constants.h:35
group
goby::util::logger::GroupSetter group(std::string n)
Definition: logger_manipulators.h:134
goby::acomms::ABCDriver::handle_initiate_transmission
void handle_initiate_transmission(const protobuf::ModemTransmission &m) override
Virtual initiate_transmission method. Typically connected to MACManager::signal_initiate_transmission...
goby::acomms::ABCDriver::ABCDriver
ABCDriver()
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::util::hex_decode
void hex_decode(const std::string &in, std::string *out)
Decodes a (little-endian) hexadecimal string to a byte string. Index 0 and 1 (first byte) of in are w...
Definition: binary.h:47
goby::acomms::ABCDriver::startup
void startup(const protobuf::DriverConfig &cfg) override
Starts the modem driver. Must be called before poll().
goby::acomms::ABCDriver::shutdown
void shutdown() override
Shuts down the modem driver.
goby::util::logger::DEBUG1
@ DEBUG1
Definition: flex_ostreambuf.h:77
goby::util::hex_encode
void hex_encode(const std::string &in, std::string *out, bool upper_case=false)
Encodes a (little-endian) hexadecimal string from a byte string. Index 0 of in is written to index 0 ...
Definition: binary.h:94
httplib::detail::trim
std::pair< size_t, size_t > trim(const char *b, const char *e, size_t left, size_t right)
Definition: httplib.h:2472
goby::glog
util::FlexOstream glog
Access the Goby logger through this object.