Note: Goby version 1 (shown here) is now considered obsolete. Please use version 2 for new projects, and consider upgrading old projects.

Goby Underwater Autonomy Project  Series: 1.1, revision: 163, released on 2013-02-06 14:23:27 -0500
goby-acomms: libmodemdriver (Driver to interact with modem firmware)

Table of contents for libmodemdriver:

Return to goby-acomms: Overview of Acoustic Communications Libraries.

Abstract class: ModemDriverBase

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

WHOI Micro-Modem Driver: MMDriver

The goby::acomms::MMDriver extends the goby::acomms::ModemDriverBase for the WHOI Micro-Modem acoustic modem. It is tested to work with revision 0.93.0.30 of the Micro-Modem firmware, but is known to work with older firmware (at least 0.92.0.85), as well as newer (WHOI firmware 0.93.0.35 to 0.93.0.51 has a critical bug, however, where XST should be set to 0). The following commands of the WHOI Micro-Modem are implemented:

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

Control Computer to Modem ($CC). Also implemented is the NMEA acknowledge (e.g. $CACLK for $CCCLK):

Mapping between modem_message.proto messages and NMEA fields (see http://acomms.whoi.edu/documents/uModem%20Software%20Interface%20Guide.pdf for NMEA fields of the WHOI Micro-Modem):

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

NMEA talker Mapping
$CAACK goby::acomms::protobuf::ModemDataAck.base().src() = SRC
goby::acomms::protobuf::ModemDataAck.base().dest() = DEST
goby::acomms::protobuf::ModemDataAck.frame() = Frame#-1 (Goby starts counting at frame 0, WHOI starts with frame 1)
$CADRQ Data request is anticipated from the $CCCYC or $CACYC and buffered. Thus it is not translated into any of the modem_message.proto messages.
$CARXD goby::acomms::protobuf::ModemDataTransmission.base().src() = SRC
goby::acomms::protobuf::ModemDataTransmission.base().dest() = DEST
goby::acomms::protobuf::ModemDataTransmission.ack_requested() = ACK
goby::acomms::protobuf::ModemDataTransmission.frame() = F#
goby::acomms::protobuf::ModemDataTransmission.data() = goby::acomms::hex_decode(HH...HH)
$CACYC If we did not send $CCCYC, buffer data for $CADRQ by sending this message n = 0 to Nframes times:
goby::acomms::protobuf::ModemDataRequest.base().src() = ADR1
goby::acomms::protobuf::ModemDataRequest.base().dest() = ADR2
goby::acomms::protobuf::ModemDataRequest.max_bytes() = 32 for Packet Type == 0, 64 for Packet Type == 2, 256 for Packet Type == 3 or 5
goby::acomms::protobuf::ModemDataRequest.frame() = n
$CAREV Not translated into any of the modem_message.proto messages.
$CAERR Not translated into any of the modem_message.proto messages.
$CAMPR goby::acomms::protobuf::ModemRangingReply.base().dest() = SRC (SRC and DEST flipped to be SRC and DEST of $CCMPC)
goby::acomms::protobuf::ModemRangingReply.base().src() = DEST
goby::acomms::protobuf::ModemRangingReply.one_way_travel_time(0) = Travel Time
goby::acomms::protobuf::ModemRangingReply.type() = goby::acomms::protobuf::MODEM_TWO_WAY_PING
$SNTTA goby::acomms::protobuf::ModemRangingReply.base().src() = modem_id
goby::acomms::protobuf::ModemRangingReply.one_way_travel_time(0) = TA
goby::acomms::protobuf::ModemRangingReply.one_way_travel_time(1) = TB
goby::acomms::protobuf::ModemRangingReply.one_way_travel_time(2) = TC
goby::acomms::protobuf::ModemRangingReply.one_way_travel_time(3) = TD
goby::acomms::protobuf::ModemRangingReply.type() = goby::acomms::protobuf::REMUS_LBL_RANGING
$CATOA goby::acomms::protobuf::ModemRangingReply.base().src() = SRC (of related $CARXD, $CAACK)
goby::acomms::protobuf::ModemRangingReply.base().dest() = DEST (of related $CARXD, $CAACK)
Travel times are ambiguous: have to figure it out from knowledge we don't have here goby::acomms::protobuf::ModemRangingReply.one_way_travel_time(0) = 0.SSSS
goby::acomms::protobuf::ModemRangingReply.one_way_travel_time(1) = 1.SSSS
goby::acomms::protobuf::ModemRangingReply.one_way_travel_time(2) = 2.SSSS
goby::acomms::protobuf::ModemRangingReply.one_way_travel_time(n) = n.SSSS (n = floor(10000 m / 1500 m/s) since 10km is a sensible upper bound on the WHOI Micro-Modem performance)
goby::acomms::protobuf::ModemRangingReply.type() = goby::acomms::protobuf::MODEM_ONE_WAY_SYNCHRONOUS

Control Computer to Modem ($CC):

$CCTXD SRC = goby::acomms::protobuf::ModemDataTransmission.base().src()
DEST = goby::acomms::protobuf::ModemDataTransmission.base().dest()
A = goby::acomms::protobuf::ModemDataTransmission.ack_requested()
HH...HH = goby::acomms::hex_encode(goby::acomms::protobuf::ModemDataTransmission.data())
$CCCYC CMD = 0 (deprecated field)
ADR1 = goby::acomms::protobuf::ModemDataInit.base().src()
ADR2 = goby::acomms::protobuf::ModemDataInit.base().dest()
Packet Type = goby::acomms::protobuf::ModemDataInit.base().rate()
ACK = 0 (deprecated field)
Nframes = goby::acomms::protobuf::ModemDataInit.num_frames()

If ADR1 == modem_id, buffer data for later $CADRQ by sending this message n = 0 to Nframes times:
goby::acomms::protobuf::ModemDataRequest.base().src() = ADR1
goby::acomms::protobuf::ModemDataRequest.base().dest() = ADR2
goby::acomms::protobuf::ModemDataRequest.max_bytes() = 32 for Packet Type == 0, 64 for Packet Type == 2, 256 for Packet Type == 3 or 5
goby::acomms::protobuf::ModemDataRequest.frame() = n
$CCCLK Not translated from any of the modem_message.proto messages. (taken from the system time using the boost::date_time library)
$CCCFG Not translated from any of the modem_message.proto messages. (taken from values passed to the extension MicroModemConfig::nvram_cfg of goby::acomms::protobuf::DriverConfig)
$CCCFQ Not translated from any of the modem_message.proto messages. $CCCFQ,ALL sent at startup.
$CCMPC protobuf::MODEM_TWO_WAY_PING == goby::acomms::protobuf::ModemRangingRequest.type()
SRC = goby::acomms::protobuf::ModemRangingRequest.base().src()
DEST = goby::acomms::protobuf::ModemRangingRequest.base().dest()
$CCPDT protobuf::REMUS_LBL_RANGING == goby::acomms::protobuf::ModemRangingRequest.type()
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::protobuf::ModemRangingRequest.max_range() m *2/ 1500 m/s * 1000 ms/s
goby::acomms::protobuf::ModemRangingRequest.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::protobuf::ModemRangingRequest.enable_beacons() >> 0 & 1
BF = goby::acomms::protobuf::ModemRangingRequest.enable_beacons() >> 1 & 1
CF = goby::acomms::protobuf::ModemRangingRequest.enable_beacons() >> 2 & 1
DF = goby::acomms::protobuf::ModemRangingRequest.enable_beacons() >> 3 & 1

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.

These are the requirements of the acoustic modem:

Optionally, it can also support

The steps to writing a new driver include:

The full ABC Modem example driver exists in acomms/libmodemdriver/abc_driver.h and acomms/libmodemdriver/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:

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends