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: Overview of Acoustic Communications Libraries

Table of Contents for goby-acomms: Overview of Acoustic Communications Libraries.

Quick Start

To get started using the goby-acomms libraries as quickly as possible:

  1. If you haven't yet, follow instructions on installing Goby.
  2. Identify which components you need:
    • Encoding and decoding from C++ types to bit-packed messages: libdccl.
    • Queuing of DCCL and CCL messages with priority based message selection: libqueue.
    • A driver for interacting with the acoustic modem firmware. Presently the WHOI Micro-Modem <http://acomms.whoi.edu/> is supported: libmodemdriver.
    • Time division multiple access (TDMA) medium access control (MAC): libamac.
  3. Look at the "simple" code examples that accompany each library (dccl_simple.cpp, queue_simple.cpp, driver_simple.cpp, amac_simple.cpp). Then look at the example that uses all the libraries together: chat.cpp. The full list of examples is given in this table.
  4. Refer to the rest of the documentation as needed.

Please visit <https://answers.launchpad.net/goby> with any questions.

Overview

Analogy to established networking systems

To start on some (hopefully) common ground, let's begin with an analogy to Open Systems Initiative (OSI) networking layers in this table. For a complete description of the OSI layers see <http://www.itu.int/rec/T-REC-X.200-199407-I/en>.

OSI Layer Goby library API class(es) Example(s)
Application Not yet part of Goby MOOS Application: pAcommsHandler
Presentation libdccl: Encoding and decoding goby::acomms::DCCLCodec dccl_simple.cpp
two_message.cpp
plusnet.cpp
test.cpp
chat.cpp
Session Not used, sessions are established passively.
Transport libqueue: Priority based message queuing goby::acomms::QueueManager queue_simple.cpp
chat.cpp
Network Does not yet exist. All transmissions are considered single hop, currently. Addressing routing over multiple hops is an open and pressing research problem.
Data Link libmodemdriver: Modem driver classes derived from goby::acomms::ModemDriverBase; e.g. goby::acomms::MMDriver driver_simple.cpp
chat.cpp
libamac: Medium Access Control (MAC) goby::acomms::MACManager amac_simple.cpp
chat.cpp
Physical Not part of Goby Modem Firmware, e.g. WHOI Micro-Modem Firmware (NMEA 0183 on RS-232) (see Interface Guide)

Acoustic Communications are slow

Do not take the previous analogy too literally; some things we are doing here for acoustic communications (hereafter, acomms) are unconventional from the approach of networking on electromagnetic carriers (hereafter, EM networking). The difference is a vast spread in the expected throughput of a standard internet hardware carrier and acoustic communications. For example, an optical fiber can put through greater than 10 Tbps over greater than 100 km, whereas the WHOI acoustic Micro-Modem can (at best) do 5000 bps over several km. This is a difference of thirteen orders of magnitude for the bit-rate distance product!

Efficiency to make messages small is good

Extremely low throughput means that essentially every efficiency in bit packing messages to the smallest size possible is desirable. The traditional approach of layering (e.g. TCP/IP) creates inefficiencies as each layer wraps the message of the higher layer with its own header. See RFC3439 section 3 ("Layering Considered Harmful") for an interesting discussion of this issue <http://tools.ietf.org/html/rfc3439#page-7>. Thus, the "layers" of goby-acomms are more tightly interrelated than TCP/IP, for example. Higher layers depend on lower layers to carry out functions such as error checking and do not replicate this functionality.

Total throughput unrealistic: prioritize data

The second major difference stemming from this bandwidth constraint is that total throughput is often an unrealistic goal. The quality of the acoustic channel varies widely from place to place, and even from hour to hour as changes in the sea affect propagation of sound. This means that it is also difficult to predict what one's throughput will be at any given time. These two considerations manifest themselves in the goby-acomms design as a priority based queueing system for the transport layer. Messages are placed in different queues based on their priority (which is determined by the designer of the message). This means that

In contrast, TCP/IP considers all packets equally. Packets made from a spam email are given the same consideration as a high priority email from the President. This is a tradeoff in efficiency versus simplicity that makes sense for EM networking, but does not for acoustic communications.

Despite all this, simplicity is good

The "law of diminishing returns" means that at some point, if we try to optimize excessively, we will end up making the system more complex without substantial gain. Thus, goby-acomms makes some concessions for the sake of simplicity:

Component model

A relatively simple component model for the goby-acomms libraries showing the interface classes :

goby-acomms-overview.png
For a more detailed model, see the UML models section.

libdccl: Encoding and decoding

Dynamic Compact Control Language (DCCL) provides a structure for defining messages to be sent through an acoustic modem. The messages are configured in XML and are intended to be easily reconfigurable, unlike the original CCL framework used in the REMUS vehicles and others (for information on CCL, see <http://acomms.whoi.edu/ccl/>. DCCL can operate within a CCL network, as the most significant byte (or CCL ID) is 0x20.

DCCL messages are packed based on boundaries determined with knowledge of the XML file. They are not self-describing as this would be prohibitively expensive in terms of data use. Thus, the sender and receiver must have a copy of the same XML file for decoding a given message. Also, each message is defined by an ID that must be unique with a network.

Detailed documentation for goby-acomms: libdccl (Dynamic Compact Control Language).

libqueue: Priority based message queuing

The goby-acomms queuing library (libqueue) interacts with both the application level process that handles decoding (either through libdccl or other CCL codecs) and the modem driver process that talks directly to the modem.

On the application side, libqueue provides the ability for the application level process to push (CCL or DCCL encoded) messages to various queues and receive messages from a remote sender that correspond to messages in the same queue (e.g. you have a queue for STATUS_MESSAGE that you can push messages to you and also receive other STATUS_MESSAGEs on). The push feature is called by the application level process and received messages are signaled to all previous bound slots (see Signal / Slot model for asynchronous events).

On the driver side, libqueue provides the modem driver with data upon request. It chooses the data to send based on dynamic priorities (and several other configuration parameters). It will also pack several messages from the user into a single frame from the modem to fully utilize space (e.g. if the modem frame is 32 bytes and the user's data are in 16 byte DCCL messages, libqueue will pack two user frames for each modem frame). This packing and unpacking is transparent to the application side user. Note, however, that libqueue will not split a user's data into frames (like TCP/IP). If this functionality is desired, it must be provided at the application layer. Acoustic communications are too unpredictable to reliably stitch together frames.

Detailed documentation for goby-acomms: libqueue (Message Priority Queuing).

libmodemdriver: Modem driver

The goby-acomms Modem driver library (libmodemdriver) provides an interface from the rest of goby-acomms to the acoustic modem firmware. While currently the only driver available is for the WHOI Micro-Modem, this library is written in such a way that drivers for any acoustic modem that interfaces over a serial connection and can provide (or provide abstractions for) sending data directly to another modem on the link should be able to be written. Any one who is interested in writing a modem driver for another acoustic modem should get in touch with the goby project <https://launchpad.net/goby> and see Writing a new driver.

Detailed documentation for goby-acomms: libmodemdriver (Driver to interact with modem firmware).

libamac: Medium Access Control (MAC)

The goby-acomms MAC library (libamac) handles access to the shared medium, in our case the acoustic channel. We assume that we have a single (frequency) band for transmission so that if vehicles transmit simultaneously, collisions will occur between messaging. Therefore, we use time division multiple access (TDMA) schemes, or "slotting". Networks with multiple frequency bands will have to employ a different MAC scheme or augment libamac for the frequency division multiple access (FDMA) scenario.

The MAC library provides two basic types of TDMA:

Detailed documentation for goby-acomms: libamac (Medium Access Control).

Software concepts used in goby-acomms

Signal / Slot model for asynchronous events

The layers of goby-acomms use a signal / slot system for asynchronous events such as receipt of an acoustic message. Each signal can be connected (goby::acomms::connect()) to one or more slots, which are functions or member functions matching the signature of the signal. When the signal is emitted, the slots are called in order they were connected. To ensure synchronous behavior and thread-safety throughout goby-acomms, signals are only emitted during a call to a given library's API class do_work method (i.e. goby::acomms::ModemDriverBase::do_work(), goby::acomms::QueueManager::do_work(), goby::acomms::MACManager::do_work()).

For example, if I want to receive data from libqueue, I need to connect to the signal QueueManager::signal_receive. Thus, I need to define a function or class method with the same signature:

void receive_data(const goby::acomms::protobuf::ModemDataTransmission& msg);

At startup, I then connect the signal to the slot:

goby::acomms::connect(&q_manager.signal_receive, &receive_data);

If instead, I was using a member function such as

class MyApplication
{
  public: 
      void receive_data(const goby::acomms::protobuf::ModemDataTransmission& msg);

};

I would call connect (probably in the constructor for MyApplication) passing the pointer to the class:

MyApplication::MyApplication()
{
  goby::acomms::connect(&q_manager.signal_receive, this, &MyApplication::receive_data);
}

The Boost.Signals library is used without modification, so for details see <http://www.boost.org/doc/libs/1_46_0/doc/html/signals.html>. Member function binding is provided by Boost Bind <http://www.boost.org/doc/libs/1_46_0/libs/bind/bind.html>

Google Protocol Buffers

Google Protocol Buffers are used as a convenient way of generating data structures (basic classes with accessors, mutators). They can also be serialized efficiently, though this is not generally used within goby-acomms. Protocol buffers messages are defined in .proto files that have a C-like syntax:

message MyMessage
{
   optional uint32 a = 1;
   required string b = 2;
   repeated double c = 3;
}

The identifier "optional" means a proper MyMessage object may or may not contain that field. "required" means that a proper MyMessage always contains such a field. "repeated" means a MyMessage can contain a vector of this field (0 to n entries). The sequence number "= 1" must be unique for each field and determines the serialized format on the wire. For our purposes it is otherwise insignificant. See <http://code.google.com/apis/protocolbuffers/docs/proto.html> for full details.

The .proto file is pre-compiled into a C++ class that is loosely speaking (see <http://code.google.com/apis/protocolbuffers/docs/reference/cpp-generated.html> for precise details):

class MyMessage : public google::protobuf::Message
{
  public:
    MyMessage ();

    // set
    void set_a(unsigned a);
    void set_b(const std::string& b);
    void add_c(double c);
    
    // get
    unsigned a();
    std::string b();
    double c(int index);
    const RepeatedField<double>& c(); // RepeatedField ~= std::vector

    // has
    bool has_a();
    bool has_b();
    int c_size();

    // clear    
    void clear_a();
    void clear_b();
    void clear_c();

  private:
    unsigned a_;
    std::string b_;
    RepeatedField<double> c_; // RepeatedField ~= std::vector
}

Clearly the .proto representation is more compact and amenable to easy modification. All the Protocol Buffers messages used in goby-acomms are placed in the goby::acomms::protobuf namespace for easy identification. This doxygen documentation does not understand Protocol Buffers language so you will need to look at the source code directly for the .proto (e.g. modem_message.proto).

UML models

Model that describes the static structure of goby-acomms as a whole:

goby-acomms-detailed-overview.png

Model that gives the sequence for sending a message with goby-acomms:

goby-acomms-send-message-sequence.png

Model that shows the commands needed to start and keep goby-acomms running:

goby-acomms-background-sequence.png
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends