Updated 2026-10-09
Why everything is an integer
SAE J2735 messages are encoded with ASN.1 Unaligned Packed Encoding Rules (UPER), which packs each integer into the fewest bits its declared range allows. There are no floating-point fields. Every physical quantity is therefore stored as an integer in a fixed unit, and the top of each range is usually reserved as a sentinel that means "unavailable" or "unknown". When you read a decoded message you multiply by the unit; when you create one you divide and round. The decoder does the arithmetic in its summary, and the ? help on each field repeats the unit, but you need the table below when you build messages by hand or write code against the API.
The portal's JSON uses the same integers the standard uses. A latitude of 38.8823410° N is written 388823410, not 38.8823410.
Position and geometry
| Field (type) | Unit | Range | Unavailable | Formula | Example |
|---|---|---|---|---|---|
lat (Latitude) | 1/10 microdegree (10⁻⁷ °) | −900000000 … 900000000 | 900000001 | degrees = value ÷ 10 000 000 | 388823410 → 38.8823410° N |
long (Longitude) | 1/10 microdegree | −1799999999 … 1800000000 | 1800000001 | degrees = value ÷ 10 000 000 | −771757920 → 77.1757920° W |
elev / elevation (Elevation) | 0.1 m above the WGS-84 ellipsoid | −4096 … 61439 | −4096 | meters = value ÷ 10 | 1050 → 105.0 m |
semiMajor, semiMinor (PositionalAccuracy) | 0.05 m (one standard deviation) | 0 … 254 | 255 | meters = value × 0.05 | 20 → 1.0 m |
orientation (SemiMajorAxisOrientation) | 0.0054932° (360° ÷ 65535) | 0 … 65534 | 65535 | degrees = value × 360 ÷ 65535 | 9000 → 49.4° |
laneWidth (LaneWidth) | 1 cm | 0 … 32767 | — | meters = value ÷ 100 | 350 → 3.50 m |
Node offsets x, y (Node-XY1 … XY6) | 1 cm from the previous node (first node from the reference point) | ±511 (XY1), ±1023 (XY2), ±2047 (XY3), ±4095 (XY4), ±8191 (XY5), ±32767 (XY6) | — | meters = value ÷ 100; +x east, +y north | node-XY4 x −3000 → 30.00 m west |
node-LatLon (Node-LLmD-64b) | absolute 1/10 microdegree | as Latitude/Longitude | — | — | used when offsets would overflow |
latOffset, lonOffset (OffsetLL-B18, path history) | 1/10 microdegree from the current position | −131072 … 131071 | −131072 | degrees = value ÷ 10 000 000 | −1930 → −0.000193° (about 17 m west at 38° N) |
elevationOffset (VertOffset-B12) | 0.1 m | −2048 … 2047 | −2048 | meters = value ÷ 10 | −4 → −0.4 m |
offsetX, offsetY, offsetZ (SDSM ObjectDistance) | 0.1 m from the sensor reference position | −32767 … 32767 | — | meters = value ÷ 10 | −452 → 45.2 m west |
width, length (VehicleSize) | 1 cm | 0 … 1023 / 0 … 4095 | 0 | meters = value ÷ 100 | 180 × 450 → 1.80 m × 4.50 m |
height (VehicleHeight) | 5 cm | 0 … 127 | — | meters = value × 0.05 | 30 → 1.50 m |
radiusOfCurve (RadiusOfCurvature) | 10 cm, signed (+ right, − left) | −32767 … 32767 | 32767 (straight) | meters = value ÷ 10 | 32767 → straight ahead |
dWidth, dElevation (NodeAttributeSetXY) | 1 cm change from this node on | −512 … 511 | — | meters = value ÷ 100 | 50 → lane widens by 0.50 m |
Node offset encodings are chosen per node. Pick the smallest node-XYn whose range fits both x and y; the map builder does this for you. The MAP article explains the chain of offsets in detail: MAP.
Motion
| Field (type) | Unit | Range | Unavailable | Formula | Example |
|---|---|---|---|---|---|
speed (Speed, Velocity) | 0.02 m/s | 0 … 8190 | 8191 | m/s = value × 0.02; mph = value × 0.044739 | 750 → 15.0 m/s → 33.6 mph |
heading (Heading) | 0.0125° clockwise from true north | 0 … 28799 | 28800 | degrees = value × 0.0125 | 7200 → 90.0° (east) |
heading (CoarseHeading, path history) | 1.5° | 0 … 239 | 240 | degrees = value × 1.5 | 60 → 90° |
heading (Angle, SRM requestor and MAP computed lanes) | 1.5° | 0 … 239 | — | degrees = value × 1.5 | 0 → north |
long, lat (Acceleration) | 0.01 m/s² | −2000 … 2000 | 2001 | m/s² = value ÷ 100 | 15 → 0.15 m/s² |
vert (VerticalAcceleration) | 0.02 g | −127 … 127 | −127 | g = value × 0.02 | 0 → level |
yaw (YawRate) | 0.01 °/s, + clockwise | −32767 … 32767 | — | °/s = value ÷ 100 | −20 → 0.2 °/s left |
angle (SteeringWheelAngle) | 1.5°, + right | −126 … 126 | 127 | degrees = value × 1.5 | 2 → 3° right |
speed (SpeedAdvice, SPaT advisory) | 0.1 m/s | 0 … 500 | — | m/s = value ÷ 10 | 120 → 12 m/s |
transitSchedule (DeltaTime) | 10 s, + ahead of schedule, − behind | −122 … 121 | — | seconds = value × 10 | −3 → 30 s late |
A quick mental check for speed: 0.02 m/s units mean 500 is 10 m/s (22 mph), 1000 is 20 m/s (45 mph), 1500 is 30 m/s (67 mph). The BSM template in the creator uses 750.
Time
| Field (type) | Unit | Range | Unavailable | Formula | Example |
|---|---|---|---|---|---|
secMark, second (DSecond) | 1 ms within the current UTC minute | 0 … 59999 (60000 … 60999 only during a leap second) | 65535 | seconds = value ÷ 1000 | 12345 → 12.345 s past the minute |
moy, timeStamp, minute, startTime (MinuteOfTheYear) | 1 min since 00:00 UTC on January 1 | 0 … 527039 | 527040 | day = value ÷ 1440 (+1), minute of day = value mod 1440 | 403680 → day 281, 08:00 UTC = October 8 in a non-leap year |
startYear (DYear) | 1 year | 0 … 4095 | 0 | — | 2026 |
durationTime (MinutesDuration, TIM) | 1 min | 0 … 32000 (about 22 days) | — | hours = value ÷ 60 | 720 → 12 h |
minEndTime, maxEndTime, likelyTime, startTime, nextTime (TimeMark, SPaT) | 0.1 s past the current UTC hour | 0 … 36000 (35991 … 36000 are used during a leap second) | 36001 = more than an hour away, 36002 = unknown | seconds past the hour = value ÷ 10 | 1450 → 02:25 past the hour |
timeOffset (TimeOffset, path history) | 10 ms before now | 1 … 65534 | 65535 | seconds = value ÷ 100 | 10 → 0.1 s ago |
measurementTime (MeasurementTimeOffset, SDSM) | 1 ms relative to the message time stamp | −1500 … 1500 | — | seconds = value ÷ 1000 | −120 → 120 ms before |
duration (DSecond in SRM/SSM) | 1 ms of requested service | 0 … 65535 | — | seconds = value ÷ 1000 | 20000 → 20 s |
confidence (TimeIntervalConfidence, SPaT) | index 0 … 15 of probability bands | 0 … 15 | — | a table in the standard maps the index to a probability, from about 21 % at 0 to 100 % at 15 | 12 |
sDSMTimeStamp, startDateTime (DDateTime) | year, month, day, hour, minute, second (ms), UTC offset in minutes | — | each part has its own sentinel (month 0, day 0, hour 31, minute 60, second 65535) | — | 2026-10-08 14:22:12.345 |
TimeMark is the one people get wrong most often. It is not a countdown; it is an absolute mark within the current hour. A decoder has to know the hour the message was received to turn 1450 into a clock time, and a vehicle computes "seconds until change" as the mark minus its own time within the hour. The SPaT article has a worked example.
The MinuteOfTheYear fields are UTC and do not know whether the year is a leap year; the receiver resolves that from the current date. The portal's templates use 403680 for 08:00 UTC on October 8, 2026.
Identifiers and counters
| Field (type) | Meaning | Range |
|---|---|---|
msgCnt (MsgCount) | Rolling sequence number, incremented once per message from the same sender; a receiver uses it to detect gaps and replays | 0 … 127, then wraps |
id (TemporaryID) | Four random bytes, written as eight hex characters; vehicles change it about every five minutes together with their certificate | A1B2C3D4 |
revision, msgIssueRevision (MsgCount in MAP/SPaT) | Edition of the intersection description; SPaT and MAP must carry the same revision for the same intersection | 0 … 127 |
id (IntersectionID in IntersectionReferenceID) | The intersection number, unique within the region (RoadRegulatorID 0 … 65535) | 0 … 65535 |
laneID (LaneID) | Lane number unique within one intersection; 0 is reserved | 1 … 255 |
signalGroup (SignalGroupID) | Controller output group that a movement obeys; 0 is reserved | 1 … 255 |
ingressApproach, egressApproach (ApproachID) | Approach number; 0 means unknown | 1 … 15 |
requestID (RequestID) | Chosen by a priority requester and echoed back in the SSM | 0 … 255 |
packetID (UniqueMSGID, TIM) | Nine-byte unique identifier of a TIM | 18 hex characters |
objectID (SDSM) | Tracker handle for a detected object, stable while it is tracked | 0 … 65535 |
ITIS codes
Traveler information uses the SAE J2540-2 International Traveler Information Systems phrase list. Every code is an integer 0 … 65535 grouped into blocks of 256. The blocks you will meet most are 257 … 383 (traffic conditions), 513 … 767 (incidents), 769 … 1023 (closures and lane restrictions), 1025 … 1279 (road work), 4865 … 5119 (weather), 9217 … 9472 (vehicle groups, used by RSM and EVA), 9729 … 9984 (responder groups) and 9985 … 10239 (response equipment).
Numbers inside a phrase are themselves codes: 12544 + n stands for the number n (0 … 255), so a 45 mph speed limit is the sequence 268 (speed-limit) 12589 (45) followed by a short text item for the unit. Free text is allowed as text items of up to 16 characters each, but vehicles display codes more reliably than text. The creator shows the phrase for every code you type, and the TIM article has complete examples.
Bit strings and enumerations
A BIT STRING is written in the portal's JSON as a string of 0 and 1 characters, one per declared bit, with bit 0 first (leftmost). The named bits in the standard are listed in order, so events = 0000000100000 sets bit 7, eventHardBraking. The string must have exactly the declared length (13 bits for VehicleEventFlags, 16 for IntersectionStatusObject, 12 for AllowedManeuvers, 2 for LaneDirection, 9 for ExteriorLights, 16 for HeadingSlice).
A HeadingSlice is 16 bits of 22.5° each, starting at north and going clockwise: bit 0 is 0 … 22.5°, bit 4 is 90 … 112.5°, bit 12 is 270 … 292.5°. A TIM that applies to westbound traffic sets bits 11 and 12 (247.5° … 292.5°), which the templates write as 0000000000011000.
Enumerations are written by name (protected-Movement-Allowed, forwardGears, transit). The ? help lists the names. Octet strings are uppercase hex (BEEF0001). A CHOICE is an object with exactly one key naming the alternative taken ({"node-XY3": {"x": -1200, "y": -200}}).
Worked examples
Convert a BSM core to plain values. lat 388823410, long −771757920, elev 1050, speed 750, heading 7200, accelSet.long 15: 38.8823410° N, 77.1757920° W, 105.0 m, 15.0 m/s (33.6 mph), heading 90° (east), accelerating gently at 0.15 m/s².
Encode a position. You want 39.9185° N, 77.2275° W at 120 m elevation. Multiply the degrees by 10⁷ and round: lat 399185000, long −772275000; elevation 120 × 10 = 1200.
Encode a speed limit of 35 mph for a MAP. The MAP speedLimits entry uses Velocity in 0.02 m/s. 35 mph = 15.65 m/s; 15.65 ÷ 0.02 = 782. Write {"type": "vehicleMaxSpeed", "speed": 782}.
Read a SPaT timing. The message arrived at 14:02:17 UTC. minEndTime 1450, maxEndTime 1550: the current state ends no earlier than 145.0 s past the hour (14:02:25) and no later than 155.0 s (14:02:35), so between 8 and 18 seconds from now.
Encode a lane that runs 30 m due west from the stop line. The first node is at the stop line, say 12.00 m west and 2.00 m south of the reference point: node-XY3 {x: −1200, y: −200} (XY3 fits ±20.47 m). Each following node is 30.00 m further west: node-XY4 {x: −3000, y: 0} (XY4 fits ±40.95 m).
Pick a TimeMark for "ends in 20 seconds". It is 09:31:05 UTC, so 1865.0 s past the hour; the end is at 1885.0 s; write minEndTime 18850.