Signal K Data Model

Formats

Signal K defines two data formats—full and delta—for representing and transmitting data. All Signal K data is transmitted as UTF-8 JSON.

Full Format

The full format is conceptually the simplest representation of data in Signal K. It contains all of the data from a Signal K node, which in the case of a Signal K server could me many hundreds of data points.

{
  "version": "1.0.0",
  "self": "urn:mrn:signalk:uuid:705f5f1a-efaf-44aa-9cb8-a0fd6305567c",
  "vessels": {
    "urn:mrn:signalk:uuid:705f5f1a-efaf-44aa-9cb8-a0fd6305567c": {
      "navigation": {
        "speedOverGround": {
          "value": 4.32693662,
          "$source": "ttyUSB0.GP",
          "sentence": "RMC",
          "timestamp": "2017-05-16T05:15:50.007Z"
        },
        "position": {
          "value": {
            "altitude": 0.0,
            "latitude": 37.81479,
            "longitude": -122.44880152
          },
          "$source": "ttyUSB0.GP",
          "sentence": "RMC",
          "timestamp": "2017-05-16T05:15:50.007Z"
        },
        "headingMagnetic": {
          "value": 5.55014702,
          "$source": "ttyUSB0.II",
          "sentence": "HDM",
          "timestamp": "2017-05-16T05:15:54.006Z"
        }
      },
      "name": "Motu",
      "uuid": "urn:mrn:signalk:uuid:705f5f1a-efaf-44aa-9cb8-a0fd6305567c"
    }
  },
  "sources": {
    "ttyUSB0": {
      "label": "ttyUSB0",
      "type": "NMEA0183",
      "GP": {
        "talker": "GP",
        "sentences": {
          "RMC": "2017-04-03T06:14:04.451Z"
        }
      },
      "II": {
        "talker": "II",
        "sentences": {
          "HDM": "2017-05-16T05:15:54.006Z"
        }
      }
    }
  }
}

There are several top level attributes or keys which are always present and others which are optional. The version key specifies which version of the Signal K specification is being used and must always present in a full Signal K model. Also always present in the full model is the self key. The value of self is the key within the vessels object which is the local boat. Effectively, it is a pointer into the vessels object.

Below the vessels object is a list of vessels, identified by their MMSI number or a generated unique ID. There may be many vessels if data has been received from AIS or other sources. The format for each vessel’s data uses the same standard Signal K structure but may not have the same content; likely you will not have as much data about other vessels as you have about your own.

At the same level as vessels is sources. This contains a list of sources the data was obtained from. Each data object within a vessel may have a $source key which point to a source within sources. Several data objects may reference the same source since a single NMEA sentence or PGN may map to multiple keys in Signal K.

Alternatively the source data may be embedded directly in place of the $source by using the source key:

{
  "vessels": {
    "urn:mrn:signalk:uuid:705f5f1a-efaf-44aa-9cb8-a0fd6305567c": {
      "navigation": {
        "position": {
          "value": {
            "altitude": 0.0,
            "latitude": 37.81479,
            "longitude": -122.44880152
          },
          "source": {
            "label": "ttyUSB0",
            "type": "NMEA0183",
            "talker": "GP",
            "sentence": "PRMC"
          },
          "timestamp": "2017-05-16T05:15:50.007Z"
        }
      }
    }
  }
}

For more information on sources, see the sources section.

Data objects in Signal K are organized hierarchically, for example data related to navigation such as position, speed through water and heading are all organized under a navigation sub-topic within the vessel object. Each data object has a value property which holds the actual value for that specific key. The value property may contain a number, a string or another object. Signal K keys that are object valued are object valued because the values don‘t have much semantic meaning individually. For example position – latitude doesn‘t have much meaning without an associated longitude. Therefore, these (and altitude) are grouped together in a single navigation.position key.

The values are always SI units, and always the same units for the same key. Therefore, speedOverGround is always meters per second, never knots, km/hr, or miles/hr. This means you never have to send units with data, the units are specific for a key, and defined in the data schema. A simplified version of the JSON schema with the units is available in Keys Reference in Appendix A. The units are also always specified in the values’ metadata which is available via the REST API in the meta.units property. Besides the units property, meta provides a lot of other useful information for consumers of the data.

Finally, each data object also has a timestamp property which represents the time that the value was measured. Timestamps are in ISO 8601 format – specifically the RFC 3339 extension format, which is slightly more strict than the ISO specification. For instance, it requires four digit years and specifies that T is used as a separator between the data and time portions of the timestamp.

The ordering of keys is also not important, they can occur in any order. In fact, if you are designing a device which consumes Signal K data, it is important to remember that the JSON standard does not guarantee the order of properties in an object. You MUST NOT rely on the data you receive to always be in the same order within a Signal K message.

The full format is most useful for getting the initial state of a Signal K system, for example when a display device first connects to the network or for refreshing a device‘s state when it loses a network connection.

However sending the full data model is wasteful of both bandwidth and CPU, especially when there is a large amount of available data, or the consuming device is only interested in a small portion of it. In the majority of cases, it is preferable to only exchange small, specific portions of the data.

Delta Format

By far, the most commonly produced Signal K format is the delta format. Conceptually, the delta is an update to an existing Signal K data model. A device consuming deltas could either build up a view of the Signal K full tree by consuming and combining deltas or it could request from a Signal K server the current full tree model and apply deltas to that as they are received. It is also entirely possible for a device to remain essentially stateless and treat Signal K deltas as independent packets of data, much the same way as it would handle NMEA sentences or PGNs.

An example delta message is presented below.

{
  "context": "vessels.urn:mrn:imo:mmsi:234567890",
  "updates": [
    {
      "source": {
        "label": "N2000-01",
        "type": "NMEA2000",
        "src": "017",
        "pgn": 127488
      },
      "timestamp": "2010-01-07T07:18:44Z",
      "values": [
        {
          "path": "propulsion.0.revolutions",
          "value": 16.341667
        },
        {
          "path": "propulsion.0.boostPressure",
          "value": 45500
        }
      ]
    }
  ]
}

The top level of a delta message contains an updates property and an optional context property.

{
  "context": "vessels.urn:mrn:imo:mmsi:234567890",
  "updates": [...]
}

The optional context property roots the updates to a particular location in the Signal K tree. If context is missing it is assumed that the data is related to the self context. The self context is the vessel object which the self property of the full model points to.

Context is a path from the root of the full tree to the container object, which for vessel related data must refer to a vessel directly under vessels. The delimiter in the context path is . (period). In this case the context is vessels.urn:mrn:imo:mmsi:234567890. All subsequent data is relative to that location.

The updates property holds a JSON array of update objects, each of which may have a source property, a timestamp property and an array of values containing one or more value objects.

{
  "source": {
    "label": "N2000-01",
    "type": "NMEA2000",
    "src": "115",
    "pgn": 128267
  },
  "timestamp": "2014-08-15T16:00:00.081Z",
  "values": [
    {
      "path": "navigation.courseOverGroundTrue",
      "value": 2.971
    },
    {
      "path": "navigation.speedOverGround",
      "value": 3.85
    }
  ]
}

An update has a single source value and it applies to each of the values items. In cases where data can only come from a single source, such as an NMEA 0183 talker connected to a serial port, then the source may be omitted. However, if the delta is being passed on by a Signal K server or multiplexer then source must be filled in by the server so that downstream consumers can discern where the update comes from.

In cases where a Signal K producer does not have access to a real time clock or GPS time then timestamp should be omitted. Elements in the Signal K processing chain-such as a server receiving data from a producer-should fill in timestamp if it is missing in the incoming delta message.

Each value item is then simply a pair of path and value. The path must be a leaf path: it must be a path to a leaf the of the full model. A leaf is where the actual value of the Signal K property is and where timestamp, $source and values properties are in the full model. The value is often a scalar-a single numeric value, as in the example above-but it can also be an object. For example a navigation.position value would be an object like {"latitude": -41.2936935424, "longitude": 173.2470855712}.

There are some static properties in the full model that lack the support for multiple values and metadata such as source and timestamp. An example is a vessel‘s name, directly under the vessel‘s root. This static data may appear in the delta stream, for example when received in AIS transmission. In this case the value should be the subtree of the full model, starting from the vessel's root, with just the relevant parts, and the path must be empty, indicating that the value should be merged to the full model mounted where the delta‘s context property points:

{
  "context": "vessels.urn:mrn:imo:mmsi:234567890",
  "updates": [
    {
      "source": {...},
      "timestamp": "2014-08-15T19:02:31.507Z",
      "values": [
        {
          "path": "",
          "value": {
            "name": "WRANGO"
          }
        }
      ]
    }
  ]
}

Data Quality

Data transmitted in Signal K format is assumed to be corrected for known sensor inaccuracies such as wind angle offset due to misalignment of a masthead unit on the mast, but there is no guarantee that data is accurate, or within certain bounds. Different sources will have different data quality and normal vigilance is always required.

Missing or Invalid Data

A sensor or gateway/server may want to send a message indicating known invalid data or the fact that the sensor is functioning but can not provide data, for example when a depth sensor has no bottom fix. In this case the value must be JSON null in the delta message and the server must return the value as a JSON null in the REST API.

Message Integrity

Many messaging systems specify checksums or other forms of message integrity checking. Signal K assumes a reliable transport will guarantee a valid message. This is true of TCP/IP and some other transports but not always the case. For other transports (e.g. RS-232 serial) a specific extended data format will apply, which is suited to that transport. Hence at the message level no checksum or other tests need to be made.

Encoding/Decoding

The JSON message format is supported across most programming environments and can be handled with any convenient library.

On micro-controllers with limited RAM it may be necessary to read and write Signal K data using a streaming process rather than reading the entire message into RAM before processing. There is an implementation of Signal K JSON streaming on an Arduino Mega (4K RAM) in the related Freeboard project.

results matching ""

    No results matching ""