Skip to content

TRN Software Developers Guide

Terrain Relative Navigation (TRN) capability is provided through a software library called libtrnav, which provides classes and utilities for TRN processing. Generating TRN inputs is platform-specific, given a range of potential Bathymetry and navigation sources.

The sections below begin by describing the contents and functionality provided by libtrnav, followed by a discussion of integration topics and strategies. Finally, several different existing TRN applications are described.

Contents

libtrnav

The libtrnav library is described in detail below. There are instructions for obtaining libtrnav, and information about the libraries, applications, APIs, components, and data structures comprising libtrnav.

Building libtrnav

Dependencies

  • libnetcdf
  • mframe (MBARI internal repo, git@bitbucket.org:mbari/mframe.git)
  • libpthread
  • libm
  • libcurl (for some versions of NetCDF)
  • libhdf5 (for some versions of NetCDF)
  • librt (some linux builds)

See the Quick Start Guide for detailed instructions on building and installing libtrnav.

Applications

The following command line applications are built as part of libtrnav. In general, each displays use help using -h or --help.

Application Description
trn-server TRN server (TRN instance with TCP/IP interface)
trn-client TrnClient test/diagnostic
trnu-cli TRN UDP client/server test/diagnostic
trnu-svr TRN UDP client/server test (use with trnu-cli)
trn-cli tests trn_cli API; useful as diagnostic application, and to play/replay MB1 files
mcpub general purpose UDP multicast publisher/example (uses stdio sockets)
mcsub general purpose UDP multicast subscriber/diagnostic/LCM receiver (uses stdio sockets)
mmcpub general purpose UDP multicast publisher/example (uses mframe socket wrappers)
mmcsub general purpose UDP multicast subscriber/diagnostic/LCM receiver (uses mframe socket wrappers)
mb1rs MB1 test server (serve MB1 data from files)
mb1log-player MB1 log file player
trnlog-player TRN IO log player
csvlog-player CSV log player
readlog DataLogReader test; read/show TerrainNav.log
writelog DataLogWriter test; write (empty) TerrainNav log file
log-to-matlab output TerrainNav.log (tab/space delimited)
log-to-csv output TerrainNav.log (comma delimited)
otree Binary octree file summary (newer, more options/details)
bodump Binary octree file summary
octree2patches
grd-octree-maker
csv-octree-maker
transoct
test-trnif unit test for trnif; creates interfaces, clients, and connections, logs output
test-tile test tiled map reader; summarize tiled map elements
test-trnlog summarize TRN IO log
test-geocon unit test for GeoCon class (wrapper for geocoordinate conversion, i.e. GCTP, PROJ)
test-trnattr test TrnAttr class
trn-player TRN reference implementation; processes TRN logs and CSV format data.

Libraries

The following libraries are built/installed as part of libtrnav.

Library Description/th> Sources/th> Dependencies/th>
geolib GCTP geocoordinate transformation (lat/lon <> UTM) gctp/source/* none
newmat matrices newmat/* none
qnx utilities ported from QNX AsciiFile.cc
AngleData.cc
CharData.cc
BinaryFile.cc
DataLog.cc
DataLogReader.cc
DataLogWriter.cc
FileData.cc
StringConverter.cc
StringData.cc
TimeP.cc
TimeTag.cc
ShortData.cc
Math.cc
NavUtils.cc
IntegerData.cc
FloatData.cc
DoubleData.cc
DataField.cc
DataFieldFactory.cc
LogFile.cc
-lgeolib
tnav TRN core TerrainNav.cpp
PositionLog.cpp
TerrainNavLog.cpp
TNavConfig.cpp
T NavFilter.cpp
TNavPointMassFilter.cpp
TNavParticleFilter.cpp
TNavBankFilter.cpp
TNavPFLog.cpp
TerrainMapOctree.cpp
PositionLog.cpp
TerrainNavLog.cpp
mapio.cpp
structDefs.cpp
trn_log.cpp
myOutput.cpp
OctreeSupport.cpp
ctree.cpp
OctreeNode.cpp
TerrainMapDEM.cpp
TRNUtils.cpp
matrixArrayCalcs
none
geocon geocoordinate conversion
wrapper (GCTP, libproj)
GeoCon.cpp -lqnx -lgeolib -lproj
trnw TRN Wrappers (C++ code)| trnw.cpp -ltrn -lqnx -lgeolib -lnewmat -lproj -lpthread
netif TRN network interfaces netif.c trnif_proto.c trnif_msg.c none
trnucli TRN UDP client component trnu_cli.c -lstdc++ -lmframe -lpthread -lm
trncli TRN TCP client component trn_cli.c -lgeocon -lqnx -lgeolib -lmframe -lpthread lm
mb1 MB1 message API mb1_msg.cpp none

Scripts

Script Description/th>
opt/examples/trn-player/pkg/trnplayer-mkpkg.sh make MacOS binary app-only tar.gz distribution (beta)
opt/examples/trn-player/pkg/trnplayer-sandbox.sh Make sandboxed libtrnav build for trn-player example (beta)

TRN software architecture

Terrain Relative Navigation (TRN) consists of a set of libraries and tools, collectively referred to as libtrnav, maintained in the repository

https://bitbucket.org/mbari/workspace/projects/TRN

The repository contents are organized as follows:

Directory Contents/th>
build-utils cmake find-package files
config example TRN config files
cygwin not sure who checked this in...
doc Doxygen config file and Makefile
gctp GCTP geo-coordinate conversion library
newmat matrix library
opt optional, application-specific code (may have additional dependencies)
opt/rov TRN w/ LCM front end
opt/dorado Dorado AUV
opt/stanford Stanford user utilities
opt/sentry WHOI Sentry
qnx-utils framework code ported from QNX
terrain-nav TRN core classes
tools utility scripts
trnw TRN C API and applications
utils utility applications

The primary purpose for libtrnav is providing software components to implement TRN particle filters and enable integration with host systems and software.

The essential elements of a TRN application include: configuration, sensor and map inputs, a TRN particle filter (PF), TRN position estimate and uncertainty outputs.

These are illustrated in the diagram below:


Fig: TRN Architecture (embedded)

A TRN instance may be embedded in an application (as shown above), or run as a separate process and operated over a network using a TCP client.


Fig: TRN Architecture (distributed)

libtrnav may be thought of in terms of several conceptual layers, some of which are provided by libtrnav, and application-specific layers implemented by users. Some examples of application-specific code may be found in libtrnav/opt.

The following diagram illustrates TRN architectural elements and some existing implementations.


Fig: TRN Architectural Layers

The TRN core implementation provides C++ and C APIs. Key data structures and APIs are briefly summarized below; the TRN Doxygen documentation is recommended for browsing the source and understanding component relationships and details.

libtrnav APIs

Table: libtrnav API summary

API Description Directory Sources
TRN C++ TRN core classes terrain-nav
qnx-utils
newmat
TRN C| TRN core C wrappers trnw trnw
netif TRN network interfaces trnw netif
trnif_msg
trnif_proto
mb1_msg
TRN clients TRN client interfaces utils TerrainNavClient
TrnClient

libtrnav Component Classes

libtrnav Interfaces

Table: libtrnav interface summary

Interface Description Sources
TerrainNav TRN object API Octree
OctreeNode
OctreeSupport
OctreeTestCode
PositionLog
TNavBankFilter
TNavConfig
TNavExtendKalmanFilter
TNavFilter
TNavPFLog
TNavParticleFilter
TNavPointMassFilter
TNavSigmaPointFilter
TRNUtils
TerrainMap
TerrainMapDEM
TerrainMapOctree
TerrainNav
TerrainNavLog
TrnLog
genFilterDefs
mapio
matrixArrayCalcs
myOutput
particleFilterDefs
structDefs
trn_common
trn_log
qnx-utils
TRN server TRN TCP socket trn_server
trn_log
TNavConfig
TerrainNav
netif netif
trnif_msg
trnif_proto
mb1_msg
R1C3
MB1 server MB1 UDP socket (output) netif
TRNU server TRN TCP socket (output) netif
TRNUM server TRN UDP multicast socket (output) netif
TrnClient TRN client
Extends TerrainNavClient to
enable deferred initialization
preferred for new applications
TrnClient
TerrainNavClient TRN Client TerrainNavClient

libtrnav Data Structures

Table: libtrnav data structure summary

Data Structure Source/th> Description/th>
PoseT terrain-nav/structDefs.h Position Update Input, Position Estimate Output
MeasT terrain-nav/structDefs.h Motion (process) Update Input
SensorT terrain-nav/structDefs.h Bathymetry sensor configuration (geometry, process noise, measurement noise)
CommsT terrain-nav/structDefs.h TRN server IO messages
trnu_pub_t trnw/trn_msg.h TRN UDP output message
mb1_t trnw/mb1_msg.h MB1 format bathymetry output message
trncli_t trnw/trn_cli.h TRN client (C API)
trnmsg_header_t
trnmsg_t
trn_type_t
trn_float_t
trn_init_t
trn_meas_t
trn_triplet_t
trn_box_t
trn_pose_t
trnw/trnif_msg.h TRN network interface message structures (C API)
trnucli_t trnw/trnu_cli.h TRN UDP client
trn_config_t trnw/trnw.h TRN configuration (C API)

Integration

Integration Overview

There are a number of application-specific considerations to be resolved when integrating TRN into a host system:

System Requirements
Processing resources needed for TRN

Input Processing
Sensors, sampling strategies, and protocols for bathymetry, navigation, and attitude sensors. Converting sensor data to TRN input formats.

TRN implementation
TRN implementation selection: embedded, distributed (network), online (real-time), or offline (post-processing).

Outputs
Interpretation and distribution of TRN outputs

Configuration
Handling TRN configuration parameters

These are detailed in the sections that follow.

System Requirements

TRN will perform reasonably well on pentium class hardware with 8 GB of RAM, depending on the working processing load. On the other hand, SBCs like Raspberry PI and Beaglebone with small ARM processors may not be adequate. As a general guideline, it's helpful to run TRN on a well-provisioned host.

libtrnav builds and runs on MacOS, Linux, and Windows (using Cygwin).

As mentioned elsewhere, a particle filter size of N=10000 seems to be a good default (and is currently used in all applications).

The following table summarizes the computing environment for several existing TRN applications.

Application CPU RAM OS Notes
Dorado x86 i5? 32/64-bit PC-104 ? MHz 8 GB Windows XP/Cygwin Reson sonar computer
Sentry x86 i7 64-bit PC-104 ? MHz 8 GB Ubuntu 22.04 Linux WHOI Datapod backseat computer
LRAUV ARM 64-bit 16 GB ? MHz Custom Linux kernel nvidia jetson nano backseat computer
ROV x86 64-bit Apple Macbook Pro 16 GB MacOS
Offline testing/sim x86 64-bit 4 GB RAM Ubuntu 18+ VMWare guest on MacOS host

TRN implementation

libtrnav include a pair of TRN particle filter implementations that may be used to develop applications: embedded and distributed. Selection depends on the application's architecture, host computing platform, and network topology. Some applications combine different implementations for different contexts (e.g. on-line real-time vs off-line post-processing applications).

Embedded

A TRN (TerrainNav) object may be embedded in an application, then configured and managed directly by application code. The TerrainNav class is written in C++, though C bindings are available using the trnw (TRN wrapper) API.

The C++ API has a number of constructors with different configuration information, for example:


// map path
// vehicle spec path (file containing sensor types, geometry)
// filter type: TRN filter implementation ID (typically particle filter)
// map type: Map type ID (typically binary octree)
// directory: TRN log directory

TerrainNav(char *mapName, char *vehicleSpecs,
             const int &filterType, const int &mapType, char *directory);

// This version includes an optional particles file path, which is used to store particle state information

TerrainNav(char* mapName, char* vehicleSpecs, char* particles,
             const int& filterType, const int& mapType, char* directory);

The C API constructor takes a trn_cfg_t structure. The structure members are used to invoke a C++ constructor.

wtnav_t *wtnav_new(trn_cfg_t *cfg);

Applications using an embedded TRN instance include MB-System (mbtrnpp application) and Long Range AUV.

Distributed (TRN server)

TRN also includes an implementation called TRN server (trn-server). TRN server is an application that may be built for Linux, MacOS and Windows (using Cygwin). It includes a TRN instance with a TCP socket interface, enabling clients to perform TRN operations (motion/measurement updates, position estimates) remotely. Each TRN server supports a single client connection.

In this way, distributed applications may be partitioned in a more modular way, with TRN functions hosted in a location, for example, with more compute resources or more convenient network or physical location. It is useful in system architectures with isolated core vehicle functions.

In a distributed implementation, application code uses one of several TRN clients. The client API is similar to the TerrainNav API, with added socket configuration. Clients include TrnClient and TerrainNavClient.

Applications the use a distributed implementation include Dorado AUV and it's simulator.

A variant of distributed implementation uses an embedded TRN instance in conjunction with pub-sub (UDP, UDP multicast) interfaces to distribute TRN output. In this scenario, multiple clients receive TRN outputs, while TRN is managed by the application. The UDP network interface supports TRN filter resets. UDP multicast is unidirectional, and does not support any TRN configuration/control.


Fig: Sentry TRN Architecture (distributed)

Applications using a distributed implementation include WHOI ROV Sentry, and MBARI ROV Ventana. In both cases, MB-System (mbtrnpp) manages the TRN operations, and publishes outputs used by clients to inform navigation and control.

Using TRN Offline

TRN is expected to work in an online (real-time) context, in which TRN outputs inform navigation. For testing and simulation, TRN may also be used in an offline context, where TRN receives input from files or test software rather than sensors. Offline applications (e.g. simulation) may run in discreet time, i.e. faster than real time.

TRN does not have or impose any intrinsic timing dependencies or requirements. The primary consideration in relationship to timing is TRN computational load/delay, which depends on the size of the particle filter and host computing resources (memory, CPU, disk speed).

Input Processing

As it is very application-specific, input processing accounts for the majority of effort required for TRN integration.

Applications may receive TRN input data from sensors (via serial, sockets), files, databases, messaging middleware framework, e.g. LCM, ROS, ZeroMQ, or a combination of these. Each of these have their own formats, timing, and other considerations that depend on the specific input source.

Whatever the source, bathymetry, navigation and attitude inputs must be acquired, potentially processed (e.g. parsing and unit conversion), and formatted into structures appropriate for TRN.

Because it is so specific to context, libtrnav doesn't include a general purpose framework for managing sensor input streams; these are outside the scope of libtrnav, available elsewhere, and the choice is more sensibly to be left to the developer.

However, libtrnav's application-specific code (libtrnav/opt) includes a number of examples with different platforms and architectures.


Fig: TRN IO Examples

In general, TRN input rates are typically lower than sensor sampling rates. In fact, as described in the users guide, over-sampling can correlate inputs, leading to to mis-convergence. Sampling for TRN should ensure adequate independence between measurements; the TRN update rate may depend on vehicle speed, sensor orientation and terrain.

For reference, the following table lists output rates for some sensors used for TRN.

Application bathymetry navigation
Dorado Kongsberg EM2040 200/400 Khz @ 3 Hz
Reson 7125 @ 3 Hz
Reson T50 @ ? Hz
Kearfott INS ? Hz
Sentry Kongsberg EM2040 200/400 Khz @ ? Hz INS @ ? H
LRAUV RDI Pathfinder DVL @ 0.5 Hz AHRS-M2 @ 1.5 H
ROV DVL @ ? Hz
Kongsberg EM2040 @ 3 Hz (LASS)
Sprint INS @ 15? Hz

Input streams may be decimated (sub-sampled) as needed. Sensor data streams are typically at different rates. Bathymetry inputs should correspond to the location and attitude associated with them. If data are buffered, be aware that updates should not use data that is too old, or for which there is significant temporal skew between motion and measurement updates.

As a guideline, TRN inputs should be spaced by at least ~3 m. For example, if a platform is moving at 1 m/s and using a sonar pinging at 3 Hz, there are 3 samples/m; every 9th sample would be ~ 3m apart.

Input data are ultimately converted to the TRN data structures PoseT and MeasT for process and measurement updates, respectively. The C API uses wmeast_t and wposet_t, which are simple C wrappers around PoseT and MeasT objects:

PoseT, MeasT (terrain-nav/structDefs.h)

void TerrainNav::motionUpdate(poseT* incomingNav)
void TerrainNav::measUpdate(measT* incomingMeas, const int& type)

wposet_t, wmeast_t (trnw/trnw.h, .cpp)

void wtnav_motion_update(wtnav_t *self, wposet_t *incomingNav)
void wtnav_meas_update(wtnav_t *self, wmeast_t *incomingMeas, const int type)

poseT

struct poseT {

    double x, y, z;                // North, East, Down position (m)
    double vx, vy, vz, ve;         // Vehicle velocity wrto terrain (m/s) [1]
    double vw_x, vw_y, vw_z;       // Vehicle velocity wrto water (m/s) [1]
    double vn_x, vn_y, vn_z;       // Vehicle velocity wrto an inertial frame (m/s) [1]
    double wx, wy, wz;             // Vehicle angular velocity wrto an inertial frame (rad/s) [1]
    double ax, ay, az;             // Vehicle aceleration wrto an inertial frame (m/s^2) [1]
    double phi, theta, psi;        // 3-2-1 Euler angles relating Body frame to  
                                   // an inertial NED frame (rad).
    double psi_berg, psi_dot_berg; // TRN states

    double time;                   // Time (seconds since 01-01-1970T00:00:00)

    bool dvlValid;                 // DVL measurement valid flag
    bool gpsValid;                 // GPS measurement valid flag
    bool bottomLock;               // DVL bottom lock valid flag

    double covariance[N_COVAR];    // Covariances: XYZ, phi, theta, psi, wy, wz  
                                   // (passively stable in roll) (see above units)

    // [1] coordinates in Body Frame

    poseT();
    poseT& operator=(const poseT& rhs);
    poseT& operator-=(const poseT& rhs);
    poseT& operator+=(const poseT& rhs);

    int   serialize(char *buf, int buflen);
    int unserialize(char *buf, int buflen);
};

The PoseT structure is used for both input and output to TRN.

For input, most applications use a subset of these fields:

 time
 x, y, z
 vx, vy, vz
 phi, theta, psi
 dvlValid
 gpsValid
 bottomLock

vx is strictly required for TRN; vy, vz are optional.

To fully initialize, TRN requires that a motion update be received with vx (along-track velocity) not equal to zero, which indicates forward motion. Until that happens, TRN emits an error message indicating that TRN has not been initialized when measurement updates are received. Once motion has been established, motion and measurement updates may occur in any order.

vx is used for most configurations to indicate motion, and its value is not critical, but must be != 0 in order for TRN to initialize.

In general, TRN does not require velocity to operate, but in some contexts, velocity may be integrated to determine position, in which case it is needed.

For output, a PoseT instance is passed into estimatePose, which populates these fields:

 time            // time
 x,y,z           // position estimate
 covariance      // covariances (uncertainties)
                 // covariance[0] : x/N covariance 
                 // covariance[2] : y/E covariance
                 // covariance[5] : z/D covariance
                 // covariance[1] : XY covariance (?)

The remaining fields are somewhat application-specific, and may be used in some TRN configurations.

measT

The measT structure is used for measurement update (bathymetry) input. In libtrnav, measurements are often referred to as beams; this stems from the fact that TRN bathymetry sensors are historically acoustic instruments, i.e. sonars and DVLs. They could, however, include LIDAR or other sensors. Here, measurement and beams are used interchangeably.

struct measT {
    double time;              // Measurement time (s)
    int dataType;             // Sensor type [1]
    double phi, theta, psi;   // 3-2-1 Euler angles relating Body frame to 
                              // an inertial NED frame (rad).
    double x, y, z;           // North, East, Down position (m)

    double* covariance;       // covariance array [2]
    double* ranges;           // beam ranges [2]
    double* crossTrack;       // beam component perpendiclar to velocity [2]
    double* alongTrack;       // beam component parallel to velocity [2]
    double* altitudes;        // beam component downward [2]
    double* alphas;           // alpha values [2]
    bool* measStatus;         // beam measurement valid flag [2]
    unsigned int ping_number; // measurement number
    int numMeas;              // number of beams
    int* beamNums;            // beam numbers (for sensors w/ variable beams) [2]

    // [1] sensor type enumerations  
    //      1: DVL  
    //      2: Multibeam  
    //      3: Single Beam  
    //      4: Homer Relative Measurement  
    //      5: Imagenex multibeam   
    //      6: Side-looking DVL  
    // [2] array with dimension numMeas

    measT();
    measT(unsigned int nummeas, int datatype);
    ~measT();
    void clean();
    measT& operator=(const measT& rhs);

    int   serialize(char *buf, int buflen);
    int unserialize(char *buf, int buflen);
};  

measT is passed into measUpdate, with these fields populated:

 time
 dataType
 x, y, z
 phi, theta, psi
 ranges
 crossTrack
 alongTrack
 altitudes
 measStatus
 ping_number
 numMeas
 beamNums

Sensor Geometry and Measurements

A bathymetry source with a single beam is sufficient for TRN to produce reliable estimates. While such sensors exist, acoustic sensors with multiple beams are commonly used. In the context of TRN input, each beam is an independent measurement. Each sounding of a sensor with N beams (e.g. DVLs, multibeam sonars) produces N measurements with a common timestamp. Thus the measT structure contains a single timestamp with arrays of beam components. Conversely, a measT structure may contain a single beam. If there were a significant time difference between beams, it would be necessary to timestamp them separately in separte measurement updates. Again, a measurement's time, location and attitude must correspond with it's bathymetry.

The measT dataType member (sensor type) is used to enable TRN to differentiate between different beam geometries.

TRN uses sensor type to select appropriate validation algorithms for certain types.

In the TRN filter base class, TNavFilter, sensor type is used to project measT ranges onto sensor frame components: alongTrack, acrossTrack, altitude. dataType also effects how variances are computed.

For multibeam sensors, TRN uses the measT beam component members (alongTrack, acrossTrack, altitudes). Multibeam sensors are assumed to have coplanar beams, extending radially from the sensors, i.e. a fan.

For DVL and other types, TRN uses the measT ranges member. DVL is assumed to have a radial array of 4 downward looking beams, with azimuth and elevation angles defining the orientaton.


Fig: Sensor Beam Geometry

TRN configuraton parameters describing sensor types and geometry are specified in vehicle specifications and sensor configuration files, further described in the configuration section.

Coordinate Systems

Both poseT and measT use a cartesian coordinate systems, with dimensions in meters. Maps are generally Mercator Projections. Both UTM and local projections have been used. When using UTM, there is significant error when operating in a region far from the origin. Such errors are proportional to the distance from the origin. Using a local Mercator projection enables the origin to be defined in the region of interest, reducing the errors.

Latitude and longitude is the scheme most commonly used by position and navigation sensors. TRN position inputs must use the map's cartesian coordinates (northings and eastings). libtrnav includes the GCTP library for converting lat/lon to UTM; it does not support other projections. Libraries such as libproj may be used, which can support conversions between lat/lon and a wide range of existing and custom projections.

libtrnav provides a class called GeoCon that serves as a wrapper for geocoordinate conversions. It provides a common API that enables conversions with different underlying implementations to be passed into functions using a single type.
GeoCon currently supports GCTP and libproj implementations, but could be extended to support other implementations. GeoCon also has a C API; the C type is wgeocon_t.

The TRN C API (trnw.cpp) functions (for converting MB1 bathymetry records to poseT and measT) demonstrate how GeoCon is used.

int wposet_mb1_to_pose(wposet_t **dest, mb1_t *src, wgeocon_t *geocon)
int wmeast_mb1_to_meas(wmeast_t **dest, mb1_t *src, wgeocon_t *geocon)

Outputs

A TRN instance (or client) obtains the current TRN estimate using the estimatePose method.

# CPP API (TerrainNav.h)
virtual void estimatePose(poseT* estimate, const int &type);

# C API (trnw.h)
void wtnav_estimate_pose(wtnav_t *self, wposet_t *estimate, const int type);

The poseT argument is used to return the TRN update, i.e., the position estimate and covariances, along with measurement time, location, and attitude.

The type argument is an enumerated type to select whether the filter value returned is the MLE (maximum likelihood estimator) or MMSE (minimum mean squared error). As described above, the MLE corresponds tallest peak in the likelihood distribution, and MMSE is the lowest error value. Typically MMSE is used, as MLE may be skewed by multimodal distributions. The two could potentially used in conjunction (with some computational cost) to signal convergence, since they should coincide when TRN is truly converged.

Convergence

The value returned by TerrainNav::lastMeasSuccessful() indicates whether the most recent measurement update resulted in a TRN estimate update; it does not indicate whether TRN is converged or valid.

# CPP API (TerrainNav.h)
virtual inline bool lastMeasSuccessful();
# C API (trnw.h)
wtnav_last_meas_successful(wtnav_t *self);

There is a virtual method that nominally returns a boolean convergence flag:

# CPP API (TerrainNav.h)
bool isConverged();
# C API (trnw.h)
bool wtnav_is_converged_(wtnav_t *self);

However, it may be implemented differently by different TRN particle filter implementations, and is not considered to be a reliable indicator of TRN estimate validity. It is recommended that applications implement convergence criteria that make sense in the use context.

For example, the Dorado mapping AUV evaluates NED covariances over a series of returns. A number of consecutive successful updates with covariances that fall within a specified threshold consitute convergence. Once converged, TRN estimates are considered valid until an measurement falls outside the convergence threshold.

Applications may also implement logic to reset the TRN filter under some circumstances. There are different reset mechanisms that allow a 'global' reset (start fresh using the full spatial particle distribution), or a 'local' reset that redistributes particles using the last valid offset or a specified bounding box. A local reset may be useful when covariances are suspiciously small, possibly indicating that TRN has converged on incorrectly on a local minimum. In terrain with adequate information, TRN should reconverge quickly to a similar value when a local reset is made.

In brief: convergence criteria and requirements are somewhat application-specific, and the application should determine whether to use TRN estimates.

Log Files

TRN generates log files that include a message logs for debugging, and data logs containing input and output.

Log File Format/th> Contents/th>
trn.log.nnnn text TRN message log (segmented, 0000-9999)
TrnIO.log binary TRN data log (multiple record types)
TerrainNav.log binary TRN data log (single record type, legacy)
TNavPFLog.log binary TRN particle state log (a lot of data)

The TrnIO log may contain multiple record types, identified with IDs:

MOTN_IN = motion update input
MEAS_IN = measurement update input
MSE_OUT = MMSE output
MLE_OUT = MLE output

TrnIO record types are dynamically sized, and contain minimal record headers and sync patterns, making them efficient in terms of storage and straightforward to search and parse, even if corrupted.

The TerrainNav log uses a single record type to record (MLE, MMSE) outputs and inputs, which is less efficient; it writes all fields, including arrays (which are statically sized to TRN_MAX_BEAMS), whether they are used or not. Also, record type is ambiguous, and do not contain any sync patterns.

Logs are written to the directory passed to TerrainNav constructor, along with configuraton files used to configure the TRN instance.

Network Output

For TRN applications that require TRN updates to be distributed, e.g. to multiple processes or across networks, libtrnav provides components implementing socket-based TRN IO, collectively referred to as the netif API.


Fig: netif Feature Summary

The following code example shows how a TRN UDP update server is started:


wtnav_t *trn_instance...;

// define a server resource bundle
trnuif_res_t rr_resources={0},*g_trnu_res=&rr_resources;

// log directory, session ID strings
const char *log_dir=."";
const char *session_str="202501010000";
// socket address
const char *host = "192.168.1.1";
int port = 270001;
// disable server heartbeat (< 0)
int hbto = -1;

// create a netif TRN UDP server (trnusvr)
netif_t *svr = netif_new("trnusvr",             // server ID
                         host,                  // socket address
                         port,
                         ST_UDP,                // socket type
                         IFM_REQRES,            // protocol (req/res or pub/sub)
                         hbto,                  // heartbeat config
                         trnif_msg_read_trnu,   // protocol methods
                         trnif_msg_handle_trnu, // defined in netif_proto.c
                         trnif_msg_pub_trnu);

if(NULL!=svr){

    // configure network resource bundle, which contains
    // references needed for server to invoke callbacks

    // TRN instance 
    g_trnu_res->trn = trn_instance;

    // TRN reset callback function 
    g_trnu_res->reset_callback = s_mbtrnpp_trnu_reset_callback;

    // TRN reset using offset callback function
    g_trnu_res->reset_ofs_callback = s_mbtrnpp_trnu_reset_ofs_callback;

    // TRN reset using box callback function
    g_trnu_res->reset_box_callback = s_mbtrnpp_trnu_reset_box_callback;

    netif_set_reqres_res(svr,g_trnu_res);
    netif_show(svr,true,5);

    // initialize server message log
    netif_init_log(svr, "trnusvr", log_dir, session_str);
    mlog_tprintf(svr->mlog_id,"*** trnusvr session start [%s] ***\n", session_str);
    mlog_tprintf(svr->mlog_id,"libnetif v[%s] build[%s]\n",netif_get_version(),netif_get_build());

    // start server
    retval = netif_connect(svr);
} else {
    fprintf(stderr,"%s:%d - ERR allocation\n",__FUNCTION__,__LINE__);
}

// do stuff...

// release trnusvr resources
 netif_destroy(&trnusvr);

Binary Logs using mframe library

Some libtrnav utilities use mframe, a C library maintained in the MBARI git repo

https://bitbucket.org/mbari/mframe

mframe encapsulates commonly used components including

  • files
  • sockets
  • serial ports
  • threads
  • clocks
  • config files
  • debug output writers
  • logs
  • containers
  • utilities

for reuse and reliability, using a simple and consistent API.

The mframe log class, mlog, may be used to create binary or ascii logs.
The example below demonstrates uses mlog to create a TRN update log.


// log handle
mlog_id_t trnu_blog_id = MLOG_ID_INVALID;

// log default configuration
mlog_config_t trnu_blog_conf = {100 * SZ_1M, ML_NOLIMIT, ML_NOLIMIT, ML_OSEG | ML_LIMLEN, ML_FILE, ML_TFMT_ISO1806};

// log path 
char *trnu_blog_path = NULL;

// set the log path
trnu_blog_path = (char *)malloc(512);
sprintf(trnu_blog_path, "%s//%s-%s%s", mbtrn_cfg->trn_log_dir, TRNU_BLOG_NAME,
        s_mbtrnpp_session_str(NULL,0,RF_NONE), MBTRNPP_LOG_EXT);

// create log
trnu_blog_id = mlog_get_instance(trnu_blog_path, &trnu_blog_conf, TRNU_BLOG_NAME);

// print log configuration summary to console
mlog_show(trnu_blog_id, true, 5);

// open the log
mlog_open(trnu_blog_id, flags, mode);


// do stuff...

// TRN update structure (filled in elsewhere)
trn_update_t *pstate...

// log it
trn_pub_blog(pstate, trnu_blog_id);

// when done...

// release log resources
mlog_delete_instance(trnu_blog_id);


// write TRN update to log
trn_pub_blog(trn_update_t *update,
                             mlog_id_t log_id)
{
    int retval=-1;

    if(NULL!=update && log_id!=MLOG_ID_INVALID){

        retval = 0;
        int iobytes = 0;
        double offset_n = update->mse_dat->x - update->pt_dat->x;
        double offset_e = update->mse_dat->y - update->pt_dat->y;
        double offset_z = update->mse_dat->z - update->pt_dat->z;

        // serialize data
        trnu_pub_t pub_data = {
            TRNU_PUB_SYNC,
            {
                {update->pt_dat->time,update->pt_dat->x,update->pt_dat->y,update->pt_dat->z,
                    {update->pt_dat->covariance[0],update->pt_dat->covariance[2],update->pt_dat->covariance[5],update->pt_dat->covariance[1]}
                },
                {update->mle_dat->time,update->mle_dat->x,update->mle_dat->y,update->mle_dat->z,
                    {update->mle_dat->covariance[0],update->mle_dat->covariance[2],update->mle_dat->covariance[5],update->mle_dat->covariance[1]}
                },
                {update->mse_dat->time,update->mse_dat->x,update->mse_dat->y,update->mse_dat->z,
                    {update->mse_dat->covariance[0],update->mse_dat->covariance[2],update->mse_dat->covariance[5],update->mse_dat->covariance[1]}
                },
                {update->mse_dat->time,offset_n,offset_e,offset_z,
                    {update->mse_dat->covariance[0],update->mse_dat->covariance[2],update->mse_dat->covariance[5],update->mse_dat->covariance[1]}
                },
                {use_offset_time,use_offset_n,use_offset_e,use_offset_z,
                    {use_covariance[0],use_covariance[1],use_covariance[2],use_covariance[3]}
                },
            },
            update->reinit_count,
            update->reinit_tlast,
            update->filter_state,
            update->success,
            update->is_converged,
            update->is_valid,
            update->mb1_cycle,
            update->ping_number,
            n_converged_streak,
            n_converged_tot,
            n_unconverged_streak,
            n_unconverged_tot,
            update->mb1_time,
            reinit_time,
            update->update_time
        };

        // write data to log
        if( (iobytes=mlog_write(log_id,(byte *)&pub_data, sizeof(pub_data)))>0){
            retval=iobytes;
        }
    }
    return retval;
}

Configuration

TRN applications need to manage configuration parameters. that may need to be incorporated into the host system configuration mechanism as part of an integration process.

TRN configuration parameters are detailed below; these include configuration files, compile time options, and run-time

Configuration Files

  • files: map, vehicle spec, device spec : optional: particles

TRN constructors require a map file or directory, and a vehicle config file. The vehicle config file references one or more sensor config files. An optional particles file may also be used.

Maps

A TRN map may be either a binary octree (.bo) or digital elevation map (DEM, .grd format). A map may be provided as a path to a single file or a directory containing a set of map map tiles (sub-maps) and a key file describing the tile boundaries. Since maps can be large, tiled maps limit run-time memory use; switching between tiles incurs modest run-time delays. The tiled map key file (tiles.csv) is a CSV file that includes a header (specifies number of tiles) and a line for each tile that includes filename, origin easting, origin northing:

TileName, Easting, Northing, <number of tiles>
<file>, <origin Easting>, <origin northing>
...

Example: tiles.csv

TileName , Easting , Northing , 9
PortugueseLedgeTile1.bo , 594250 , 4061250
PortugueseLedgeTile2.bo , 594750 , 4061250
PortugueseLedgeTile3.bo , 595250 , 4061250
PortugueseLedgeTile4.bo , 594250 , 4061750
PortugueseLedgeTile5.bo , 594750 , 4061750
PortugueseLedgeTile6.bo , 595250 , 4061750
PortugueseLedgeTile7.bo , 594250 , 4062250
PortugueseLedgeTile8.bo , 594750 , 4062250
PortugueseLedgeTile9.bo , 595250 , 4062250

libtrnav has a few utilities for working with map files:

Utility Description/th> Use Notes/th>
csv-octree-maker generates binary octree map from UTM .csv (DEM) map csv-octree-maker <file> (w/o .grd extension)
additional use notes in source header
grd-octree-maker generates binary octree map from UTM .grd (DEM) ma grd-octree-maker <file> (w/o .grd extension)
additional use notes in source header
test-tile checks tiled map directory test-tile <directory>
transoct translate .bo origin transoct file (w/o .grd extension)
Writes output to <file>Serm.bo
Note: contains hard-coded constants
octree2patches
otree traverse binary tree and show summary
optionally print tree as text
otree [options] <file>

Options:
-f : specify map file
-p : print tree to console (a LOT of text)
-c : compare to Octree.Print tree stats
-v : enable verbose output
-h : print this help message
bodump like otree, but less capable, and unfortunately named bodump <file>

Vehicle Spec File

The vehicle specification file is a text file with a list of sensor config files, mounting geometry, and a process noise parameter. It's format is rigid; it uses a naming convention \<NAME>_spec.cfg, and its contents are arranged as follows:

# comments...

NAME:<config file name (w/o _spec.cfg)>
NUMSENSORS:<number of sensor entries (integer)>
DRIFTRATE:<process noise figure (floating point)>

SENSORNAME:<sensor spec file name (w/o _spec.cfg)>
DR:<phi>,<theta>,<psi>
DT:<x>,<y>,<z>
...

Example: Vehicle Spec File

# Vehicle specification sheet
#----------------------------
NAME:mappingAUV
NUMSENSORS:3
DRIFTRATE:1.5

SENSORNAME:rdiDVL
DR:0,0,0
DT:0,0,0

SENSORNAME:resonSeabat8101
DR:0,0,0
DT:0,0,0

SENSORNAME:imagenexdeltat
DR:0,0,0
DT:0,0,0

The parameters are described as follows:

Name Description/th>
NAME Configuration name (matches file name, w/o _spec.cfg suffix)
NUMSENSORS Number of sensor entries to follow
DRIFTRATE Somewhat of a misnomer, represents TRN process noise, typically 1.5/2
depends on terrain, instruments, etc.
SENSORNAME name of sensor spec file (w/o _spec.cfg suffix)
DR sensor orientation, NED Euler angles in vehicle frame
DT sensor mounting location, distance from vehicle center, m

Sensor Specification File

The sensor specification files, referenced by the vehicle spec file, are text files with a sensor configuration information. It's format is rigid; it uses a naming convention \<NAME>_spec.cfg, and their contents are arranged as follows:

# comments...
NAME:<sensor name (matches file, w/o _spec.cfg suffix)>
TYPE: <sensor type (integer)>
NUMBEAMS: <number of beams>
PERCENTRANGEERROR: <range error, %>
BEAMWIDTH: <beam width (deprecated)>

# optional fields, type-dependent
PITCH ANGLES:<comma separated list of pitch angles>
DELTA_PITCH:<pitch angle increment>
INIT_YAW ANGLE:<comma separated list of yaw angles>
DELTA_YAW: <yaw angle increment>
INIT_PITCH ANGLES|starting pitch angle(s) in sensor frame, decimal degrees|
Name Description
NAME Configuration name (matches file name, w/o _spec.cfg suffix)
TYPE Sensor type enumeration value
1: DVL
2: Multibeam
3: Single Beam
4: Homer Relative Measurement
5: Imagenex multibeam
6: Side-looking DVL
NUMBEAMS number of beams
PERCENTRANGEERROR estimated range error (% of range)
BEAMWIDTH width of beams, m (deprecated)
PITCH ANGLES pitch angles in sensor frame, decimal degrees
used for non-multibeam types
DELTA_PITCH pitch angle increment, decimal degrees
used for non-multibeam types
YAW ANGLES yaw angles in sensor frame, decimal degrees
used for non-multibeam type
DELTA_YAW yaw angle increment, decimal degrees
used for non-multibeam types

Multibeam sensors are assumed to extend radially from the sensor origin. Non-multibeam sensors are assumed to be arranged in a radial array around the sensor origin, with azimuth YAW ANGLE (relative to E) separated by DELTA_YAW. The beams are pitched downward at PITCH ANGLE, optionally incrementing by DELTA_PITCH.

Example: multibeam sensor spec file

# Sensor specification sheet
#----------------------------
NAME: resonSeabat8101
TYPE: 2
NUMBEAMS: 20
PERCENTRANGEERROR: 4
BEAMWIDTH: 1.5

PITCH ANGLES: 0
DELTA_PITCH: 0
INIT_YAW ANGLE: 0
DELTA_YAW: 0

Example: DVL sensor spec file

# Sensor specification sheet
#----------------------------
NAME: rdiDVL
TYPE: 1
NUMBEAMS: 4
PERCENTRANGEERROR: 4
BEAMWIDTH: 3.9

PITCH ANGLES:30, 30, 30, 30
YAW ANGLES: -45, 135, 45, -135

#----------------------------------------------------------------
# Sensor beam configuration diagram:
#
# Beam numbering convention, looking down on Dvl from above.  
# x and y are the vehicle axes. x points to port, y to starboard.               
#                                                                        
#                  x                                                     
#                  ^                                                          
#                  |                                                     
#                1 | 3                                                   
#                  |------> y                                            
#                4   2                                                   
#                                                                        
# Each beam is spaced 45 degrees from the horizontal/vertical.            
# The inclination angle is 30 degrees.        

Compile Time Configuration

In addition to the build options that are described above, there are a few macros that are not exposed as configuration options, but that it may be good to be aware of:

macro file/th> description/th>
TRN_MAX_BEAMS terrain-nav/structDefs.h Maximum number of beams for a sensor
N_COVAR terrain-nav/structDefs.h covariance matrix size for TRN estimates (number of TRN states?)
TRN_MSG_SIZE terrain-nav/structDefs.h Maximum TRN server (commsT) message length (bytes)
MAP_NOISE_MULTIPLIER terrain-nav/genFilterDefs.h Multiplier on map noise in particle filter
MOTION_NOISE_MULTIPLIER terrain-nav/genFilterDefs.h Multiplier on motion noise std in particle filter
X_STDDEV_INIT terrain-nav/genFilterDefs.h X stdev initial value for TRN resets
Y_STDDEV_INIT terrain-nav/genFilterDefs.h Y stdev initial value for TRN resets
Z_STDDEV_INIT terrain-nav/genFilterDefs.h Z stdev initial value for TRN resets
PHI_STDDEV_INIT terrain-nav/genFilterDefs.h phi (roll) stdev initial value for TRN resets
THETA_STDDEV_INIT terrain-nav/genFilterDefs.h theta (pitch) stdev initial value for TRN resets|
PSI_STDDEV_INIT terrain-nav/genFilterDefs.h psi (yaw) stdev initial value for TRN resets
MAX_PARTICLES terrain-nav/genFilterDefs.h number or particles in particle filter

Run-time Configuration

As described earlier, TRN constructors include arguments specifying several files. Since the path may differ for a remote TRN instance, only the file names are specified. In other words, a TRN client and server are expected to refer to the same file name, but it may be in different places on their respective hosts. The file paths are specified using environment variables; these should always be set for TRN applications:

Environment Variable Description
TRN_LOGFILES path to TRN log output
TRN_DATAFILES path to TRN configuration files (vehicle spec, platform spec
TRN_MAPFILES path to TRN map files

There are a few uncommonly used environment variables:

Environment Variable Description
TRN_LOGDIR Used by TrnClient to set directory used for logs and saving config files
TRN_GROUP Application-specific; UDP multicast group used but trnxpp (/opt/rov)
TLP_SESSION Used by csvlog-player, trnlog-player, mb1log-player to set session name used in output
LIBGCTP Used by gctp/test/test_proj; directory containing geolib.a

For applications that use LCM, these environment variables may also be used:

Environment Variable Description
LCM_DEFAULT_URL LCM URL (UDP multicast group/port, TTL parameter)
CLASSPATH Java classpath, needed by lcm-spy (for lcm.jar, application-specific JAR files)

Developer Integration Tools

libtrnav does not directly provide libraries or frameworks for integration, i.e. configuration, input handling and logging.

Existing applications may provide helpful examples and resources, some of which are highlighted below:

Application Of Interest Location About
rov LCM input opt/rov See:
trnxpp.hpp
pcf_utils.hpp
rov configuration opt/rov Application configuration file parsing
See:
trnxpp_cfg.hpp
rov input source abstraction opt/rov Scheme to present hetergeneous input sources using common interfaces
See:
trn_lcm_input.hpp
*_provider_IF.hpp
*_input.hpp
rov IO handling opt/rov TRN clients, servers, file IO, LCM TRN output
See:
trnxpp.hpp trnxpp_ctx.hpp
rov logging, per-module debugging opt/rov See:
log_utils.hpp
trn_debug.hpp
rov TRN IO utilities opt/rov TRN format conversion, formatted output, matrix calcs, beam component decomposition
See:
trnx_utils
sentry TrnClient example opt/sentry Stand-alone TRN UDP client example (w/ test server)
See:
README-trndev-build.md
trnu-svr
trnu-cli
lrauv LCM input opt/lrauv
MB-System:mbtrnpp