npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sailingnaturali/signalk-dsc

v0.3.0

Published

Receive, log, and alert on DSC (VHF digital selective calling) calls — distress, urgency, safety, routine — from NMEA 0183 ($CDDSC/$CDDSE) and NMEA 2000 (PGN 129808).

Readme

@sailingnaturali/signalk-dsc

SignalK plugin that receives, logs, and alerts on DSC (VHF digital selective calling) traffic — distress, urgency, safety, and routine calls — from both NMEA 0183 ($--DSC/$--DSE) and NMEA 2000 (PGN 129808).

Why

When a vessel hits the red button, its radio broadcasts a DSC burst on channel 70 with MMSI, position, and nature of distress — perfectly readable even when the follow-up voice call on 16 is not. Stock SignalK mostly drops this data: the 0183 hook misses common sentence variants and persists nothing, and the N2K converter has no PGN 129808 mapping at all. If you might be the nearest boat, you want every received alert stored with its position and surfaced as an alarm — that is this plugin.

Logging that traffic is also the regulatory standard. Maritime radio rules require compulsorily-equipped vessels to record every distress, urgency, and safety call made or intercepted, with the time and position of the station in distress (47 CFR §80.409; SOLAS Ch. IV; ITU Radio Regulations; Canada's TP 1539). Pleasure craft are generally exempt from the log mandate — this just gives you that same SOLAS-grade record automatically. The parser gaps and the regulation are written up in more detail on the engineering blog.

What you get

For every DSC call heard by a connected radio:

  • A persistent call log — JSONL on disk, served at GET /signalk/v2/api/resources/dsc-calls (anonymously readable when the server allows read-only access). Raw sentence/PGN is always kept alongside the parsed fields: time, MMSI, category, nature of distress, position, UTC time.

  • Alarms under your own vesselnotifications.dsc.distress (state emergency), notifications.dsc.urgency (alarm), notifications.dsc.safety (alert). Routine calls never alarm. Repeated re-transmissions of the same alert (DSC auto-repeats until acknowledged) update the stored call instead of re-alarming. Alerts received within the last hour are re-raised after a server restart — notifications are in-memory, and a received MAYDAY must not vanish because the server bounced.

  • A voice-sized message — the notification message is deliberately minimal (type, vessel, situation, range and direction from own position, action):

    DSC distress alert: vessel Wind Chaser, sinking, 2.3 nautical miles northwest. Monitor channel 16.

    Full detail (MMSI, coordinates, reported time, transport) goes to the call log and the logbook entry instead, so TTS pipelines stay terse.

  • Remote-vessel deltas — the caller's navigation.position (and a distress notification) under vessels.urn:mrn:imo:mmsi:<caller>, so chartplotters can show where the call came from.

  • Every stored call carries an ownShip snapshot of the moment it arrived — position, course, heading, speed, wind, pressure, and (when a source publishes them) sea state, visibility, and cloud coverage. Absent sensor, absent field.

  • Logbook entries are written with vhf: "70" (DSC is received on channel 70 by definition) plus structured observations; non-distress calls that propose a working channel get it in the entry text and on the stored event as workingChannel.

  • Optional ship's-log entries via signalk-logbook — a GMDSS-style radio log of received distress/urgency/safety traffic.

Note: Visibility on environment.outside.visibility is read as meters and converted to the logbook 0–9 fog scale; an integer value ≤ 9 is assumed to already be a fog-scale code, so sub-10-meter metric readings would be misread.

Transports

  • NMEA 0183: registers custom DSC and DSE sentence parsers (these replace the server's stock DSC hook with a superset: tolerant of sparse distress alerts some radios emit — see nmea0183-signalk#217 — and with DSE position refinement from ±1 NM to ten-thousandths of a minute, which the stock parser ignores entirely).
  • NMEA 2000: listens to the server's analyzer stream (N2KAnalyzerOut) for PGN 129808, since n2k-signalk produces no delta for it.

Configuration

| Option | Default | Notes | | --- | --- | --- | | maxEvents | 1000 | Oldest calls dropped beyond this. | | logbookEnabled | true | Requires signalk-logbook and a token. | | logbookRoutine | false | Also log routine calls. | | logbookUrl | http://localhost:3000/plugins/signalk-logbook/logs | | | logbookToken | empty | SignalK access token; logbook writes are skipped without one (plugin routes are auth-gated). | | snapshotPaths | [] | Extra { field, path } pairs added to the ownShip snapshot on each stored call (position, course, heading, speed, wind, pressure, sea state, visibility and cloud coverage are always attempted). |

Trying it without a radio

Quick test script

The repo includes a script that builds a valid DSC sentence and fires it at the server over UDP. First add a UDP input in your SignalK pipedProviders (Settings → Connections → Add):

{
  "id": "dsc-test-udp",
  "pipeElements": [{ "type": "providers/simple",
    "options": { "type": "NMEA0183", "subOptions": { "type": "udp", "port": "7777" } } }]
}

Then send a fake distress call:

# Default: sinking, MMSI 366191919, near Boundary Pass → naturalaspi.local:7777
node scripts/send-test-dsc.js

# npm alias
npm run send-test-dsc

# Different nature of distress
node scripts/send-test-dsc.js --nature fire
node scripts/send-test-dsc.js --nature mob --category urgency

# Different vessel / position
node scripts/send-test-dsc.js --mmsi 316555777 --lat 48.9 --lon -123.5

# Different host / port
node scripts/send-test-dsc.js --host localhost --port 7777

All --nature values: fire, flooding, collision, grounding, listing, sinking, adrift, abandon, piracy, mob, epirb.

Verify the call was captured:

GET /signalk/v2/api/resources/dsc-calls

Manual sentence injection

You can also feed raw sentences through any NMEA 0183 connection (TCP, UDP, file playback):

$CDDSC,12,3380400790,12,05,00,1423108312,2019,,,S,E*69
$CDDSE,1,1,A,3380400790,00,45894494*1B

Clearing an alarm

A received distress/urgency/safety call raises notifications.dsc.<category> and is re-raised for up to an hour across server restarts. To clear an active alarm — dropping the live notification and stopping the restart re-raise:

SIGNALK_TOKEN=<readwrite-token> npm run clear-dsc -- --category distress

--category all clears all three. Clearing is a write, so it needs a readwrite token (the same one used to fire a test MOB). A new incoming call still alarms normally.

Limitations

  • Distress relays, acknowledgements, and cancellations are stored (with acknowledgement/distressedMmsi fields) but don't yet clear or transform the original alarm.
  • Multi-sentence DSE groups are ignored (single-sentence covers Class-D gear).
  • A raised distress notification stays active until cleared from the server — deliberate: a received MAYDAY should not silently expire.

License

MIT