Preparing search index...

    Working with Signal K Deltas

    WASM plugins can both emit and receive Signal K deltas. This page covers both directions.

    Use the emit() function to send delta messages to the Signal K server:

    import {
    emit,
    createSimpleDelta,
    SK_VERSION_V1,
    SK_VERSION_V2
    } from '@signalk/assemblyscript-plugin-sdk/assembly'

    // Emit a v1 delta (default - for regular navigation data)
    const tempDelta = createSimpleDelta('environment.outside.temperature', '288.15')
    emit(tempDelta)

    // Emit a v2 delta (for Course API and v2-specific paths)
    const courseDelta = createSimpleDelta(
    'navigation.course.nextPoint',
    positionJson
    )
    emit(courseDelta, SK_VERSION_V2)

    Note: Plugins should NOT include source or timestamp in emitted deltas. The server automatically:

    • Sets $source to the plugin ID
    • Fills in timestamp with the current time

    The emit() function accepts an optional second parameter to specify the Signal K version:

    Version Constant Use Case
    v1 (default) SK_VERSION_V1 Regular navigation data: navigation.*, environment.*, electrical.*, etc.
    v2 SK_VERSION_V2 Course API paths and v2-specific data that should not be mixed into the v1 full data model

    Why does this matter?

    • v1 deltas update the full Signal K data model and are available via the REST API and WebSocket subscriptions
    • v2 deltas are emitted as events for v2 API subscribers without mixing into the v1 data model

    Most plugins should use v1 (the default). Only use v2 when emitting Course API data or other v2-specific paths.

    This mirrors the TypeScript plugin API where handleMessage() accepts an optional skVersion parameter.


    WASM plugins can subscribe to receive Signal K deltas, enabling them to react to navigation data changes, course updates, sensor readings, and other vessel data in real-time.

    Export a delta_handler() function to receive deltas:

    // assembly/index.ts

    // Plugin state
    let vesselLat: f64 = 0.0
    let vesselLon: f64 = 0.0
    let hasPosition: bool = false

    export function delta_handler(deltaJson: string): void {
    // Check for position updates
    if (deltaJson.indexOf('"path":"navigation.position"') >= 0) {
    const lat = parseFloat64FromJson(deltaJson, 'latitude')
    const lon = parseFloat64FromJson(deltaJson, 'longitude')

    if (lat !== 0.0 || lon !== 0.0) {
    vesselLat = lat
    vesselLon = lon
    hasPosition = true
    debug('Position updated: ' + lat.toString() + ', ' + lon.toString())
    }
    }

    // Check for course nextPoint
    if (deltaJson.indexOf('"path":"navigation.course.nextPoint"') >= 0) {
    // Extract destination coordinates and perform calculations
    // ...
    }

    // Check for speedOverGround
    if (deltaJson.indexOf('"navigation.speedOverGround"') >= 0) {
    const speed = parseFloat64FromJson(deltaJson, 'value')
    // Process speed data
    }
    }

    // Helper function to parse float from JSON
    function parseFloat64FromJson(json: string, key: string): f64 {
    const searchKey = '"' + key + '":'
    const match = json.indexOf(searchKey)
    if (match < 0) return 0.0

    let start = match + searchKey.length
    while (
    start < json.length &&
    (json.charCodeAt(start) == 32 || json.charCodeAt(start) == 9)
    ) {
    start++
    }

    let end = start
    while (end < json.length) {
    const c = json.charCodeAt(end)
    if (c == 44 || c == 125 || c == 93) break // comma, }, ]
    end++
    }

    const numStr = json.substring(start, end).trim()
    return parseFloat(numStr)
    }

    Deltas received by delta_handler() include $source and timestamp (added by the server):

    {
    "context": "vessels.self",
    "updates": [
    {
    "$source": "n2k-on-ve.can-socket.43",
    "timestamp": "2024-01-15T12:30:00.000Z",
    "values": [
    {
    "path": "navigation.position",
    "value": { "latitude": -17.68, "longitude": 177.39 }
    },
    { "path": "navigation.speedOverGround", "value": 5.2 }
    ]
    }
    ]
    }
    1. Course Calculations - React to navigation.course.nextPoint and navigation.position to calculate bearing, distance, XTE
    2. Anchor Watch - Monitor navigation.position and compare to anchor position
    3. Speed Alerts - Watch navigation.speedOverGround for threshold breaches
    4. Environment Monitoring - Track environment.wind.*, environment.water.temperature, etc.

    When values are cleared (e.g., destination removed), the server sends null values:

    export function delta_handler(deltaJson: string): void {
    if (deltaJson.indexOf('"path":"navigation.course.nextPoint"') >= 0) {
    // Try to extract position first
    const lat = parseFloat64FromJson(deltaJson, 'latitude')
    const lon = parseFloat64FromJson(deltaJson, 'longitude')

    if (lat !== 0.0 || lon !== 0.0) {
    // Valid position - update state
    nextPointLat = lat
    nextPointLon = lon
    hasDestination = true
    } else {
    // Check if this is a null/clear operation
    const pathIdx = deltaJson.indexOf('"path":"navigation.course.nextPoint"')
    const checkRange = deltaJson.substring(
    pathIdx,
    Math.min(pathIdx + 100, deltaJson.length) as i32
    )
    if (checkRange.indexOf('"value":null') >= 0) {
    hasDestination = false
    debug('Destination cleared')
    }
    }
    }
    }
    • Filter Early - Check for relevant paths before parsing to minimize processing
    • State Caching - Store parsed values in global variables rather than re-parsing
    • Debouncing - High-frequency data (GPS at 10Hz) may benefit from debouncing calculations