{
  "$defs": {
    "OdidBasicID": {
      "description": "Basic ID message: identifies the unmanned aircraft (UA) or, for flattened\naviation traffic, the manned aircraft.",
      "properties": {
        "UAType": {
          "description": "UA type (enum): 0=None, 1=Aeroplane (fixed wing), 2=Helicopter/Multirotor, 3=Gyroplane, 4=Hybrid lift (VTOL fixed wing), 5=Ornithopter, 6=Glider, 7=Kite, 8=Free balloon, 9=Captive balloon, 10=Airship, 11=Free-fall parachute, 12=Rocket, 13=Tethered powered aircraft, 14=Ground obstacle, 15=Other. For aviation traffic the type is derived from the ADS-B/UAT emitter category or OGN aircraft type where available (0 or 1 when it is not).",
          "title": "Uatype",
          "type": "integer"
        },
        "IDType": {
          "description": "Type of UASID (enum): 0=None, 1=Serial number (ANSI/CTA-2063-A), 2=CAA registration ID, 3=UTM-assigned UUID, 4=Specific session ID; 5=ICAO 24-bit address (Dronetag aviation extension; 5-15 are reserved in ASTM F3411). Aviation traffic uses 2 with the ICAO hex address for ADS-B, 5 for OGN/FLARM/ADS-L, and 0 for UAT.",
          "title": "Idtype",
          "type": "integer"
        },
        "UASID": {
          "description": "UAS identifier, up to 20 characters, decoded as UTF-8 with non-printable bytes dropped. Interpretation depends on IDType. For aviation traffic this is the ICAO 24-bit address (6 hex digits) or the OGN/FLARM device address.",
          "title": "Uasid",
          "type": "string"
        }
      },
      "required": [
        "UAType",
        "IDType",
        "UASID"
      ],
      "title": "OdidBasicID",
      "type": "object"
    },
    "OdidData": {
      "description": "Top-level parsed & datified Open Drone ID payload.\n\nA single decoded transmission (a Message Pack, or one standalone message). Any\nmessage type absent from the transmission is ``None`` (or an empty list for\n``BasicID``). ``Auth`` data is intentionally not modelled: it is dropped by\n``nullify_opendroneid_dict`` because it is only meaningful together with the\nraw message bytes.\n\nAviation traffic (ADS-B, ADS-L, UAT, FLARM, OGN) received by Dronetag sensors\nis flattened into this same structure and always arrives as a Message Pack\nwith ``BasicID``, ``Location`` and usually ``SelfID``; ``System`` and\n``OperatorID`` are always ``None`` for aviation.",
      "properties": {
        "BasicID": {
          "description": "Basic ID messages. A UAS may broadcast up to two with distinct IDType (e.g. a serial number plus a session ID).",
          "items": {
            "$ref": "#/$defs/OdidBasicID"
          },
          "title": "Basicid",
          "type": "array"
        },
        "Location": {
          "anyOf": [
            {
              "$ref": "#/$defs/OdidLocation"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Location/Vector message, or None if not present in the transmission."
        },
        "SelfID": {
          "anyOf": [
            {
              "$ref": "#/$defs/OdidSelfID"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Self ID message, or None if not present in the transmission."
        },
        "System": {
          "anyOf": [
            {
              "$ref": "#/$defs/OdidSystem"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "System message, or None if not present in the transmission."
        },
        "OperatorID": {
          "anyOf": [
            {
              "$ref": "#/$defs/OdidOperatorID"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Operator ID message, or None if not present in the transmission."
        }
      },
      "title": "OdidData",
      "type": "object"
    },
    "OdidLocation": {
      "description": "Location/Vector message: the aircraft's position, altitude and motion.",
      "properties": {
        "Status": {
          "description": "Operational status (enum): 0=Undeclared, 1=Ground, 2=Airborne, 3=Emergency, 4=Remote ID system failure.",
          "title": "Status",
          "type": "integer"
        },
        "Direction": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Course over ground in degrees relative to true North, 0 <= x < 360. None if invalid/unknown (wire sentinel 361).",
          "title": "Direction"
        },
        "SpeedHorizontal": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Ground speed in m/s (>= 0, capped at 254.25). None if invalid/unknown (wire sentinel 255).",
          "title": "Speedhorizontal"
        },
        "SpeedVertical": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Vertical speed in m/s (up positive, capped at +/-62). None if invalid/unknown (wire sentinel 63).",
          "title": "Speedvertical"
        },
        "Latitude": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "WGS84 latitude in degrees. None if invalid/unknown (wire sentinel 0).",
          "title": "Latitude"
        },
        "Longitude": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "WGS84 longitude in degrees. None if invalid/unknown (wire sentinel 0).",
          "title": "Longitude"
        },
        "AltitudeBaro": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Barometric pressure altitude in meters (ref 29.92 inHg / 1013.25 mb). None if invalid/unknown (wire sentinel -1000).",
          "title": "Altitudebaro"
        },
        "AltitudeGeo": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Geodetic altitude in meters (WGS84-HAE). None if invalid/unknown (wire sentinel -1000).",
          "title": "Altitudegeo"
        },
        "HeightType": {
          "description": "Reference for Height (enum): 0=above takeoff, 1=above ground level.",
          "title": "Heighttype",
          "type": "integer"
        },
        "Height": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Height above the HeightType reference in meters. None if invalid/unknown (wire sentinel -1000).",
          "title": "Height"
        },
        "HorizAccuracy": {
          "description": "Horizontal accuracy bucket (enum): 0=unknown, 1=10NM (18.52 km), 2=4NM (7.408 km), 3=2NM (3.704 km), 4=1NM (1.852 km), 5=0.5NM (926 m), 6=0.3NM (555.6 m), 7=0.1NM (185.2 m), 8=0.05NM (92.6 m), 9=30 m, 10=10 m, 11=3 m, 12=1 m; 13-15 reserved.",
          "title": "Horizaccuracy",
          "type": "integer"
        },
        "VertAccuracy": {
          "description": "Vertical (geodetic) accuracy bucket (enum): 0=unknown, 1=150 m, 2=45 m, 3=25 m, 4=10 m, 5=3 m, 6=1 m; 7-15 reserved.",
          "title": "Vertaccuracy",
          "type": "integer"
        },
        "BaroAccuracy": {
          "description": "Barometric altitude accuracy bucket; same enum as VertAccuracy.",
          "title": "Baroaccuracy",
          "type": "integer"
        },
        "SpeedAccuracy": {
          "description": "Speed accuracy bucket (enum): 0=unknown, 1=10 m/s, 2=3 m/s, 3=1 m/s, 4=0.3 m/s; 5-15 reserved.",
          "title": "Speedaccuracy",
          "type": "integer"
        },
        "TSAccuracy": {
          "description": "Timestamp accuracy bucket (enum): 0=unknown, otherwise N x 0.1 s (1=0.1 s ... 15=1.5 s).",
          "title": "Tsaccuracy",
          "type": "integer"
        },
        "Timestamp": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "UTC time of the location fix as a datetime (resolved by datify), stored and serialized as naive UTC (ISO 8601 without offset suffix). Transmitted on the wire as seconds after the full hour; None if invalid/unknown or outside the accepted time window.",
          "title": "Timestamp"
        },
        "TimestampEstimated": {
          "default": false,
          "description": "True when Timestamp was substituted with the processing/receive time because the transmitted value was invalid or out of range (non-strict mode).",
          "title": "Timestampestimated",
          "type": "boolean"
        }
      },
      "required": [
        "Status",
        "HeightType",
        "HorizAccuracy",
        "VertAccuracy",
        "BaroAccuracy",
        "SpeedAccuracy",
        "TSAccuracy"
      ],
      "title": "OdidLocation",
      "type": "object"
    },
    "OdidOperatorID": {
      "description": "Operator ID message.",
      "properties": {
        "OperatorIdType": {
          "description": "Type of OperatorId (enum): 0=Operator ID; 1-200 reserved, 201-255 private.",
          "title": "Operatoridtype",
          "type": "integer"
        },
        "OperatorId": {
          "description": "Operator identifier, up to 20 characters, decoded as UTF-8.",
          "title": "Operatorid",
          "type": "string"
        }
      },
      "required": [
        "OperatorIdType",
        "OperatorId"
      ],
      "title": "OdidOperatorID",
      "type": "object"
    },
    "OdidSelfID": {
      "description": "Self ID message: free-text description supplied by the operator.\n\nFor aviation traffic this carries the strongest human-readable identifier the\nreceiver decoded: flight number, tail number or callsign.",
      "properties": {
        "DescType": {
          "description": "Type of Desc (enum): 0=free text, 1=emergency clarification, 2=extended-status clarification; 3-200 reserved, 201-255 private. Aviation traffic always uses 0.",
          "title": "Desctype",
          "type": "integer"
        },
        "Desc": {
          "description": "Free-form description text, up to 23 characters, decoded as UTF-8. For aviation traffic: the flight number or tail number for ADS-B (e.g. 'MNB9406'), the flight plan ID for UAT, and the registration or callsign for OGN/FLARM.",
          "title": "Desc",
          "type": "string"
        }
      },
      "required": [
        "DescType",
        "Desc"
      ],
      "title": "OdidSelfID",
      "type": "object"
    },
    "OdidSystem": {
      "description": "System message: operator location and group/area information.\n\nNever present for aviation traffic (ADS-B/ADS-L/UAT/FLARM/OGN carry no\noperator information).",
      "properties": {
        "OperatorLocationType": {
          "description": "Source of the operator location (enum): 0=takeoff, 1=live GNSS (dynamic), 2=fixed.",
          "title": "Operatorlocationtype",
          "type": "integer"
        },
        "ClassificationType": {
          "description": "Classification scheme (enum): 0=undeclared, 1=EU. CategoryEU/ClassEU are only meaningful when this is 1 (EU).",
          "title": "Classificationtype",
          "type": "integer"
        },
        "OperatorLatitude": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "WGS84 operator latitude in degrees. None if invalid/unknown (wire sentinel 0).",
          "title": "Operatorlatitude"
        },
        "OperatorLongitude": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "WGS84 operator longitude in degrees. None if invalid/unknown (wire sentinel 0).",
          "title": "Operatorlongitude"
        },
        "AreaCount": {
          "description": "Number of aircraft in the group/swarm (1 for a single UA).",
          "title": "Areacount",
          "type": "integer"
        },
        "AreaRadius": {
          "description": "Group operational area radius in meters (0-2550). Default 0.",
          "title": "Arearadius",
          "type": "integer"
        },
        "AreaCeiling": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Group operational area ceiling in meters (WGS84-HAE). None if invalid/unknown (wire sentinel -1000).",
          "title": "Areaceiling"
        },
        "AreaFloor": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Group operational area floor in meters (WGS84-HAE). None if invalid/unknown (wire sentinel -1000).",
          "title": "Areafloor"
        },
        "CategoryEU": {
          "description": "EU UA category (enum, only if ClassificationType=EU): 0=undeclared, 1=Open, 2=Specific, 3=Certified; 4-15 reserved.",
          "title": "Categoryeu",
          "type": "integer"
        },
        "ClassEU": {
          "description": "EU UA class (enum, only if ClassificationType=EU): 0=undeclared, 1=Class 0, 2=Class 1, ... 7=Class 6; 8-15 reserved.",
          "title": "Classeu",
          "type": "integer"
        },
        "OperatorAltitudeGeo": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Operator geodetic altitude in meters (WGS84-HAE). None if invalid/unknown (wire sentinel -1000).",
          "title": "Operatoraltitudegeo"
        },
        "Timestamp": {
          "anyOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "UTC time of the System message as a datetime (resolved by datify), stored and serialized as naive UTC (ISO 8601 without offset suffix). Transmitted on the wire relative to 2019-01-01 00:00:00 UTC; None if invalid/unknown or outside the accepted time window.",
          "title": "Timestamp"
        },
        "TimestampEstimated": {
          "default": false,
          "description": "True when Timestamp was substituted with the processing/receive time because the transmitted value was invalid or out of range (non-strict mode).",
          "title": "Timestampestimated",
          "type": "boolean"
        }
      },
      "required": [
        "OperatorLocationType",
        "ClassificationType",
        "AreaCount",
        "AreaRadius",
        "CategoryEU",
        "ClassEU"
      ],
      "title": "OdidSystem",
      "type": "object"
    },
    "Registration": {
      "description": "Maker/model resolved from a Remote ID serial via the offline rid.db.\n\nOnly available on Scout Sensor+ with a valid license; other units always\nsend ``null``. `certain` is False when the serial only matched a\nbounding-box range - the maker is right, the model is the registry's best\nguess.",
      "properties": {
        "maker": {
          "description": "Manufacturer of the drone or Remote ID module (e.g. 'DJI').",
          "title": "Maker",
          "type": "string"
        },
        "model": {
          "description": "Model name as registered in the FAA Declaration of Compliance registry.",
          "title": "Model",
          "type": "string"
        },
        "type": {
          "description": "What the serial belongs to: 'uav' (the drone itself) or 'rid_module' (an add-on Remote ID broadcast module).",
          "title": "Type",
          "type": "string"
        },
        "certain": {
          "description": "True when the serial matched an exact registry entry. False when it only fell inside a registered serial range - the maker is right, the model is the registry's best guess.",
          "title": "Certain",
          "type": "boolean"
        }
      },
      "required": [
        "maker",
        "model",
        "type",
        "certain"
      ],
      "title": "Registration",
      "type": "object"
    },
    "Tech": {
      "description": "Receiving technology of a message.\n\nB4=Bluetooth 4 legacy advertising, B5=Bluetooth 5 Long Range, WB=Wi-Fi\nBeacon, WN=Wi-Fi NaN (all four are drone Remote ID); AL=ADS-L 868 MHz,\nAB=ADS-B 1090 MHz, OG=OGN 868 MHz, UT=UAT 978 MHz, FL=FLARM 868 MHz\n(all five are aviation, received by the optional aviation modules).",
      "enum": [
        "B4",
        "B5",
        "WB",
        "WN",
        "AL",
        "AB",
        "OG",
        "UT",
        "FL"
      ],
      "title": "Tech",
      "type": "string"
    }
  },
  "$id": "https://help.dronetag.cz/downloads/scout-json-odid.schema-v1.0.json",
  "description": "A single observation in the JSON+ODID format.\n\nOne decoded drone Remote ID or aviation (ADS-B/ADS-L/UAT/FLARM/OGN)\ntransmission with reception metadata. Sent by the JSON forwarders as one\nJSON object per line (JSONL), or as an array/joined lines when batching is\nconfigured. Invalid/unknown values are ``null``, never in-band sentinels.",
  "properties": {
    "sn": {
      "description": "Serial number of the Scout that received the message (e.g. 'D11234567812345678').",
      "title": "Sn",
      "type": "string"
    },
    "mac": {
      "description": "Transmitter MAC address formatted 'xx:yy:zz:aa:bb:cc'. Aviation traffic has no MAC, so one is synthesized from the zero-padded 24-bit ICAO/device address; treat (tech, mac) as the unique key of an aircraft.",
      "title": "Mac",
      "type": "string"
    },
    "counter": {
      "description": "Message counter of the receiving module. Always 0 for aviation traffic.",
      "title": "Counter",
      "type": "integer"
    },
    "rssi": {
      "description": "Received signal strength in dBm (negative). For aviation modules the value is estimated from the demodulator signal level. Out-of-range values are forwarded as-received, not clamped.",
      "title": "Rssi",
      "type": "integer"
    },
    "tech": {
      "$ref": "#/$defs/Tech",
      "description": "Receiving technology: 'B4' (Bluetooth legacy), 'B5' (Bluetooth Long Range), 'WB' (Wi-Fi Beacon), 'WN' (Wi-Fi NaN) for drone Remote ID; 'AB' (ADS-B 1090 MHz), 'AL' (ADS-L 868 MHz), 'UT' (UAT 978 MHz), 'FL' (FLARM 868 MHz), 'OG' (OGN 868 MHz) for aviation."
    },
    "odid": {
      "$ref": "#/$defs/OdidData",
      "description": "Parsed Open Drone ID payload with invalid fields nulled. Aviation traffic is flattened into the same structure (identifier in BasicID, flight number/callsign in SelfID, never a System message)."
    },
    "recv_id": {
      "default": 0,
      "description": "Identifier of the chip on the module that received the message (component id and receiver type packed together). 0 for aviation.",
      "title": "Recv Id",
      "type": "integer"
    },
    "module_id": {
      "default": 0,
      "description": "Number of the module/antenna that received the message; corresponds to the antenna position on the box. 0 for aviation.",
      "title": "Module Id",
      "type": "integer"
    },
    "module_type": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Type of the receiving module. Drone Remote ID: the Dronetag device-type id of the module (mainly for internal use; non-Dronetag PCBs are not covered). Aviation: the receiving frequency in MHz (1090, 978 or 868).",
      "title": "Module Type"
    },
    "msg_type": {
      "default": 15,
      "description": "ODID message type (enum): 0=Basic ID, 1=Location, 2=Auth (never forwarded), 3=Self ID, 4=System, 5=Operator ID, 15=Message Pack. Aviation traffic always arrives as 15 (Pack).",
      "title": "Msg Type",
      "type": "integer"
    },
    "noise_floor": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Noise floor in dBm measured by the receiving chip, or null when the chip does not report one (only some Wi-Fi receivers do).",
      "title": "Noise Floor"
    },
    "registration": {
      "anyOf": [
        {
          "$ref": "#/$defs/Registration"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Drone maker/model resolved offline from the Remote ID serial number. Only available on Scout Sensor+ with a valid license; null when the unit is not licensed, the serial is unknown, or the message carries no serial (all aviation traffic)."
    }
  },
  "required": [
    "sn",
    "mac",
    "counter",
    "rssi",
    "tech",
    "odid"
  ],
  "title": "Dronetag Scout JSON+ODID message",
  "type": "object",
  "version": "1.0",
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
