Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c5a1302
Merge pull request #27 from Botts-Innovative-Research/rtmp-driver
drewbotts May 14, 2026
bf87d33
Merge branch 'opensensorhub:master' into 34-ais-shipxplorer-driver
BillBrown341 May 18, 2026
0aa61ba
initial NMEA Ais driver push
BillBrown341 May 19, 2026
1784b39
reorganized output and report types. Added multiple message handling …
BillBrown341 May 19, 2026
8379f1a
refractored Class A reports and added class B report
BillBrown341 May 20, 2026
dd3b49c
made AIS Messags its own output
BillBrown341 May 20, 2026
45161bf
added Message Id Descriptions for each Report
BillBrown341 May 20, 2026
4dce338
updated ReadMe
BillBrown341 May 22, 2026
cba811c
Updated to use Generic CommProvider
BillBrown341 May 22, 2026
28e1111
refractored code to use AisLib Java Library
BillBrown341 May 22, 2026
9a07ba2
added all primary outputs to driver
BillBrown341 May 22, 2026
fcd5997
updated depricated stop() method to stopReader()
BillBrown341 May 26, 2026
132dba9
added default logging in handler switch statement for unknown ids
BillBrown341 May 26, 2026
e754a7a
remved shipXplorer default in Config
BillBrown341 May 26, 2026
5f1d4c8
updated ReadMe
BillBrown341 May 26, 2026
09dce8b
added catch for non-AIS NMEA messages
BillBrown341 May 26, 2026
2ba8d46
Merge branch 'opensensorhub:master' into 34-ais-shipxplorer-driver
BillBrown341 May 28, 2026
67c6b20
fixed bundle activator
BillBrown341 Jun 3, 2026
31e802d
Updated org info from botts to geo
BillBrown341 Jun 3, 2026
52923de
added/updated license header for all code
BillBrown341 Jun 3, 2026
87efdea
removed unused imports
BillBrown341 Jun 3, 2026
bcd38bc
created Nmea Ais Helper
BillBrown341 Jun 3, 2026
2102526
added common record builder methods used in all outputs
BillBrown341 Jun 3, 2026
d673644
integrated NmeaAisHelper functionality into NmeaAis Classes
BillBrown341 Jun 3, 2026
e04bac2
added receiver sampling time field
BillBrown341 Jun 3, 2026
2838c27
completed receiver sampling time addition
BillBrown341 Jun 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
293 changes: 293 additions & 0 deletions sensors/positioning/sensorhub-driver-nmeaais/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
# NMEA AIS Message Decoder Driver

Decodes live AIS (Automatic Identification System) NMEA sentences received over any OSH-supported communication channel (UDP, TCP, serial) and publishes structured observation data for each vessel and aid-to-navigation.

## NMEA Sentence Structure

Each AIS transmission is a comma-delimited NMEA sentence:

```
!AIVDM,1,1,,A,15Muan<000qm2=2CavBWSCL20@2?,0*6A
```

| Field | Example | Meaning |
|-------|---------|---------|
| Sentence type | `!AIVDM` | AIS VHF Data-link Message |
| Fragment count | `1` | Total fragments in this message |
| Fragment number | `1` | This fragment's index |
| Sequential ID | *(blank)* | Links multi-part messages |
| Channel | `A` | AIS Channel A (161.975 MHz) or B (162.025 MHz) |
| Payload | `15Muan<000q...` | 6-bit ASCII-armored AIS payload |
| Fill bits | `0` | Padding bits added to final payload |
| Checksum | `6A` | NMEA XOR checksum |

**Supported sentence types:** This driver only processes sentences beginning with `!AIVDM` (messages received from other vessels) or `!AIVDO` (own-vessel transmissions). All other NMEA sentences arriving on the configured port — such as GPS fix sentences (`$GPGGA`, `$GPRMC`) — are silently ignored. This means the driver can safely share a UDP port carrying mixed NMEA traffic without producing errors or garbled output.

---

## Hardware Requirements

- [ShipXplorer AIS Dongle](https://www.shipxplorer.com/ais-dongle) (or any RTL-SDR / AIS receiver)
- [ShipXplorer AIS Antenna](https://www.shipxplorer.com/ais-antenna) (or suitable VHF antenna)

## External Software Requirements
This driver does not directly receive or decode AIS radio signals from SDR hardware.
Instead, it expects already-decoded NMEA 0183 AIS messages to be forwarded to the driver over a supported network interface
Because of this, users must run external AIS decoding or forwarding software capable of:

- Receiving AIS signals from SDR hardware or another AIS source
- Decoding the AIS transmissions into NMEA 0183 AIS sentences
- Forwarding the decoded messages to this OSH driver

Examples of compatible software include:
- [AIS-Catcher](https://jvde-github.github.io/AIS-catcher-docs/) — decodes RTL-SDR radio input and forwards NMEA sentences over UDP
- AIS Dispatcher
---

## Setup

### 1. Install and run AIS-Catcher (or other AIS decoding software)

Start AIS-Catcher and forward sentences to your OSH node over UDP:

```bash
# Forward to localhost port 10110
AIS-catcher -u 127.0.0.1 10110
```

For a remote OSH node replace `127.0.0.1` with the node's IP address.

### 2. Add the driver in OSH Admin Panel

1. Navigate to **Sensor Drivers** → **Add Driver**
2. Select **NMEA AIS Message Decoder Driver**
3. Fill in:
- **Serial Number** — any unique identifier for this driver instance (e.g. `AIS-001`)
- **Communication Settings** → select **UDP2 Comm Provider**
- **Local Address** — the address OSH should bind to (e.g. `127.0.0.1`)
- **Local Port** — must match the port used in AIS-Catcher (e.g. `10110`)
- Leave Remote Host/Port empty — the driver only *receives* data, it does not send

> **Note:** Configure *Local Port*, not *Remote Port*. Remote Host/Port is for outbound connections; this driver only receives inbound UDP packets.

### 4. Start the driver

Click **Start**. The driver connects to the UDP socket, reads incoming sentences, and begins publishing to all outputs as messages arrive.

---

## Outputs

The driver registers **7 outputs**. Each output creates a Feature of Interest (FOI) keyed by MMSI the first time a vessel is seen.

### 1. NMEA AIS Messages (`nmeaAisOutputRawMessages`)

Publishes every raw sentence as received — before decoding. Useful for logging, replay, and debugging.

| Field | Type | Description |
|-------|------|-------------|
| sampleTime | Time | System time sentence was received |
| sentenceType | Text | e.g. `!AIVDM` |
| fragmentCount | Integer | Total fragments in message |
| fragmentNumber | Integer | This fragment's index |
| sequentialId | Text | Multi-part link ID |
| channel | Text | `A` or `B` |
| frequency | Double (MHz) | 161.975 (A) or 162.025 (B) |
| rawPayload | Text | Encoded AIS payload |
| fillBits | Integer | Padding bit count |
| checkSum | Text | NMEA checksum |

---

### 2. Position Report Class A (`nmeaAisOutputPositionClassA`)

Decoded Class A shipborne position reports. Published for message types **1**, **2**, and **3** — all three carry an identical field layout.

| Field | Type | Description |
|-------|------|-------------|
| messageId | Integer | 1 = Scheduled, 2 = Assigned, 3 = Interrogation response |
| reportDescription | Text | Human-readable type description |
| repeat | Integer | Repeat indicator (0–3) |
| mmsi | Integer | Maritime Mobile Service Identity |
| navStatus | Integer | Navigational status (0 = underway, 1 = at anchor, etc.) |
| rot | Integer | Rate of turn (–128 to +127; –128 = not available) |
| sog | Double (kn) | Speed over ground |
| positionAccuracy | Integer | 1 = high (≤10 m), 0 = low |
| location | Lat/Lon | Position in decimal degrees |
| cog | Double (°) | Course over ground |
| heading | Integer (°) | True heading (511 = not available) |
| timeStamp | Time | UTC second of fix |
| smi | Integer | Special manoeuvre indicator |
| raim | Integer | RAIM flag |
| commState | Integer | Communication state |

---

### 3. Position Report Class B (`nmeaAisOutputPositionClassB`)

Decoded Class B position reports. Handles both **type 18** (Standard CS) and **type 19** (Extended CS). Type 19 additionally carries vessel name, ship type, and dimensions; those fields are empty/zero for type 18 records.

| Field | Type | Description |
|-------|------|-------------|
| messageId | Integer | 18 = Standard, 19 = Extended |
| reportDescription | Text | Human-readable type description |
| repeat | Integer | Repeat indicator |
| mmsi | Integer | MMSI |
| sog | Double (kn) | Speed over ground |
| positionAccuracy | Integer | 1 = high, 0 = low |
| location | Lat/Lon | Position in decimal degrees |
| cog | Double (°) | Course over ground |
| heading | Integer (°) | True heading (511 = not available) |
| timeStamp | Time | UTC second of fix |
| unitFlag | Integer | 0 = SOTDMA unit, 1 = CS unit *(type 18 only)* |
| displayFlag | Integer | Display capability *(type 18 only)* |
| dscFlag | Integer | DSC capability *(type 18 only)* |
| bandFlag | Integer | Band capability *(type 18 only)* |
| message22Flag | Integer | Frequency management flag *(type 18 only)* |
| modeFlag | Integer | 0 = autonomous, 1 = assigned |
| raim | Integer | RAIM flag |
| commStateFlag | Integer | SOTDMA/ITDMA selector *(type 18 only)* |
| commState | Integer | Communication state *(type 18 only)* |
| name | Text | Vessel name *(type 19 only; empty for type 18)* |
| shipType | Integer | Ship type code *(type 19 only)* |
| dimBow | Integer (m) | GPS antenna to bow *(type 19 only)* |
| dimStern | Integer (m) | GPS antenna to stern *(type 19 only)* |
| dimPort | Integer (m) | GPS antenna to port *(type 19 only)* |
| dimStarboard | Integer (m) | GPS antenna to starboard *(type 19 only)* |
| epfd | Integer | EPFD type *(type 19 only)* |
| dte | Integer | Data terminal equipment *(type 19 only)* |
| assignedMode | Integer | Assigned mode flag *(type 19 only)* |

---

### 4. Base Station Report (`nmeaAisOutputBaseStation`)

UTC/date and position broadcasts from fixed AIS base stations. Published for message types **4** and **11** (identical layout).

| Field | Type | Description |
|-------|------|-------------|
| messageId | Integer | 4 = UTC/Date Report, 11 = UTC/Date Response |
| reportDescription | Text | Human-readable type description |
| repeat | Integer | Repeat indicator |
| mmsi | Integer | Base station MMSI |
| utcYear | Integer | UTC year |
| utcMonth | Integer | UTC month (1–12) |
| utcDay | Integer | UTC day (1–31) |
| utcHour | Integer | UTC hour (0–23; 24 = not available) |
| utcMinute | Integer | UTC minute (0–59) |
| utcSecond | Integer | UTC second (0–59) |
| positionAccuracy | Integer | 1 = high, 0 = low |
| location | Lat/Lon | Base station position |
| epfd | Integer | EPFD type |
| raim | Integer | RAIM flag |

---

### 5. Static and Voyage Data (`nmeaAisOutputStaticVoyage`)

Vessel identity and voyage information from Class A vessels. Published for message **type 5**. This is a multi-sentence (two-part) message; AISLib reassembles both parts before this output is updated.

| Field | Type | Description |
|-------|------|-------------|
| messageId | Integer | Always 5 |
| reportDescription | Text | Human-readable type description |
| repeat | Integer | Repeat indicator |
| mmsi | Integer | MMSI |
| aisVersion | Integer | AIS version (0 = ITU1371) |
| imoNumber | Integer | IMO number (0 = not available) |
| callSign | Text | Call sign |
| name | Text | Vessel name |
| shipType | Integer | Ship type code |
| dimBow | Integer (m) | GPS antenna to bow |
| dimStern | Integer (m) | GPS antenna to stern |
| dimPort | Integer (m) | GPS antenna to port |
| dimStarboard | Integer (m) | GPS antenna to starboard |
| epfd | Integer | EPFD type |
| etaMonth | Integer | ETA month |
| etaDay | Integer | ETA day |
| etaHour | Integer | ETA hour |
| etaMinute | Integer | ETA minute |
| draught | Double (m) | Maximum static draught |
| destination | Text | Destination port |
| dte | Integer | Data terminal equipment |

---

### 6. Class B Static Data (`nmeaAisOutputStaticDataClassB`)

Vessel name, callsign, and dimensions from Class B vessels. Published for message **type 24**.

Type 24 is transmitted in two separate sentences: Part A (name only) and Part B (callsign, ship type, dimensions). The driver caches Part A names by MMSI and publishes a combined record when Part B arrives. If Part B is received before Part A, the name field will be empty until the next transmission cycle.

| Field | Type | Description |
|-------|------|-------------|
| messageId | Integer | Always 24 |
| reportDescription | Text | Human-readable type description |
| repeat | Integer | Repeat indicator |
| mmsi | Integer | MMSI |
| name | Text | Vessel name (from Part A; empty if not yet received) |
| callSign | Text | Call sign (from Part B) |
| shipType | Integer | Ship type code |
| dimBow | Integer (m) | GPS antenna to bow |
| dimStern | Integer (m) | GPS antenna to stern |
| dimPort | Integer (m) | GPS antenna to port |
| dimStarboard | Integer (m) | GPS antenna to starboard |
| vendorId | Text | Manufacturer vendor ID |

---

### 7. Aid-to-Navigation Report (`nmeaAisOutputAidNavigation`)

Position and status of fixed and floating aids to navigation (buoys, lighthouses, beacons). Published for message **type 21**.

| Field | Type | Description |
|-------|------|-------------|
| messageId | Integer | Always 21 |
| reportDescription | Text | Human-readable type description |
| repeat | Integer | Repeat indicator |
| mmsi | Integer | Aid MMSI |
| typeOfAidsToNav | Integer | Aid type (1 = unspecified; 2 = reference; 3 = RACON; 4 = fixed; 21–29 = floating; etc.) |
| name | Text | Aid name |
| positionAccuracy | Integer | 1 = high, 0 = low |
| location | Lat/Lon | Aid position |
| dimBow | Integer (m) | Bow dimension |
| dimStern | Integer (m) | Stern dimension |
| dimPort | Integer (m) | Port dimension |
| dimStarboard | Integer (m) | Starboard dimension |
| epfd | Integer | EPFD type |
| utcSecond | Integer | UTC second of report |
| offPositionIndicator | Integer | 0 = on position, 1 = off position |
| raim | Integer | RAIM flag |
| virtualAid | Integer | 0 = physical, 1 = virtual (simulated) |
| assignedMode | Integer | 0 = autonomous, 1 = assigned |

---

## Unhandled Message Types

The following AIS message types are received and parsed by the radio layer but are **not published** to any output. Each is silently discarded after the raw sentence is recorded in the `nmeaAisOutputRawMessages` output.

| Type | Name | Reason not output |
|------|------|-------------------|
| 6 | Addressed Binary Message | Application-specific binary payload; no standard decoded fields |
| 7 | Binary Acknowledge | Acknowledgment sequence numbers only; no sensor data |
| 8 | Binary Broadcast Message | Application-specific binary payload |
| 9 | SAR Aircraft Position Report | Position report for SAR aircraft; not currently implemented |
| 10 | UTC/Date Inquiry | Request-only message; no data payload |
| 12 | Addressed Safety-Related Message | Point-to-point text; not broadcast |
| 13 | Safety-Related Acknowledge | Acknowledgment only |
| 14 | Safety-Related Broadcast | Broadcast text alert; not currently implemented |
| 15 | Interrogation | Request-only message |
| 16 | Assignment Mode Command | Administrative base station command |
| 17 | DGNSS Binary Broadcast | Differential GPS corrections; specialized use |
| 20 | Data Link Management | Base station time-slot reservation; no vessel data |
| 22 | Channel Management | Base station VHF frequency management |
| 23 | Group Assignment Command | Base station group command |
| 25 | Single Slot Binary Message | Application-specific binary payload |
| 26 | Multiple Slot Binary Message | Application-specific binary payload |
| 27 | Long-Range AIS Broadcast | Class A/B position report for vessels outside base station coverage; not currently implemented |

All received sentences — including the above types — are available in the raw messages output for custom processing or logging.

## Additional Resources
- [USCG Navigation Center](https://www.navcen.uscg.gov/ais-messages)
39 changes: 39 additions & 0 deletions sensors/positioning/sensorhub-driver-nmeaais/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
description = 'NMEA AIS Decoder'
ext.details = "Driver to Decode NMEA AIS Messages"
version = '1.0.0'

dependencies {
implementation 'org.sensorhub:sensorhub-core:' + oshCoreVersion
testImplementation('junit:junit:4.13.1')
implementation 'dk.dma.ais.lib:ais-lib-messages:2.8.5'
implementation 'dk.dma.ais.lib:ais-lib-communication:2.8.5'

}

// exclude tests requiring connection to the sensor
// these have to be run manually
// If tests are to be excluded list them here as follows
// exclude '**/TestNameClass.class'
//test {
// useJUnit()
//}

// add info to OSGi manifest
osgi {
manifest {
attributes ('Bundle-Vendor': 'Botts Inc')
attributes ('Bundle-Activator': 'org.sensorhub.impl.sensor.aisshipxplorer.Activator')
Comment thread
BillBrown341 marked this conversation as resolved.
Outdated
}
}

// add info to maven pom
ext.pom >>= {
developers {
developer {
id 'BillBrown341'
name 'Bill Brown'
organization 'Botts-inc'
Comment thread
BillBrown341 marked this conversation as resolved.
Outdated
organizationUrl 'https://botts-inc.com/'
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/***************************** BEGIN LICENSE BLOCK ***************************
The contents of this file are subject to the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one
at http://mozilla.org/MPL/2.0/.

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.

Copyright (C) 2020-2025 Botts Innovative Research, Inc. All Rights Reserved.
******************************* END LICENSE BLOCK ***************************/
package org.sensorhub.impl.sensor.nmeaais;

import org.sensorhub.utils.OshBundleActivator;

/**
* The presence of this class tells the OpenSensorHub OSGI machinery about this module.
* It is referenced in the 'Bundle-Activator' attribute in the OSGI section of the build.gradle file.
*/
@SuppressWarnings("unused")
public class Activator extends OshBundleActivator {
}
Loading
Loading