RFC-0013: D0 Device Layer — Raw Telemetry Schema
Motivation
Telemachus Core v0.2 (RFC-0001) defines a unified 10 Hz schema for mobility data. In practice, the distinction between raw device output and pipeline-enriched data is not formalized. This leads to:
- RS3 exports containing
road_type,event,target_speed— columns that a real device never sends - Real device data (Teltonika FMC880) missing fields the schema expects (
heading,hdop) - No clear contract for what a “D0 file” should contain vs. what a “D1 file” adds
This RFC introduces a strict layered data model (D0→D4) where each layer has an explicit column contract.
Principle
D0 = raw device output. No enrichment, no interpretation, no external data.
A D0 file contains only what the telematics device physically measures and transmits. Any column derived from external sources (maps, DEM, algorithms) belongs to D1 or higher.
D0 Column Specification
Mandatory Fields
Every D0-compliant file MUST contain these columns:
| Column | Type | Unit | Source | Frequency | Description |
|---|---|---|---|---|---|
ts | datetime | UTC ISO 8601 | Device clock | IMU rate | Timestamp of the measurement frame |
lat | float64 | degrees WGS84 | GNSS | GNSS rate | Latitude. NaN between GNSS ticks if GNSS rate < IMU rate |
lon | float64 | degrees WGS84 | GNSS | GNSS rate | Longitude. NaN between GNSS ticks |
speed_mps | float32 | m/s | GNSS | GNSS rate | Ground speed. NaN between GNSS ticks |
ax_mps2 | float32 | m/s² | IMU accel | IMU rate | Longitudinal acceleration (+ = forward) |
ay_mps2 | float32 | m/s² | IMU accel | IMU rate | Lateral acceleration (+ = left) |
az_mps2 | float32 | m/s² | IMU accel | IMU rate | Vertical acceleration (~9.81 at rest) |
device_id | string | — | Config | per-file | Unique device identifier |
trip_id | string | — | Config | per-file | Unique trip identifier |
Recommended Fields (GNSS metadata)
These fields SHOULD be present when the hardware provides them:
| Column | Type | Unit | Description |
|---|---|---|---|
heading_deg | float32 | degrees [0, 360) | Course over ground (COG). NaN when stationary |
altitude_gps_m | float32 | m | GNSS altitude (NMEA GGA). Typical accuracy: 10–30m |
hdop | float32 | — | Horizontal dilution of precision. < 2.0 = good |
n_satellites | int8 | — | Number of satellites used in fix. > 6 = reliable |
Optional Fields (extended IMU)
Present only if the device has the corresponding sensor:
| Column | Type | Unit | Description |
|---|---|---|---|
gx_rad_s | float32 | rad/s | Gyroscope X (roll rate) |
gy_rad_s | float32 | rad/s | Gyroscope Y (pitch rate) |
gz_rad_s | float32 | rad/s | Gyroscope Z (yaw rate) |
If the device lacks a gyroscope (e.g., Teltonika FMC880), these columns MUST be absent or all-NaN. They MUST NOT be filled with zeros.
Optional Fields (vehicle I/O)
| Column | Type | Description |
|---|---|---|
ignition | bool | Vehicle ignition state (digital input) |
odometer_m | float64 | Odometer reading (CAN/OBD, if available) |
Multi-Rate Convention
D0 files are timestamped at the IMU rate (typically 10 Hz). GNSS columns (lat, lon, speed_mps, heading_deg, etc.) contain NaN on rows where no GNSS fix is available. This is called “multi-rate D0.”
Example at 10 Hz IMU + 1 Hz GNSS:
ts, lat, lon, speed_mps, ax_mps2, ay_mps2, az_mps2
2025-01-01T08:00:00.000Z, 49.3347, 1.3830, 5.2, 0.12, 0.03, 9.81
2025-01-01T08:00:00.100Z, NaN, NaN, NaN, 0.15, -0.01, 9.80
2025-01-01T08:00:00.200Z, NaN, NaN, NaN, 0.11, 0.02, 9.82
...
2025-01-01T08:00:01.000Z, 49.3348, 1.3831, 5.3, 0.13, 0.01, 9.81
What D0 MUST NOT Contain
The following columns are explicitly excluded from D0. They belong to higher layers:
| Column | Correct Layer | Reason |
|---|---|---|
road_type | D1 (Road Context) | Requires map data |
speed_limit_kmh | D1 (Road Context) | Requires map data |
altitude_dem_m | D1 (DEM Enrichment) | Requires external DEM |
slope_pct | D1 (DEM Enrichment) | Derived from DEM |
event | D2 (Event Detection) | Algorithmic output |
sqs_global | D1 (Signal Quality) | Computed metric |
lat_matched | D1 (Map Matching) | Requires OSRM |
target_speed | D3 (Indicators) | Model output |
Layer Model (D0→D4)
| Layer | Name | Input | Output | Description |
|---|---|---|---|---|
| D0 | Device | Hardware | Raw CSV/Parquet | What the device sends |
| D1 | Cleaned & Contextualized | D0 | Enriched D0 + new columns | GPS interpolation, IMU calibration, map matching, DEM, SQS |
| D2 | Events & Situations | D1 | D1 + event column + event table | Driving events, curve classification |
| D3 | Indicators | D1 + D2 | KPI JSON per trip | Driving score, distances, event rates, Road Genome |
| D4 | Fleet Aggregation | D3 × N | Panel statistics | Benchmarking, trends, anomalies |
D1 — Columns Added
| Column | Stage | Description |
|---|---|---|
lat (interpolated) | GPS Upsampling | NaN gaps filled (linear or kinematic) |
interpolated | GPS Upsampling | Boolean: true if this row was interpolated |
dist_m | GPS Cleaning | Incremental haversine distance |
lat_matched, lon_matched | Map Matching | OSRM-snapped position |
road_type | Road Context | OSM road classification |
speed_limit_kmh | Road Context | Regulatory speed limit |
urban | Road Context | Urban zone (boolean) |
altitude_dem_m | DEM Enrichment | SRTM/IGN altitude |
slope_pct | DEM Enrichment | Grade (%) |
sqs_global | Signal Quality | Score 0–1 |
D2 — Columns Added
| Column | Description |
|---|---|
event | Event type per row (empty if none) |
curve_radius_m | Instantaneous curve radius |
curve_class | Classification: hairpin / sharp / moderate / gentle / straight |
D2 — Event Types
| Code | Signal | Default Threshold | Category |
|---|---|---|---|
HARSH_BRAKE | ax | < -3.0 m/s² | Driving |
HARSH_ACCEL | ax | > +2.5 m/s² | Driving |
SHARP_TURN | gz or ay | > 0.3 rad/s or > 5.0 m/s² | Driving |
SPEED_BUMP | az_delta | 3.0–5.0 m/s² | Infrastructure |
POTHOLE | az_delta | > 5.0 m/s² | Infrastructure |
CURB | ay + az | ay > 2.5 and az_delta > 3.0 | Infrastructure |
DOOR_OPEN | gy | > 3.0 rad/s (stationary) | Logistics |
STOP | speed | < 0.3 m/s for ≥ 5s | Kinematic |
D3 — Indicators
| KPI | Unit | Description |
|---|---|---|
distance_km | km | Total trip distance |
duration_s | s | Total trip duration |
driving_score | 0–100 | Exponential score based on events/km |
event_rate_per_km | events/km | Weighted event frequency |
sqs | 0–1 | Signal Quality Score |
D3 — Road Genome
Per-segment (100m) profile:
| Field | Description |
|---|---|
kappa_mean | Mean curvature κ (rad/m) |
alpha_mean | Mean slope α (fraction) |
v_cible_kmh | Target speed from v_max × exp(−λ₁ |
risk | Risk index |
road_type | Dominant road type |
Hardware Mapping
Teltonika FMC880 (Exobox DL100)
| Device field | D0 column | Notes |
|---|---|---|
| Timestamp | ts | UTC |
| Latitude | lat | 1 Hz |
| Longitude | lon | 1 Hz |
| Speed | speed_mps | Convert from km/h |
| Course | heading_deg | COG 0–360° |
| Altitude | altitude_gps_m | NMEA GGA |
| HDOP | hdop | — |
| Satellites | n_satellites | — |
| Accel X/Y/Z | ax_mps2 / ay_mps2 / az_mps2 | 10 Hz |
| Gyro | — | Not available on FMC880 |
| Ignition | ignition | Digital input |
RoadSimulator3 (synthetic)
| RS3 field | D0 column | Notes |
|---|---|---|
| timestamp | ts | 10 Hz uniform |
| lat, lon | lat, lon | NaN between ticks if multi-rate |
| speed | speed_mps | — |
| heading | heading_deg | Computed from trajectory |
| acc_x/y/z | ax_mps2 / ay_mps2 / az_mps2 | Includes gravity on az |
| gyro_x/y/z | gx_rad_s / gy_rad_s / gz_rad_s | NaN if disabled |
Note: RS3 also exports
road_type,event,target_speedetc. These are ground truth metadata for validation (see P014), NOT part of D0.
Validation Rules
A D0 file is valid if:
- All mandatory columns are present
tsis monotonically increasingaz_mps2mean at rest ≈ 9.81 ± 1.0 m/s² (gravity check)lat/lonare within [-90, 90] / [-180, 180] when not NaN- No enrichment columns are present (see §3.6)
Relationship to Other RFCs
| RFC | Relationship |
|---|---|
| RFC-0001 (Core v0.2) | This RFC formalizes the D0 layer implicit in RFC-0001 |
| RFC-0003 (Events) | D2 event types and thresholds |
| RFC-0005 (Quality) | SQS dimensions feed from D0 quality fields (hdop, n_satellites) |
| RFC-0007 (Manifest) | D0 manifest should declare device_model and sensor capabilities |
| RFC-0009 (RS3 Pipeline) | RS3 produces D0-compliant output + separate ground truth |
Migration from v0.1
Existing Telemachus files that mix D0 and D1 columns remain valid for processing. The pipeline SHOULD accept both formats. New exports SHOULD conform to the layered model.
References
- Telemachus Core v0.2 (RFC-0001)
- P013 — IMU Rectification Without Gyroscope (Edet, 2026)
- P014 — Ground Truth Validation (Edet, 2026)
- Teltonika FMC880 Technical Specification
- NMEA 0183 Standard (GGA, RMC sentences)
End of RFC-0013.