Skip to content

apl-ocean-engineering/liboculus

Repository files navigation

liboculus

Build Status

(No, sadly, not that kind of Oculus)

This library contains code for:

  • Communicating with a Blueprint Subsea Oculus imaging sonar over its ethernet interface.
  • Requesting that the sonar start pinging.
  • Decoding and parsing fields from the resulting ping messages from the sonar.
  • Loading and parsing sonar data recorded as either:
    • Raw streams of binary packets.
    • Note: This repo includes scaffold code for reading .oculus files saved from the Blueprint GUI, but that format is proprietary and undocumented we cannot parse .oculus files.

The library contains no special provisions for saving sonar data, but it's straightforward to write packets as a raw binary stream (which the library can read) -- see tools/oculus_client.cpp for an example.


Build/Installation

This is a hybrid repository:

  • We primarily build using catkin, though there are no ROS dependencies in the code. We hope the code is still useful for others looking to talk to the Oculus.

  • Historically, the repo has also supported the fips C++ dependency management tool. To build with fips: ./fips build

The primary dependency is on g3log.

  • If using catkin, there are two options:
    • clone g3log_ros into your workspace's src/ directory
    • use the provided liboculus.rosinstall file: cd <catkin_ws>/src; vcs import --input liboculus/liboculus.repos
  • It will be handled automagically if using fips.

The (optional) test suite also requires Googletest and the (also optional) binary oc_client requires CLI11, both of which are also handled by fips.

Internally, the ethernet interface uses Boost::asio.


oc_client binary

The repo contains one binary, oc_client which can read data either from a real Oculus sonar via ethernet, or from a file containing raw Ethernet data.

As noted above, it cannot read files saved by the proprietary Oculus GUI as that is based on a proprietary data format (independent from the SimplePingResult format used in this code).

Here's the help string for oc_client:

Simple Oculus Sonar app
Usage: oc_client [OPTIONS]

Options:
  -h,--help                   Print this help message and exit
  -v,--verbose                Additional output (use -vv for even more!)
  --ip TEXT                   IP address of sonar or "auto" to automatically detect.
  -o,--output TEXT            Filename to save sonar data to.
  -i,--input TEXT             Filename to read sonar data from.
  -n,--frames INT             Stop after (n) frames.

The --output format works for both live data, and datafiles specified with --input. The generated files are raw binary streams of sonar packets, and can be opened by oc_client.

Library Design

See oc_client as a sample non-ROS client. A typical client will have instances of two interface classes. Both use Boost::Asio for network IO and must be given an boost::asio::io_context on construction.

  • DataRx receives packets from the sonar, calling a callback function for each ping.
  • StatusRx monitors the UDP broadcast-based protocol used to autodetect sonars on the network. On receiving a good sonar status, it calls a callback.

The client must implement callbacks that will handle data from the sonar (for example) -- independent callbacks must be defined for the Oculus V1 and V2 packets. DataRx also has a callback on successful connection with a sonar which can be used to send a configuration to the sonar (this will start the sonar pinging).

This library makes liberal use of overlay classes in order to provide zero-copy accessor functions into the raw data chunks received from the oculus. These classes overlay the struct hierarchy defined in thirdparty/Oculus/Oculus.h, making it possible to directly cast between the types depending on which accessors you want to use:

  • OculusSimplePingResult carries all image data from the oculus.
  • Its first field is the OculusSimpleFireMessage that triggered data collection
  • In turn, the first field of the OculusSimpleFireMessage is an OculusMessageHeader

So, in our code:

  • MessageHeader (SimplePingResult.h)
    • Overlays OculusMessageHeader (there exist an accessor function that returns the original Oculus type)
    • However, it contains a buffer that will accept the full message payload, which is then used by other classes that provide accessors.
  • SimplePingResult (SimplePingResult.{h,cpp}) overlays the OculusSimplePingResult.
    • SimplePingResult subclasses MessageHeader
    • Overlays both OculusSimpleFireMessage and OculusSimplePingResult (there are accessor functions that cast it to either)
    • It has instances of two other overlay classes, BearingData and ImageData.
  • BearingData (BearingData.h) TODO: I'm still confused by how this one actually gets the bearings.
  • ImageData (ImageData.h) overlays the buffer in a SimplePingResult, using OculusSimpleFireMessage.imageOffset to index into the buffer at the correct spot.

Other files/classes:

  • DataTypes.h: Utility conversions for enums defined in Oculus.h

  • StatusRx: Connects to the fixed status port; stuffs messages into a SonarStatus and calls SonarClient's callback with the SonarStatus.

  • SonarStatus: Wrapper around OculusStatusMsg. Only used to dump it to LOG(DEBUG), so I'd like to see it disappear in favor of a log_status helper function.

  • IoServiceThread: thin wrapper which runs a boost::asio::io_context within a thread. Used by both StatusRx and DataRx


Related Packages


License

This code is released under the BSD 3-clause license.

This repository contains one file provided by Blueprint as part of their free "Oculus Viewer" sample application (thirdparty/Oculus/Oculus.h), which describes their protocol and data formats. This file is distributed under GPLv3.