Goby Underwater Autonomy Project
Series: 1.1, revision: 163, released on 2013-02-06 14:23:27 -0500
|
Table of Contents for goby-acomms: Overview of Acoustic Communications Libraries.
To get started using the goby-acomms libraries as quickly as possible:
Please visit <https://answers.launchpad.net/goby> with any questions.
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) |
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!
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.
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.
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:
A relatively simple component model for the goby-acomms libraries showing the interface classes :
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).
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).
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).
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).
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 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).
Model that describes the static structure of goby-acomms as a whole:
Model that gives the sequence for sending a message with goby-acomms:
Model that shows the commands needed to start and keep goby-acomms running: