Integration API Sonar 3D-15
Introduction
The Water Linked Sonar 3D-15 provides real-time 3D views of underwater environments using a low-bandwidth Range Image Protocol (RIP1). This protocol efficiently transmits data such as 3D points or grayscale bitmaps over UDP multicast, enabling live visualization, analysis, or archival for later use.
A Python example implementation of the Sonar API is available on github.
Range Image Protocol (RIP1)
RIP1 is a compact format for range and bitmap images, designed for <10 Mbit bandwidth. Each packet:
- Fits within a single UDP packet (max 65,507 bytes).
- Starts with the 4-byte ID
"RIP1". - Contains a length field, a serialized Protobuf payload, and a CRC-32.
Decoding Steps:
- Verify the
RIP1ID. - Check the packet length and CRC-32.
- Decode into
waterlinked.rip1.Packet. - Use
.type_urlto identify the contained message. - Decode
.valueinto that message type. - Interpret fields per the
.protoFile.
Network
By default, the Sonar 3D-15 uses UDP Multicast (224.0.0.96:4747), so any device on the local network can receive data without knowing the sonar’s IP.
Image Sizes and Update Rates
| Mode | Resolution (W×H) | FOV (H×V) | Rate |
|---|---|---|---|
| Low Frequency | 256 × 64 | 90° × 40° | 5 Hz |
| High Frequency | 256 × 64 | 40° × 40° | 20 Hz |
Message Types
RIP1 supports several Protobuf-encoded messages, including:
BitmapImageGreyscale8
- 8-bit grayscale; each pixel = signal strength or shaded depth.
typeenum differentiates signal strength vs. shaded.
RangeImage
- Each pixel represents distance (radius) to the strongest reflection.
0= no valid data.radius = pixelValue * imagePixelScale.
Coordinate and Image Conventions
Axes (right-handed):
- x: forward
- y: right
- z: downward
Image:
![]()
- Horizontal FOV (
fovH) spans width. - Vertical FOV (
fovV) spans height. (px, py)maps to(yaw, pitch):yaw = (px / (width - 1)) * fovH - (fovH / 2) pitch = (py / (height - 1)) * fovV - (fovV / 2)
Example: Converting Range Image to 3D:
// Convert pixelValue to radius in meters
if (pixelValue == 0) {
return Undefined; // no valid data
}
radius = pixelValue * imagePixelScale;
// Convert (yaw, pitch) from degrees to radians
yaw = deg2rad((px / (width - 1)) * fovH - (fovH / 2));
pitch = deg2rad((py / (height - 1)) * fovV - (fovV / 2));
// Cartesian conversion (x forward, y right, z down)
x = radius * cos(pitch) * cos(yaw);
y = radius * cos(pitch) * sin(yaw);
z = -radius * sin(pitch); // z is downward
.proto File (Excerpt)
// Water Linked Sonar 3D-15 protocol
syntax = "proto3";
package waterlinked.sonar.protocol;
import "google/protobuf/timestamp.proto";
import "google/protobuf/any.proto";
// Packet is the top-level message that is sent over the wire.
message Packet {
google.protobuf.Any msg = 1; // Use .type_url to deserialize .value
// into one of the messages defined in this proto file
}
message Header {
google.protobuf.Timestamp timestamp = 1;
// Sequence id is a monotonically increasing number that identifies
// each shot of the sonar. It can be used to detect missing shots or
// to relate messages of different types to each other. The value
// wraps around to 0 after reaching the maximum value.
uint32 sequence_id = 2;
}
enum BitmapImageType {
// Bitmap image showing for each x,y pixel the strength of the
// strongest reflection
SIGNAL_STRENGTH_IMAGE = 0;
// Bitmap image showing a shaded representation of the depth in the
// range image. The light source is behind and above the observer.
// (This is experimental and may be removed in the future)
SHADED_IMAGE = 1;
}
// BitmapImageGreyscale8 can be shown to user without further processing.
// Uncompressed, 8-bit color depth, greyscale.
message BitmapImageGreyscale8 {
Header header = 1;
float speed_of_sound = 2; // Configured speed of sound in water in m/s
float range = 3; // Configured range in meters
uint32 frequency = 4; // Configured imaging frequency in Hz
BitmapImageType type = 5; // Identifier for what is shown in the image
uint32 width = 6;
uint32 height = 7;
float fov_horizontal = 8;
float fov_vertical = 9;
// image pixel data is organized in rows of pixels.Each row is
// 'width' wide, and there are 'height' rows. Each pixel is an 8-bit
// value that represents the intensity of the pixel, from 0 to 255
bytes image_pixel_data = 10;
}
// RangeImage measures the distance to the strongest reflection
// for each pixel.
message RangeImage {
Header header = 1;
float speed_of_sound = 2; // Configured speed of sound in water in m/s
float range = 3; // Configured range in meters
uint32 frequency = 4; // Configured imaging frequency in Hz.
uint32 width = 5;
uint32 height = 6;
float fov_horizontal = 7; // In degrees
float fov_vertical = 8; // In degrees
float image_pixel_scale = 9;
// image_pixel_data is organized in rows of pixels. Each row is
// 'width' wide, and there are 'height' rows.
// Each pixel is a 16-bit value that must be multiplied by
// image_pixel_scale to obtain the distance in meters from the sonar
// to the strongest reflection in that direction.
repeated uint32 image_pixel_data = 10;
}
Compatibility Notes
- Additional message types/fields may be introduced.
- Decoders should ignore unrecognized messages.
- Major breaking changes will involve a new protocol identifier.
Thank you for using the Water Linked Sonar 3D-15!
Refer to the full protocol specification and .proto file shown above for more information.