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

@rhizomatics/signalk-delta-squelch-plugin

v0.4.6

Published

Clean up noisy SignalK deltas by rounding to sensor's real precision and throttling insignificant or unchanging deltas, reducing compute and storage usage and false anchor alerts.

Readme

signalk-delta-squelch-plugin

npm version npm downloads SignalK Plugin CI code style: oxfmt License boat tech directory

Cuts noisy, redundant SignalK deltas at the source, before they reach the full data model, other plugins, or connected clients. Reduce size of stored data both by removing pointless changes, and better efficiency for analytic column-store databases.

Use at own risk, incorrect configuration for your boat's systems may mean GNSS (e.g. GPS) positions on plotters or anchor trackers are stale, and likewise for depth, tide or wind data.

The problem

Sources like GPS receivers emit far more precision, and far more frequent updates (10Hz for modern ones), than the physical measurement actually supports. At anchor, a typical GPS fix wanders by several metres from multipath and receiver noise alone — producing a stream of deltas like this example for a berthed boat:

{"path": "navigation.position", "value": {"latitude": 54.372581233333334, "longitude": -4.907908233333333}}
{"path": "navigation.position", "value": {"latitude": 54.3725812,         "longitude": -4.907908233333333}}
{"path": "navigation.position", "value": {"latitude": 54.3725811,         "longitude": -4.9079082499999995}}

None of that movement is real; all of it costs CPU, eMMC and SD Card wear, and power to generate, log, and re-broadcast — multiplied by every plugin and client subscribed to the path. Throttling a single websocket subscription doesn't help: the noise is still generated, still processed by every other consumer, and often still written to disk.

There's an additional benefit if using a column-oriented data store to archive data, such as Parquet, InfluxDB or QuestDB - these databases are much more efficient where there are fewer unique values to store (the 'cardinality') so trimming off the false millimetre position resolution can save on CPU and disk space both when writing data and later querying.

And if you're watching an anchor tracker, there's a little less meaningless clutter on the boat tracks.

What this plugin does

It registers a registerDeltaInputHandler — a hook that runs before the server applies a delta to the full data model or forwards it to anyone. For each value on a recognised path, it:

  1. Rounds the value to a resolution matching the sensor's real-world accuracy (configurable per category, or per path).
  2. Removes updates that don't move past that resolution, so consumers see clean, sensible numbers arriving only when something actually changed.
  3. Squelches unchanging text/boolean values — notification, state, and switch paths — once the same value has been seen for a configurable number of consecutive readings in a row (10 by default).
  4. Forwards a heartbeat at a configurable interval even with no change, so nothing downstream mistakes a quiet source for a dead one.
  5. Optionally rejects GNSS position spikes — the classic anchor-watch false-alarm cause, where a single bad fix implies the boat teleported — while always letting through small jumps that are just ordinary GNSS scatter, not glitches.

Everything is gated on the raw value with a hysteresis margin (1.5× the rounding step), not the rounded value — a reading sitting right on a rounding boundary won't flip back and forth every sample.

Categories and defaults

| Category | Matches (auto-detected) | Default resolution | | ------------- | ------------------------------------------------------------ | ---------------------------- | | position | navigation.position only | 0.000001° lat & lon (~0.11m) | | temperature | any path containing "temperature" | 0.01 K | | velocity | any path containing "speed" | 0.05 m/s (~0.1 knot) | | heading | heading / course / angle / direction / variation / deviation | 0.01745 rad (~1°) | | height | depth / height / altitude / draft / freeboard | 0.1 m | | voltage | any path containing "voltage" | 0.1 V | | pressure | any path containing "pressure" | 100 Pa (1 mbar) | | humidity | any path containing "humidity" | 0.001 (0.1%) |

Any path that doesn't match one of these is passed through untouched unless you add an explicit override.

For older GPS antenna, without modern L5 and SBAS for high resolution, 0.00001 may be more appropriate, the default setting covers modern systems that have <1m resolution.

Text and boolean values

Paths whose value is a string or boolean (notification states, autopilot state, switch positions, ...) have no physical resolution to round to, so they're squelched by exact-value repetition instead: a value is only dropped once it's been seen unchanged for unchangingCountThreshold consecutive readings in a row (10 by default). Earlier repeats, any change of value, and the heartbeat are always forwarded — so a path that only ever emits on an actual change is never squelched at all, and a flappy or rarely-updated path still gets the same protection as numeric ones.

This applies to any path emitting a string or boolean value; there's no category to auto-detect since there are no units or rounding step involved.

Position outlier rejection (anchor-watch GNSS spikes)

A single position implying a speed above your configured maximum (with a safety margin) is treated as a GNSS glitch and dropped — the boat stays at its last good fix. If several readings in a row agree on the new location within a short window, it's accepted as real movement (e.g. after a GNSS dropout), not a one-off spike.

A jump smaller than minimum spike distance (2m by default) is never rejected, no matter how fast it implies the boat moved. At that scale it's ordinary GNSS scatter, not a glitch — and that scatter is useful: a "cocked hat" of nearby fixes spread over time can average out to a better position estimate than trusting any single fix, so it's worth keeping rather than squelching away.

This only applies to the vessel's own position (app.selfContext) — an AIS target moving fast isn't an anomaly just because it isn't us — and it only addresses large, single-fix jumps. It does not fix slow GNSS wander at anchor - these are genuinely different problems: a spike is one bad sample surrounded by good ones, wander is every sample being slightly wrong in a way no single-sample check can detect.

All squelch state (position or otherwise) is tracked per $source as well as per path. This plugin runs upstream of the server's own source-priority resolution, so a path fed by more than one device (e.g. a chartplotter GNSS and an AIS transceiver's own GNSS both reporting navigation.position) is seen here as each source's raw, independent stream — never compared against each other. Without that, a legitimate correction from a poorer fix to a better one (or a source switch driven by your SignalK priority rules) could look like the boat teleporting relative to whichever source reported last, and get wrongly rejected as a spike. The rejected-spike debug log names the context (self for the vessel's own position), path, and source together, along with the two positions, the time elapsed between them, and the distance and implied speed that triggered the rejection — enough to tell which device is actually the noisy one.

Configuration

All of the above is configurable from the plugin's config screen:

  • Heartbeat interval — global default, overridable per path.
  • Unchanging value threshold — global default (10) for how many consecutive identical text/boolean readings are forwarded before squelching kicks in, overridable per path.
  • Default rounding resolution per category — temperature, velocity, heading, height, voltage, pressure, humidity (native SignalK SI units), and position (lat/lon in degrees, settable independently).
  • Position outlier settings — enable/disable, max vessel speed (knots), safety margin multiplier, minimum spike distance (2m default — jumps below this are never rejected), confirmation count, and confirmation window.
  • Path-specific overrides — add a path to set its own resolution (or lat/lon resolution, for position-shaped paths), category, heartbeat, or unchanging value threshold, overriding the category default or handling a path that isn't auto-recognised.

Install

Install from the SignalK admin UI Apps & Plugins Store, or:

cd ~/.signalk && npm install @rhizomatics/signalk-delta-squelch-plugin

Then enable it under Apps & PluginsConfiguration* → Delta Squelch.

License

Apache-2.0