{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://trinetrahq.com/schemas/sangam/1.0/common.schema.json",
  "title": "SANGAM common definitions",
  "description": "Building blocks shared by every SANGAM record. Positions are WGS84 decimal degrees. Local covariances are in an east-north frame centred on the reported point, in square metres. Bearings are degrees clockwise from true north. Times are ISO-8601 UTC with a trailing Z.",
  "$defs": {
    "schemaVersion": {
      "description": "Schema version of the record. Minor versions only add optional fields.",
      "type": "string",
      "pattern": "^1\\.[0-9]+$"
    },
    "id": {
      "description": "Opaque identifier, unique within its record type and producer.",
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
    },
    "time": {
      "description": "ISO-8601 UTC timestamp with Z suffix, optional fraction up to microseconds.",
      "type": "string",
      "format": "date-time",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]{1,6})?Z$"
    },
    "lat": {"type": "number", "minimum": -90, "maximum": 90, "description": "Latitude, decimal degrees (WGS84)."},
    "lon": {"type": "number", "minimum": -180, "maximum": 180, "description": "Longitude, decimal degrees (WGS84)."},
    "latLon": {
      "description": "A [lat, lon] pair in decimal degrees.",
      "type": "array",
      "minItems": 2,
      "maxItems": 2,
      "items": {"type": "number"}
    },
    "cov2": {
      "description": "2x2 covariance [[ee, en], [ne, nn]] in an east-north frame. Must be symmetric and positive semi-definite (checked by the SDK).",
      "type": "array",
      "minItems": 2,
      "maxItems": 2,
      "items": {"type": "array", "minItems": 2, "maxItems": 2, "items": {"type": "number"}}
    },
    "point": {
      "description": "A geographic point, for example a sensor location.",
      "type": "object",
      "required": ["lat", "lon"],
      "properties": {
        "lat": {"$ref": "#/$defs/lat"},
        "lon": {"$ref": "#/$defs/lon"},
        "height_m": {"type": "number", "description": "Height above local ground or water, metres."}
      },
      "additionalProperties": false
    },
    "position": {
      "description": "Point estimate with optional east-north covariance in m^2.",
      "type": "object",
      "required": ["lat", "lon"],
      "properties": {
        "lat": {"$ref": "#/$defs/lat"},
        "lon": {"$ref": "#/$defs/lon"},
        "cov_m2": {"$ref": "#/$defs/cov2"}
      },
      "additionalProperties": false
    },
    "positionWithCov": {
      "description": "Point estimate with mandatory east-north covariance in m^2.",
      "type": "object",
      "required": ["lat", "lon", "cov_m2"],
      "properties": {
        "lat": {"$ref": "#/$defs/lat"},
        "lon": {"$ref": "#/$defs/lon"},
        "cov_m2": {"$ref": "#/$defs/cov2"}
      },
      "additionalProperties": false
    },
    "polar": {
      "description": "Sensor-relative measurement: range and bearing from origin with 1-sigma errors. Omit range_m (or set null) for a bearing-only measurement.",
      "type": "object",
      "required": ["bearing_deg", "sigma_bearing_deg", "origin"],
      "properties": {
        "range_m": {"type": ["number", "null"], "minimum": 0, "description": "Range from origin, metres. Omit or null for a bearing-only measurement."},
        "sigma_range_m": {"type": ["number", "null"], "exclusiveMinimum": 0, "description": "1-sigma range error, metres. Required with range_m."},
        "bearing_deg": {"type": "number", "minimum": 0, "exclusiveMaximum": 360, "description": "Bearing from origin, degrees clockwise from true north."},
        "sigma_bearing_deg": {"type": "number", "exclusiveMinimum": 0, "description": "1-sigma bearing error, degrees."},
        "origin": {"$ref": "#/$defs/point"}
      },
      "additionalProperties": false
    },
    "velocity": {
      "description": "Velocity over ground in m/s, east and north components, optional covariance in (m/s)^2.",
      "type": "object",
      "required": ["east_ms", "north_ms"],
      "properties": {
        "east_ms": {"type": "number", "description": "Eastward velocity, m/s."},
        "north_ms": {"type": "number", "description": "Northward velocity, m/s."},
        "cov_m2s2": {"$ref": "#/$defs/cov2"}
      },
      "additionalProperties": false
    },
    "label": {
      "description": "Object class. 'unknown' is allowed in sensor reports; fused tracks always carry one of the five object classes.",
      "enum": ["small_boat", "large_vessel", "person", "animal", "debris", "unknown"]
    },
    "classification": {
      "type": "object",
      "required": ["label", "confidence"],
      "properties": {
        "label": {"$ref": "#/$defs/label"},
        "confidence": {"type": "number", "minimum": 0, "maximum": 1, "description": "Confidence in the label, 0 to 1. Under 0.5 counts as unclassified."}
      },
      "additionalProperties": false
    },
    "sensorType": {
      "description": "radar, eo (visible or low-light camera), ir (thermal camera), acoustic, ais (AIS-like transponder), fence (perimeter intrusion sensor).",
      "enum": ["radar", "eo", "ir", "acoustic", "ais", "fence"]
    },
    "level": {
      "description": "Alert level, same words across Trinetra.",
      "enum": ["normal", "watch", "warning", "danger"]
    },
    "attributes": {
      "description": "Flat key-value extras (vendor fields, SNR, RCS). Keys are snake_case; values are scalars.",
      "type": "object",
      "propertyNames": {"pattern": "^[a-z][a-z0-9_]*$"},
      "additionalProperties": {"type": ["string", "number", "boolean", "null"]}
    }
  }
}
