Echogram Visualisation and Analysis
Easily import multi-frequency acoustic data stored in 'HAC' files (see http://biblio.uqar.ca/archives/30005500.pdf for more information on the format), and produce echogram visualisations with predefined or customized color palettes. It is also possible to merge consecutive echograms; mask or delete unwanted echogram areas; model and subtract background noise; and more important, develop, test and interpret different combinations of frequencies in order to perform acoustic filtering of the echogram's data.
Version: 1.0.1 | Author: Héctor Villalobos | License: GPL-3
The echogram package provides tools for importing, visualising, and analysing multi-frequency acoustic data from scientific echosounders (Simrad EK60). It supports two input formats:
- HAC files — the ICES standard data exchange format (HAC specification)
- RAW files — the native binary format produced by Simrad EK60 echosounders
The package produces echogram visualisations (colour-coded 2D images of acoustic backscatter as a function of depth and ping/time/distance), and enables multi-frequency analysis such as acoustic filtering by combining data from different frequencies.
Almost every function in the package operates on or produces an object of class "echogram", which is a list with three components:
| Component | Type | Description |
|---|---|---|
depth |
numeric vector (length p) | Mean depth (m) of each sample row |
Sv |
matrix (p × k) | Mean volume backscattering strength (Sv, in dB); has an "frequency" attribute (e.g. "38 kHz") |
pings |
data.frame (k rows) | Per-ping metadata: pingTime, detBottom, speed, cumdist |
The package is organised into four functional layers, shown below from bottom (low-level I/O) to top (user-facing analysis).
graph TD
subgraph "Layer 1: Low-level Binary I/O"
A["read.EK_raw"]
B["get_dgIdx"]
C["dgTime"]
D["xcvrConf"]
end
subgraph "Layer 2: Datagram Parsers"
E["get_CON0"]
F["get_NME0"]
G["get_RAW0"]
H["parse.nmea"]
end
subgraph "Layer 3: File Readers → echogram objects"
I["read.EK60_raw"]
J["read.EK_bot"]
K["read.EK_idx"]
L["read.echogram"]
M["ek2echogram"]
N["sampleRange"]
O["convertPower"]
P["convertAngles"]
end
subgraph "Layer 4: Visualisation & Analysis"
Q["echogram"]
R["palette.echogram"]
S["join.echogram"]
T["match.echogram"]
U["add.echogram"]
V["mask.echogram"]
W["trim.echogram"]
X["sample.echogram"]
Y["noise.echogram"]
end
subgraph "HAC helpers"
L1["position.hac"]
L2["navigation.hac"]
L3["bottom.hac"]
L4["mergeSvmat"]
end
A --> B
A --> E
A --> F
A --> G
A --> J
A --> K
B --> E
B --> F
B --> G
B --> J
B --> K
C --> E
C --> F
C --> G
C --> J
C --> K
D --> E
E --> I
F --> I
G --> I
H --> I
I --> M
N --> M
N --> O
O --> M
P -.-> M
L1 --> L
L2 --> L
L3 --> L
L4 --> L
L4 --> S
R --> Q
W --> Q
Q --> X
T --> U
V --> U
Reads an entire EK60/EK80 file as a raw byte vector. Foundation for all RAW-file processing. Not intended to be called directly.
Scans the raw byte vector and builds a datagram index table — a data.frame recording where each datagram (CON0, NME0, RAW0, BOT0, IDX0, etc.) starts, ends, and its length. This index is the key for all subsequent datagram extraction.
Extracts the datagram timestamp from 8 raw bytes at a given offset. Converts the Windows FILETIME (100-nanosecond intervals since 1601-01-01) to POSIXct in UTC.
Parses 320 bytes of transceiver configuration data: channel ID, beam type, frequency, gain, equivalent beam angle (EBA), beamwidths, angle sensitivities/offsets, position, direction, and calibration tables (pulse lengths, gains, Sa corrections). Called by get_CON0 once per transceiver.
Extracts the CON0 configuration datagram: header info (survey name, transect name, sounder name, software version, transceiver count) and transceiver configuration for each channel.
- Calls:
read.EK_raw→get_dgIdx→dgTime,xcvrConf
Extracts all NME0 datagrams (NMEA sentences) — GPS strings with timestamps. Returns a data.frame of dgTime and raw NMEA text strings.
- Calls:
read.EK_raw→get_dgIdx→dgTime
Extracts all RAW0 datagrams containing the actual acoustic data:
- A 3D
sampleDataarray [pings × 21 fields × transceivers] with per-ping metadata (channel, frequency, power, pulse length, sound velocity, absorption, heave, roll, pitch, etc.) - A 3D
Prarray [depth samples × pings × transceivers] of received power - Optionally a 4D
Angarray [depth samples × pings × transceivers × 2] of electrical angles (alongship, athwartship) - Calls:
read.EK_raw→get_dgIdx→dgTime
Parses NMEA text strings from get_NME0 output. Automatically selects the best GPS sentence type (GPRMC → GPGGA → INGGA → GPGLL). Extracts GPS time, longitude, latitude, and optionally bearing and speed from VTG sentences.
Main entry point for EK60 RAW files. Orchestrates the full import pipeline:
read.EK_raw→ raw bytesget_CON0→ configurationget_NME0→ NMEA sentences (optionally parsed viaparse.nmea)get_RAW0→ acoustic sample data + power + angles
Returns a list: $configuration, $nmea, $pings.
Reads Simrad BOT files (bottom detection). Extracts ping times and detected bottom depths for each transceiver from BOT0 datagrams.
- Calls:
read.EK_raw→get_dgIdx→dgTime
Reads Simrad IDX files (ping index). Extracts ping number, vessel distance, latitude, longitude, and file offset from IDX0 datagrams.
- Calls:
read.EK_raw→get_dgIdx→dgTime
Main entry point for HAC files. Imports data from an HAC file using the readHAC package:
- Identifies available tuple types (echosounder, channel, ping)
- Extracts Sv matrix from ping tuples, handling varying matrix sizes via
mergeSvmat - Calculates sample depth vector from time sample interval and sound speed
- Calls
bottom.hac,position.hac,navigation.hacfor per-ping metadata - Matches navigation speed to pings and computes cumulative distance
Returns an object of class "echogram".
Converts imported EK60 data to class "echogram". Bridge between the RAW pipeline and the echogram analysis functions:
- Calls
sampleRangefor depth vector - Calls
convertPowerfor Sv (or TS) matrix - Assembles the result with ping times
Accepts optional environment (sound velocity, absorption) and calibration (gain, Sa correction) overrides.
Calculates the depth vector from EK60 data using sound speed and sample interval:
Converts received power (Pr) to Sv or TS using the sonar equation:
Uses transceiver parameters (gain, frequency, EBA) and per-ping metadata (power, pulse length, sound speed, absorption).
Converts electrical angles (alongship/athwartship) to mechanical angles using transceiver sensitivity and offset parameters.
Imports GPS positions from the Position tuple (type 20) in an HAC file: GPS time, CPU time, longitude, latitude.
Computes bearing (rhumb-line), distance (Vincenty ellipsoid), time difference, and speed between consecutive GPS fixes. Uses the geosphere package.
Extracts detected bottom range and ping times from the ping tuple in an HAC file. Optionally produces a plot.
Merges two Sv matrices with unequal row counts by padding the shorter one with NA rows, then column-binding. Used internally by read.echogram and join.echogram.
The primary plotting function. Produces a colour-coded echogram image from an "echogram" object:
- Configurable Sv threshold (
Svthr), max (Svmax), and colour separation (col.sep) - Multiple colour schemes:
"parula"(default, viapals),"EK500","echov", custom vectors, or functions - X-axis reference: ping number, nautical miles, or time
- Optional detected bottom line overlay
- Optional colour bar (uses an embedded
imageScalefunction fromsinkr) - Calls
trim.echograminternally whendepth.max/ping.ini/ping.maxare specified
Designs colour palettes for echograms. Can use three built-in schemes or custom colour vectors, interpolating via colorRampPalette. Returns a list of palette (colours) and breaks (Sv boundaries). Optionally displays a visual preview.
Crops an echogram vertically (by maximum depth) and/or horizontally (by ping range) by subsetting the Sv matrix, depth vector, and pings data.frame.
Merges two echograms of the same frequency end-to-end (column-wise). Checks that frequencies match, uses mergeSvmat to handle different depth ranges, and recalculates cumulative distance.
Synchronises two echograms from different frequencies by matching ping times. Removes non-matching and duplicated pings so that both echograms have identical dimensions — a prerequisite for add.echogram.
Adds or subtracts Sv matrices from two frequencies, in either the dB or linear domain. This is the core operation for multi-frequency acoustic filtering. Requires matched echograms (use match.echogram first) and masked echograms (use mask.echogram first to remove bottom echoes).
Creates and applies a mask to blank out unwanted regions: surface layer (by surf.off offset in metres) and/or below-bottom region (by bott.off offset relative to detected bottom). The mask is a matrix of 1's and NA's.
Interactive pixel sampling — click on an echogram to extract Sv values, ping times, and depths at specific locations. Supports coordinate transfer between frequencies (sample points on one frequency, then retrieve corresponding values from another).
Models background ambient noise using the equation from De Robertis & Higginbottom (2007):
Fits the noise level at 1 m depth (dB1m) and absorption coefficient (alpha) to observed Sv data. Can output a noise echogram for subsequent subtraction.
# Import
hacfile <- system.file("extdata", "D20150510-T202500.hac", package = "echogram")
echo.038 <- read.echogram(hacfile, channel = 1)
# Visualise
echogram(echo.038, Svthr = -70, scheme = "EK500")# Import raw
ek <- read.EK60_raw("file.raw", parseNMEA = TRUE, angles = TRUE)
# Convert to echogram
eco <- ek2echogram(ek, frequency = 1, data = "Sv")
echogram(eco)# Import two frequencies
echo.038 <- read.echogram(hacfile, channel = 1)
echo.120 <- read.echogram(hacfile, channel = 2)
# Synchronise pings
tmp <- match.echogram(echo.038, echo.120)
echo.038 <- tmp$echogram1
echo.120 <- tmp$echogram2
# Mask surface and bottom
echo.038m <- mask.echogram(echo.038, surf.off = 2, bott.off = 0.2)
echo.120m <- mask.echogram(echo.120, surf.off = 2, bott.off = 0.2)
# Subtract: 120 kHz − 38 kHz (positive = krill-like, negative = fish-like)
echo.diff <- add.echogram(echo.120m, echo.038m, "minus", "dB")
echogram(echo.diff, Svthr = floor(min(echo.diff$Sv, na.rm = TRUE)))data("echo.noise")
noise <- noise.echogram(echo.noise, ping = 2, dB1m = -131, alpha = 0.03550554, out = TRUE)
# subtract noise from real echogram using add.echogram(..., "minus", "linear")| Package | Purpose |
|---|---|
readHAC |
Parse HAC file format (tuples) |
geosphere |
Geodesic distance and rhumb-line bearing calculations |
pals |
Default parula colour palette |
data.table |
rbindlist for transceiver config assembly |
plyr |
ldply for NMEA parsing |
| File | Description |
|---|---|
data/echo.noise.rda |
Example echogram (120 kHz, deep water, no scatterers) for noise estimation |
inst/extdata/*.hac |
Demo HAC files at 38 and 120 kHz |
inst/extdata/*.bot |
Demo bottom-detection file |
inst/extdata/*.idx |
Demo ping-index file |