SPM+ (v2.84)

Concepts
Event Log
Traffic Signal Events (State)
- Detector state changes
- Ring state changes
- Phase state changes
- Cycle state changes
- Movement state changes (unique to Integrator-AI)
Traveler Events (Objects)
- Arrivals: Detailed individual traveler data upon approaching the intersection
- Passages: Traveler information during mid-intersection traversal
- Departures: Traveler data on exit, including travel time, speed, and path summaries
Conditional Events (with more in development as the platform evolves)
- Red light running
- Near misses
- Belated walkers (pedestrians entering late in the cycle)
- Jaywalking
- Impeded departures (e.g., blocked clearance due to queue spillover or gridlock)
Cycles
A cycle is the complete sequence of all phases serviced at an intersection, from the start of one major barrier until its return.
Traditional systems rely on pre-programmed fixed cycle lengths (e.g., 120 seconds), but actual operation often varies due to actuation—gap-outs, max-outs, force-offs, pedestrian calls, or preemption which can shorten or extend cycles significantly.
Integrator-AI accurately detects real-world cycle boundaries in real time by monitoring phase transitions. Specifically, it identifies the start of a new cycle when the controller transitions into one of the phases in the first barrier (typically the start of the coordinated or main-street phases).
Upon detection, it logs a cycle start event and associates all subsequent data (phase changes, movement states, traveler arrivals/passages/departures, etc.) with that cycle.
Phases
A phase is a timed interval within a cycle during which a specific set of compatible movements receives the right-of-way. Phases ensure safe separation of conflicting traffic while allowing concurrent non-conflicting movements.
SPM+ tracks separate states for vehicles and pedestrians since they can change independently. We currently support 16 phases, but this may change in the future.
Rings
A ring indicates which parallel phases are active at any time.
SPM+ will provide details on what state each ring is in, up to 4 rings. The information available for each ring is highly dependent on what kind of traffic signal interface we're using as not all of them can provide the details to track this reliably.
Detectors
A detector is a means to detect the presence of a vehicle or a pedestrian pushing a button to cross.
SPM+ tracks separate states for vehicles and pedestrians since they can change independently. We support up to 128 detectors, but this is highly dependent on the interface to the traffic signal what we have access to.
Movements
Movements are a powerful new feature exclusive to Integrator-AI. A movement represents a specific path that travelers take through an intersection—such as a northbound (NB) left turn, southbound (SB) through movement, or eastbound (EB) pedestrian crossing. This aligns directly with the real-world perspective of drivers, pedestrians, and cyclists as they navigate the intersection.
Historically, traffic engineers have analyzed intersections primarily through the lens of phases—timed intervals that serve one or more compatible movements. This approach can obscure the full picture for individual movements, especially when a single movement is permitted across multiple phases.
A common example is a protected-permissive left turn from the same approach:
- A dedicated left-turn phase (e.g., Phase 5) provides a protected green arrow, allowing the turn without opposing traffic.
- The adjacent through phase (e.g., Phase 2) allows the same left turn on a permissive circular green, requiring drivers to yield to oncoming traffic.
When phases 2 and 5 are both active from the traveler's viewpoint, the green arrow takes priority: drivers treat it as authoritative, proceeding without yielding to oncoming traffic—even if permissive options exist concurrently.
To accurately assess metrics like arrivals on red vs. green (or prohibited vs. permitted) for that left-turn movement, engineers must manually combine data from both phases (2 & 5, etc.). This process is time-consuming, error-prone, and complicates analysis.
Integrator-AI automatically aggregates data across all relevant phases to determine the true state of each movement at any time. Movements can be in one of several states:
- Prohibited (red/no go)
- Permissive (yield to conflicts, e.g., circular green)
- Protected (exclusive right-of-way, e.g., green arrow)
- Permissive after stop (e.g., flashing red)
Additionally, Integrator-AI identifies the authoritative phase—the one that best reflects the driver's experience—prioritizing the most appropriate state. For example, when Phases 2 (permissive) and 5 (protected) are both active, the left turn assigns Phase 5 as authoritative. As Phase 5 terminates, authority dynamically switches to Phase 2. Throughout this period, the left turn remains permitted (never prohibited), transitioning smoothly from protected to permissive until both phases end and it's ultimately prohibited.
This traveler-centric algorithm ensures the analysis mirrors how users actually perceive and interact with the signal and gives engineers a different perspective to tune their intersections from.
The result? Metrics like arrivals on red/green for a specific left turn are instantly accurate and available—no manual merging needed. This streamlines evaluation, optimization, and reporting, delivering a clearer, more intuitive view of intersection performance from the traveler's perspective.
True Movement
True Movement is another innovative feature introduced by Integrator-AI. Traditional traffic signal systems rely on basic detectors (e.g., inductive loops at stop bars) to infer vehicle intent, often making inaccurate assumptions about the path a traveler actually takes.
Common limitations include:
- A vehicle actuating a left-turn lane detector might ultimately go straight if the driver changes their mind—yet the system logs it as a left turn.
- In shared lanes permitting multiple movements (e.g., through + right, or through + left), the controller lacks precise data and defaults to assuming the dominant movement (typically through).
The result? Inaccurate analytics on turning volumes, approach/departure flows, and overall intersection usage.
Integrator-AI overcomes this by leveraging advanced sensing technologies—such as LiDAR and video detection systems—to track individual travelers as they progress through the intersection. By matching arrival (origin lane/approach) to departure (exit lane/direction), it determines the true movement taken, regardless of lane permissions or driver intent changes.
This delivers highly accurate, ground-truth data on traveler flows—enabling better volume counts, origin-destination matrices, performance measures, and optimization decisions based on real behavior rather than assumptions.
Movement Monitor
The Movement Monitor is a dynamic visualization tool in Integrator-AI that provides the chronological progression of a specific movement's state changes over time. You can plot these changes on an interactive graph, showing:
- The movement's state (e.g., prohibited, permissive, protected) throughout the cycle(s).
- Individual traveler (vehicle, pedestrian, or cyclist) interactions: arrivals (on prohibited/permitted), passages through the intersection, and departures.
This provides a clear, time-based view of how travelers experience the movement—revealing delays, queue clearance, arrivals on green, platooning, and potential issues like starvation or excessive waits.
Similar in concept to the Purdue Coordination Diagram (PCD)—a widely used tool for analyzing phase performance and platoon progression along arterials—the Movement Monitor shifts the focus from phases to individual movements. It offers greater detail and accuracy by leveraging true movement data and precise trajectory tracking.
By presenting ground-truth data in this traveler-centric format, the Movement Monitor enables engineers to quickly diagnose issues, evaluate timing effectiveness, and optimize from the user's perspective—far beyond traditional phase-based tools.
Per Object Records
Per-Object Records provide detailed, individual-level data for each traveler (vehicle, pedestrian, or cyclist) traversing the intersection. Unlike aggregated counts or assumptions from traditional detectors, these records capture precise timestamps and locations at key points:
- Arrival: When and where the traveler reaches the approach (e.g., stop bar or advance detection zone), including lane, speed, and signal state (prohibited/permitted).
- Passage: When and where the traveler enters the core of the intersection (conflict zone).
- Departure: When and where the traveler exits, with summaries of travel time, acceleration, path taken, and clearance status.
This high-resolution data enables engineers to reconstruct exact trajectories, measure individual delays, identify anomalies (e.g., hesitation, aggressive acceleration), and calculate metrics like queue lengths, startup lost time, or saturation flow rates with unprecedented accuracy.
By analyzing these per-object records—available via the Event Log, REST API, or detailed reports—engineers gain a forensic-level understanding of traveler-intersection interactions, supporting advanced diagnostics, safety assessments, and data-driven timing refinements.
User Interface
Cycle Page
The cycle page allows you to view data aggregated by cycle. It opens with the most recently completed cycle. You can navigate to other cycles from that starting point
- Total count of arrivals and passages for that cycle
- Start and end time for that cycle
- Duration of the cycle
- Elapsed time since the most recently completed cycle
- Arrival volume by approach heading
- Arrival volume by approach lane
- Arrival volume by heading
- Crosswalk volume by heading
- Departure volume by departure heading
- Departure volume by movement
- Departure volume by heading
- Emssions by approach
- Fuel consumption by approach
- Average travel times by movement broken down by: approach, passage, and total time

- Number of cycles to include before the current cycle
- Movement
- Movement state (permissive) with indication color
- Movement state (protected) with indication color
- Tooltip with details of what the mouse is hovering over
- Cycle barrier
Traveler interaction indicator
- blue - arrival
- green - passage
- grey - departure
- Traveler filter
- Number of cycles to include after the current cycle

- Object information
- Movement made by the object
- Arrival details
- Passage details
- Departure details

- Move to previous cycle
- Move to a specific cycle (you choose the date/time)
- Move to the next cycle
- Move to the most recent cycle
- Prune (delete) data
- App settings
- Generate a report

Settings
- Enable/disable the app
- The traffic signal interface to source state and configurations from. SPM+ will automatically import settings from this signal to generate it's information
- Maximum age of event log records. Records older than this age will automatically be deleted. A value of 0 indicates no age limit.
- Object filter to help with detection input noise
- Tells SPM+ if it should consider unrealized movements in the data it produces. It's recommended to "Include" unless the detection input is very noisy
Data Structure
Phase State Event (1000)
id number | ID of the event: always 1000 |
date number | Unix epoch timestamp, in milliseconds, of when the phase changed |
phase number | Phase number |
vehicle string | The indication that the phase has for vehicles values: none, red, yellow, green, fya, fra, prepare-to-go, flashing-green, flashing-yellow, flashing-red |
pedestrian string | The indication that the phase has for pedestrians values: none, dont-walk, flashing-dont-walk, walk |
Ring State Event (1001)
id number | ID of the event: always 1001 |
date number | Unix epoch timestamp, in milliseconds, of when the ring changed |
ring number | Ring number |
state string | The state of the ring (not resolvable from SDLC interfaces) values: none, min-green, extension, maximum, green-rest, yellow-change, red-clear, red-rest |
termination string | The termination reason the ring has resolved (not resolvable from SDLC interfaces) values: none, force-off, max-out, gap-out |
phase number | The phase that the ring is servicing. 0 if not servicing any phase |
next number | The phase that is coming up next. 0 if not known |
max number | The max value being used from the timing configurations of the phase. For example, a value of 1 indicates MAX1 is being used. A value of 3 would indicate MAX3 is being used. 0 if the value is unknown |
Detector State Event (1002)
id number | ID of the event: always 1002 |
date number | Unix epoch timestamp, in milliseconds, of when the detector changed |
detector number | Detector number |
vehicle string | The state of the vehicle detector values: clear, call |
pedestrian string | The state of the pedestrian detector values: clear, call |
Movement State Event (1003)
id number | ID of the event: always 1003 |
date number | Unix epoch timestamp, in milliseconds, of when the movement changed |
heading string | Movement heading values: nb, sb, eb, wb |
type string | Movement type values: left, through, right, u-turn, pedestrian |
state string | The state of the movement values: prohibited, permissive, protected |
indication string | The indication the movement is showing to drivers values: none, red, yellow, green, prepare-to-go, flashing-green, flashing-yellow, flashing-red, fya, fra, dont-walk, flashing-dont-walk, walk |
phase number | The authoritative phase determining the state of this movement. This will change depending on what movements are allowed and what phases are active. 0 if no phase is currently controlling the movement. If the movement is permitted on phase 2 (permissive) and 5 (protected) and both phases are active, phase 5 will take authority over the movement because it is protected. |
ring number | The ring that the phase is running under. 0 if no phase is currently controlling the movement |
Cycle State Event (1005)
id number | ID of the event: always 1005 |
date number | Unix epoch timestamp, in milliseconds, of when the cycle changed |
Object Movement Arrival Event (2000)
id number | ID of the event: always 2000 |
date number | Unix epoch timestamp, in milliseconds, of when the traveler arrived at the intersection. This is the ingress detection at the advance or stop bar zones, which ever is first. |
heading string | Heading of the approach this traveler arrived at values: nb, sb, eb, wb |
duration number | The time the traveler spent in the approach, in milliseconds |
zone.id number | ID of the final advance or stop bar zone. Stop bar zone always takes precedence |
zone.name string | Name of the final zone |
environmental.fuel float | Sum of fuel consumed while in the approach |
environmental.co2 float | Sum of co2 emitted while in the approach |
behavior.stopped number | Time in milliseconds, the object spent stopped |
behavior.accelerating number | Time in milliseconds, the object spent accelerating |
behavior.decelerating number | Time in milliseconds, the object spent decelerating |
behavior.cruising number | Time in milliseconds, the object spent cruising |
movement.heading string | The heading of the movement that this traveler executed values: nb, sb, eb, wb |
movement.type string | The type of movement that this traveler executed values: left, through, right, u-turn, pedestrian |
movement.certainty string | The certainty level of the deduced movement. The Integrator-AI will attempt to deduce what movement the traveler made, but not all information is always available. In the cases where not enough information is available to deduce the real movement, the Integrator-AI will guess based on what the primary movement of the approach lane permits. unrealized: not enough information to deduce the real movement; educated guess based on what is known realized: the deduced real movement the vehicle made; enough information is available to determine which way the traveler went |
object.id array[number] | The unique ID of the object, which consists of two numbers. The first number is a sequential ID and the second value is a millisecond unix epoch timestamp of when the object was created |
object.type string | The object type values: vehicle, pedestrian, unclassified, aircraft, animal, railcar |
object.classification string | The object's classification as provided by the sensor values: depends on sensor |
object.lwh array[float] | Length, width, height of the object. The values are in order in the array, length is index 0, width index 1, height index 2. The values are in meters |
object.speed float | Speed of the object, in meters per second, when they arrived at the intersection |
object.position array[lat,lng] | Latitude and longitude of the object when it was detected arriving at the intersection |
object.heading float | Heading angle, in radians, of the object. 0.0 is north. false indicates unknown |
lane string | Name of the lane that the object was in at the arrival. Only available for vehicle movements |
Object Movement Passage Event (2001)
id number | ID of the event: always 2001 |
date number | Unix epoch timestamp, in milliseconds, of when the traveler entered the middle of the intersection |
duration number | The time the traveler spent in the middle of the intersection (conflict zone), milliseconds |
heading string | Heading of the approach this traveler arrived at values: nb, sb, eb, wb |
zone.id number | ID of the conflict zone (middle of the intersection) |
zone.name string | Name of the conflict zone |
environmental.fuel float | Sum of fuel consumed while in the middle of the intersection |
environmental.co2 float | Sum of co2 emitted while in the middle of the intersection |
behavior.stopped number | Time in milliseconds, the object spent stopped |
behavior.accelerating number | Time in milliseconds, the object spent accelerating |
behavior.decelerating number | Time in milliseconds, the object spent decelerating |
behavior.cruising number | Time in milliseconds, the object spent cruising |
movement.heading string | The heading of the movement that this traveler executed values: nb, sb, eb, wb |
movement.type string | The type of movement that this traveler executed values: left, through, right, u-turn, pedestrian |
movement.certainty string | The certainty level of the deduced movement. The Integrator-AI will attempt to deduce what movement the traveler made, but not all information is always available. In the cases where not enough information is available to deduce the real movement, the Integrator-AI will guess based on what the primary movement of the approach lane permits. unrealized: not enough information to deduce the real movement; educated guess based on what is known realized: the deduced real movement the vehicle made; enough information is available to determine which way the traveler went |
object.id array[number] | The unique ID of the object, which consists of two numbers. The first number is a sequential ID and the second value is a millisecond unix epoch timestamp of when the object was created |
object.type string | The object type values: vehicle, pedestrian, unclassified, aircraft, animal, railcar |
object.classification string | The object's classification as provided by the sensor values: depends on sensor |
object.lwh array[float] | Length, width, height of the object. The values are in order in the array, length is index 0, width index 1, height index 2. The values are in meters |
object.speed float | Speed of the object, in meters per second, as it entered the middle of the intersection |
object.position array[lat,lng] | Latitude and longitude of the object when it was detected entering the middle of the intersection |
object.heading float | Heading angle, in radians, of the object. 0.0 is north. |
crosswalk string | Name of the crosswalk the pedestrian used. Only available for pedestrian movements |
Object Movement Departure Event (2002)
id number | ID of the event: always 2002 |
date number | Unix epoch timestamp, in milliseconds, of when the traveler reached a departure lane and is leaving the intersection |
duration number | The total time spent from arrival to departure, executing their movement, in milliseconds |
heading string | Heading of the departure this traveler is exiting at (which way they're leaving) values: nb, sb, eb, wb |
zone.id number | ID of the departure zone (zone leaving the intersection) |
zone.name string | Name of the departure zone |
environmental.fuel float | Total fuel consumed from arrival to departure, executing their movement |
environmental.co2 float | Total co2 emitted from arrival to departure, executing their movement |
behavior.stopped number | Time in milliseconds, the object spent cruising. This is totaled from the arrival and passage time |
behavior.accelerating number | Time in milliseconds, the object spent accelerating. This is totaled from the arrival and passage time |
behavior.decelerating number | Time in milliseconds, the object spent decelerating. This is totaled from the arrival and passage time |
behavior.cruising number | Time in milliseconds, the object spent cruising. This is totaled from the arrival and passage time |
movement.heading string | The heading of the movement that this traveler executed values: nb, sb, eb, wb |
movement.type string | The type of movement that this traveler executed values: left, through, right, u-turn, pedestrian |
movement.certainty string | The certainty level of the deduced movement. The Integrator-AI will attempt to deduce what movement the traveler made, but not all information is always available. In the cases where not enough information is available to deduce the real movement, the Integrator-AI will guess based on what the primary movement of the approach lane permits. unrealized: not enough information to deduce the real movement; educated guess based on what is known realized: the deduced real movement the vehicle made; enough information is available to determine which way the traveler went |
object.id array[number] | The unique ID of the object, which consists of two numbers. The first number is a sequential ID and the second value is a millisecond unix epoch timestamp of when the object was created |
object.type string | The object type values: vehicle, pedestrian, unclassified, aircraft, animal, railcar |
object.classification string | The object's classification as provided by the sensor values: depends on sensor |
object.lwh array[float] | Length, width, height of the object. The values are in order in the array, length is index 0, width index 1, height index 2. The values are in meters |
object.speed float | Speed of the object, in meters per second, as it departures the intersection |
object.position array[lat,lng] | Latitude and longitude of the object when it was detected entering the departure zone |
object.heading float | Heading angle, in radians, of the object. 0.0 is north. |
lane string | Name of the lane that the object departed in. Only available for vehicle movements |
Jaywalking Condition Event (3000)
id number | ID of the event: always 3000 |
date number | Unix epoch timestamp, in milliseconds, of when jaywalking was detected |
incident date | An AID incident ID that generated the jaywalking event. You can use this to query AID for the incident and get other details not provided in this log entry, like a recording ID (if generated) which can allow you to playback the incident on a GIS map |
movement.heading string | The heading of the movement associated to this incident values: nb, sb, eb, wb |
movement.type string | The type of movement associated to this incident. Always pedestrian for this log entry |
movement.certainty string | The certainty level of the deduced movement. The Integrator-AI will attempt to deduce what movement the traveler made, but not all information is always available. In the cases where not enough information is available to deduce the real movement, the Integrator-AI will guess based on what the primary movement of the approach lane permits. unrealized: not enough information to deduce the real movement; educated guess based on what is known realized: the deduced real movement the vehicle made; enough information is available to determine which way the traveler went |
movement.ppt number | Post prohibited time, in milliseconds. How long after the crosswalk changed to prohibited (don't walk) did the traveler get detected jaywalking. A value of 523 indicates that the traveler stepped into the crosswalk/roadway 523ms after it changed to prohibited (don't walk) |
object.id array[number] | The unique ID of the object, which consists of two numbers. The first number is a sequential ID and the second value is a millisecond unix epoch timestamp of when the object was created |
object.type string | The object type values: vehicle, pedestrian, unclassified, aircraft, animal, railcar |
object.classification string | The object's classification as provided by the sensor values: depends on sensor |
object.lwh array[float] | Length, width, height of the object. The values are in order in the array, length is index 0, width index 1, height index 2. The values are in meters |
object.position array[lat,lng] | Latitude and longitude of the object when it was detected stepping into the crosswalk/roadway |
object.heading float | Heading angle, in radians, of the object. 0.0 is north. |
Near Miss Condition Event (3001)
id number | ID of the event: always 3001 |
date number | Unix epoch timestamp, in milliseconds, of when the near miss was detected (usually when the trailing object intersects the leading objects path) |
incident date | An AID incident ID that generated the near miss event. You can use this to query AID for the incident and get other details not provided in this log entry, like a recording ID (if generated) which can allow you to playback the incident on a GIS map |
pet number | Post encroachment time, in milliseconds. The delta between the leading object and trailing object where they intersect |
intersect.latitude float | Latitude position of where both the leading and trailing objects intersected |
intersect.longitude float | Longitude position of where both the leading and trailing objects intersected |
leading object | Leading object; the first object at the intersect point |
leading.id array[number] | The unique ID of the object, which consists of two numbers. The first number is a sequential ID and the second value is a millisecond unix epoch timestamp of when the object was created |
leading.type string | The object type values: vehicle, pedestrian, unclassified, aircraft, animal, railcar |
leading.classification string | The object's classification as provided by the sensor values: depends on sensor |
leading.lwh array[float] | Length, width, height of the object. The values are in order in the array, length is index 0, width index 1, height index 2. The values are in meters |
leading.position array[lat,lng] | Latitude and longitude of the object when the near miss was detected (when the trailing object is measured intersecting the leading object) |
leading.heading float | Heading angle, in radians, of the object. 0.0 is north. |
trailing object | Trailing object; the second object at the intersect point |
trailing.id array[number] | The unique ID of the object, which consists of two numbers. The first number is a sequential ID and the second value is a millisecond unix epoch timestamp of when the object was created |
trailing.type string | The object type values: vehicle, pedestrian, unclassified, aircraft, animal, railcar |
trailing.classification string | The object's classification as provided by the sensor values: depends on sensor |
trailing.lwh array[float] | Length, width, height of the object. The values are in order in the array, length is index 0, width index 1, height index 2. The values are in meters |
trailing.position array[lat,lng] | Latitude and longitude of the object when the near miss was detected (when the trailing object is measured intersecting the leading object) |
trailing.heading float | Heading angle, in radians, of the object. 0.0 is north. |
Red Light Runner Condition Event (3002)
id number | ID of the event: always 3002 |
date number | Unix epoch timestamp, in milliseconds, of when the red light running was detected |
incident date | An AID incident ID that generated the red light running event. You can use this to query AID for the incident and get other details not provided in this log entry, like a recording ID (if generated) which can allow you to playback the incident on a GIS map |
movement.heading string | The heading of the movement associated to this incident values: nb, sb, eb, wb |
movement.type string | The type of movement associated to this incident. values: left, through, right. u-turn |
movement.certainty string | The certainty level of the deduced movement. The Integrator-AI will attempt to deduce what movement the traveler made, but not all information is always available. In the cases where not enough information is available to deduce the real movement, the Integrator-AI will guess based on what the primary movement of the approach lane permits. unrealized: not enough information to deduce the real movement; educated guess based on what is known realized: the deduced real movement the vehicle made; enough information is available to determine which way the traveler went |
movement.ppt number | Post prohibited time, in milliseconds. How long after the movement changed to prohibited (red) did the traveler get detected entering the intersection. A value of 523 indicates that the traveler entered the intersection 523ms after it changed to prohibited (red) |
object.id array[number] | The unique ID of the object, which consists of two numbers. The first number is a sequential ID and the second value is a millisecond unix epoch timestamp of when the object was created |
object.type string | The object type values: vehicle, pedestrian, unclassified, aircraft, animal, railcar |
object.classification string | The object's classification as provided by the sensor values: depends on sensor |
object.lwh array[float] | Length, width, height of the object. The values are in order in the array, length is index 0, width index 1, height index 2. The values are in meters |
object.position array[lat,lng] | Latitude and longitude of the object when it was detected stepping into the crosswalk/roadway |
object.heading float | Heading angle, in radians, of the object. 0.0 is north. |
Belated Walker Condition Event (3005)
id number | ID of the event: always 3005 |
date number | Unix epoch timestamp, in milliseconds, of when the incident was detected |
incident date | An AID incident ID that generated the belated walker event. You can use this to query AID for the incident and get other details not provided in this log entry, like a recording ID (if generated) which can allow you to playback the incident on a GIS map |
movement.heading string | The heading of the movement associated to this incident values: nb, sb, eb, wb |
movement.type string | The type of movement associated to this incident. values: left, through, right. u-turn |
movement.certainty string | The certainty level of the deduced movement. The Integrator-AI will attempt to deduce what movement the traveler made, but not all information is always available. In the cases where not enough information is available to deduce the real movement, the Integrator-AI will guess based on what the primary movement of the approach lane permits. unrealized: not enough information to deduce the real movement; educated guess based on what is known realized: the deduced real movement the vehicle made; enough information is available to determine which way the traveler went |
movement.time.walk number | The total time, in milliseconds, it took the VRU to complete their crossing |
movement.time.provided number | The total time, in milliseconds, that the crossing was permitted (how long the signal gave them) |
movement.time.deficit number | How much extra time, in milliseconds, that the VRU needed to complete their crossing |
movement.time.ppt number | Post permitted time, in milliseconds. How long after the movement changed to a permitted state did the VRU step into the crosswalk |
object.id array[number] | The unique ID of the object, which consists of two numbers. The first number is a sequential ID and the second value is a millisecond unix epoch timestamp of when the object was created |
object.type string | The object type values: vehicle, pedestrian, unclassified, aircraft, animal, railcar |
object.classification string | The object's classification as provided by the sensor values: depends on sensor |
object.lwh array[float] | Length, width, height of the object. The values are in order in the array, length is index 0, width index 1, height index 2. The values are in meters |
object.position array[lat,lng] | Latitude and longitude of the object when it was detected stepping into the crosswalk/roadway |
object.heading float | Heading angle, in radians, of the object. 0.0 is north. |
Impeded Departure Condition Event (3006)
id number | ID of the event: always 3006 |
date number | Unix epoch timestamp, in milliseconds, of when the incident was detected |
incident date | An AID incident ID that generated the impeded departure event. You can use this to query AID for the incident and get other details not provided in this log entry, like a recording ID (if generated) which can allow you to playback the incident on a GIS map |
movement.heading string | The heading of the movement associated to this incident values: nb, sb, eb, wb |
movement.type string | The type of movement associated to this incident. values: left, through, right. u-turn |
movement.certainty string | The certainty level of the deduced movement. The Integrator-AI will attempt to deduce what movement the traveler made, but not all information is always available. In the cases where not enough information is available to deduce the real movement, the Integrator-AI will guess based on what the primary movement of the approach lane permits. unrealized: not enough information to deduce the real movement; educated guess based on what is known realized: the deduced real movement the vehicle made; enough information is available to determine which way the traveler went |
object.id array[number] | The unique ID of the object, which consists of two numbers. The first number is a sequential ID and the second value is a millisecond unix epoch timestamp of when the object was created |
object.type string | The object type values: vehicle, pedestrian, unclassified, aircraft, animal, railcar |
object.classification string | The object's classification as provided by the sensor values: depends on sensor |
object.lwh array[float] | Length, width, height of the object. The values are in order in the array, length is index 0, width index 1, height index 2. The values are in meters |
object.position array[lat,lng] | Latitude and longitude of the object when it was detected stepping into the crosswalk/roadway |
object.heading float | Heading angle, in radians, of the object. 0.0 is north. |
Split Failure Condition Event (3007)
id number | ID of the event: always 3007 |
date number | Unix epoch timestamp, in milliseconds, of when the incident was detected |
incident date | An AID incident ID that generated the split failure event. You can use this to query AID for the incident and get other details not provided in this log entry, like a recording ID (if generated) which can allow you to playback the incident on a GIS map |
gor number | Green occupancy ratio |
ror number | Red occupancy ratio |
movement.heading string | The heading of the movement associated to this incident values: nb, sb, eb, wb |
movement.type string | The type of movement associated to this incident. values: left, through, right. u-turn |
movement.time number | Total time the movement was given, in milliseconds |
Reports
Taking advantage of the detailed event records we collect, we can generate many different kinds of reports from that information.
Cycles
Reports that focus on cycles
Cycle Count | Count of completed cycles |
Cycle Time | Real cycle times |
Heading
Reports that focus on headings
Approach Volume by Heading | Volume, broken down by approach heading |
Approach Speed by Heading | Speeds, broken down by approach heading |
Approach Time by Heading | Time travelers spent in the approach, broken down by approach heading |
Departure Volume by Heading | Volume, broken down by departure heading |
Cycle Approach Volume by Heading | Volume per cycle, broken down by approach heading |
Cycle Departure Volume by Heading | Volume per cycle, broken down by departure heading |
CO2 by Heading | CO2 emissions, broken down by approach heading |
Fuel by Heading | Fuel consumption, broken down by approach heading |
Lane
Reports that focus on lanes
Turning Movement Counts by Lane | Turn counts, broken down by lane (e.g. left, right, through, u-turn, etc.). These counts are the real movements executed from the lane, not what is permitted. |
Approach Volume by Lane | Volume, broken down by approach lane |
Approach Speed by Lane | Speeds, broken down by approach lane |
Approach Time by Lane | Time travelers spent in the approach, broken down by approach lane |
Departure Volume by Lane | Volume, broken down by departure lane |
Cycle Approach Volume by Lane | Volume per cycle, broken down by approach lane |
Cycle Departure Volume by Lane | Volume per cycle, broken down by departure lane |
CO2 by Lane | CO2 emissions, broken down by approach lane |
Fuel by Lane | Fuel consumption, broken down by approach lane |
Movement
Reports that focus on movements
Turning Movement Counts by Movement | Turning movement counts, broken down by the movement (e.g. NB left, EB through, SB right, etc.). These are real movements made, regardless of what approach lanes permit. |
Approach Volume by Movement | Volume, broken down by approach movement |
Approach Speed by Movement | Speeds, broken down by approach movement |
Approach Time by Movement | Time travelers spent in the approach, broken down by approach movement |
Travel Time by Movement | Total time travelers spent executing a movement. (i.e. how long does it take to make a NB left at this intersection?) |
Cycle Approach Volume by Movement | Volume per cycle, broken down by approach movement |
Cycle Turning Movement Counts | Per cycle turning movement counts |
Arrivals on Red / Green by Movement | Arrivals on red/green. This factors in movements that are allowed from multiple phases |
CO2 by Movement | CO2 emissions, broken down by approach movement |
Fuel by Movement | Fuel consumption, broken down by approach movement |
Red Light Runners by Movement | Count of red light running events, broken down by movement |
Impeded Departures by Movement | Count of impeded departures, broken down by movement |
Jaywalkers by Movement | Count of jaywalkers, broken down by movement |
Belated Walkers by Movement | Count of belated walkers, broken down by movement |
Miscellaneous
Reports that focus on individual records and chronology
Individual Traveler Movement Chronology | Provides the detailed individual record for each traveler as they move through the intersection in chronological order of their arrival time |
Cycle Chronology | Provides cycle records in chronological order |
Movement State Chronology | Provides movement state records in chronological order |
Detector Chronology | Provides a list of detector state changes in chronological order |
Split Failures | Provides a list of instances where a split failure was detected |
REST API
/api/app/spm+ | Settings | GET, PUT | Application configurations |
/api/app/spm+/events | Raw data | GET, DELETE | Access and management of raw data. Each event will have an ID, this indicates what kind of event it is. For example, event ID 1000 is a phase state event (i.e. the state of a phase changed) |
/api/app/spm+/cycles | Aggregate data | GET | Generates a summary of statistics per-cycle (this is the endpoint that the GUI uses) |
/api/app/spm+/report/* | Report | GET | Aggregate data with selectable output formats and time bin sizes |
GET /api/app/spm+/events?start={iso8601}&end={iso8601}
GET /api/app/spm+/events?start={iso8601}&end={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61start ISO8601 Date & Time | Start time of events /api/app/spm+/events?start=2025-06-16T12:15:30 |
end ISO8601 Date & Time | End time of events /api/app/spm+/events?end=2025-06-16T12:15:30 |
ids number | List of event IDs to fetch, separated by commas /api/app/spm+/events?ids=1000,1001,1002,1003 |
q array | A list of additional data to fetch, separated by commas /api/app/spm+/events?q=state - will fetch the state of the traffic signal (rings, phases, movements, etc.) at the oldest log entry returned so you don't have to query and parse those log entries |
GET /api/app/spm+/events?before={iso8601}
GET /api/app/spm+/events?before={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61before ISO8601 Date & Time | All events before this time /api/app/spm+/events?before=2025-06-16T12:15:30 |
limit number | Limit the number of events returned. You must provide a limit for this query /api/app/spm+/events?limit=10 |
ids number | List of event IDs to fetch, separated by commas /api/app/spm+/events?ids=1000,1001,1002,1003 |
q array | A list of additional data to fetch, separated by commas /api/app/spm+/events?q=state - will fetch the state of the traffic signal (rings, phases, movements, etc.) at the oldest log entry returned so you don't have to query and parse those log entries |
GET /api/app/spm+/events?after={iso8601}
GET /api/app/spm+/events?after={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61after ISO8601 Date & Time | All events after this time /api/app/spm+/events?after=2025-06-16T12:15:30 |
limit number | Limit the number of events returned. If you do not provide a limit, it will return all events after this date and time /api/app/spm+/events?limit=10 |
ids number | List of event IDs to fetch, separated by commas /api/app/spm+/events?ids=1000,1001,1002,1003 |
q array | A list of additional data to fetch, separated by commas /api/app/spm+/events?q=state - will fetch the state of the traffic signal (rings, phases, movements, etc.) at the oldest log entry returned so you don't have to query and parse those log entries |
DELETE /api/app/spm+/events?after={iso8601}
DELETE /api/app/spm+/events?after={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61after ISO8601 Date & Time | All events after this time /api/app/spm+/events?after=2025-06-16T12:15:30 |
DELETE /api/app/spm+/events?before={iso8601}
DELETE /api/app/spm+/events?before={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61before ISO8601 Date & Time | All events before this time /api/app/spm+/events?before=2025-06-16T12:15:30 |
DELETE /api/app/spm+/events?start={iso8601}&end={iso8601}
DELETE /api/app/spm+/events?start={iso8601}&end={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61start ISO8601 Date & Time | All events after this time /api/app/spm+/events?start=2025-06-16T12:15:30 |
end ISO8601 Date & Time | All events before this time /api/app/spm+/events?end=2025-06-16T12:15:30 |
GET /api/app/spm+/cycles
GET /api/app/spm+/cycles HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
prior number | Also include a number of cycles prior to this cycle /api/app/spm+/cycles?prior=2 |
q array | Additional information to query /api/app/spm+/cycles?q=elapsed - include time since last completed cycle |
HTTP/1.1 200 OK
Content-Type: application/json
{}
GET /api/app/spm+/cycles?start={iso8601}&end={iso8601}
GET /api/app/spm+/cycles?start={iso8601}&end={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61start ISO8601 Date & Time | Start time of the range to search /api/app/spm+/cycles?start=2025-06-16T12:15:30 |
end ISO8601 Date & Time | End time of the range to search /api/app/spm+/cycles?end=2025-06-16T12:15:30 |
q array | Additional information to query /api/app/spm+/cycles?q=elapsed - include time since last completed cycle |
GET /api/app/spm+/cycles?at={iso8601}
GET /api/app/spm+/cycles?at={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61at ISO8601 Date & Time | Fetch the cycle that this date and time falls within /api/app/spm+/cycles?at=2025-06-16T12:15:30 |
prior number | Also include a number of cycles prior to this cycle /api/app/spm+/cycles?prior=2 |
post number | Also include a number of cycles after the targeted cycle /api/app/spm+/cycles?post=2 |
q array | Additional information to query /api/app/spm+/cycles?q=elapsed - include time since last completed cycle |
GET /api/app/spm+/cycles?after={iso8601}
GET /api/app/spm+/cycles?after={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61after ISO8601 Date & Time | Fetch the cycle that is after this date and time /api/app/spm+/cycles?after=2025-06-16T12:15:30 |
prior number | Also include a number of cycles prior to the resolved cycle /api/app/spm+/cycles?prior=2 |
post number | Also include a number of cycles after the resolved cycle /api/app/spm+/cycles?post=2 |
q array | Additional information to query /api/app/spm+/cycles?q=elapsed - include time since last completed cycle |
GET /api/app/spm+/cycles?before={iso8601}
GET /api/app/spm+/cycles?before={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61before ISO8601 Date & Time | Fetch the cycle that is before this date and time /api/app/spm+/cycles?before=2025-06-16T12:15:30 |
prior number | Also include a number of cycles prior to the resolved cycle /api/app/spm+/cycles?prior=2 |
post number | Also include a number of cycles after the resolved cycle /api/app/spm+/cycles?post=2 |
q array | Additional information to query /api/app/spm+/cycles?q=elapsed - include time since last completed cycle |
GET /api/app/spm+/report/approach-speed-by-heading
GET /api/app/spm+/report/approach-speed-by-heading HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
format string | Specifies the output format for the report. Defaults to json /api/app/spm+/report/approach-speed-by-heading?format=excel - generates a report as an excel file download /api/app/spm+/report/approach-speed-by-heading?format=csv - generates a report as a csv file download /api/app/spm+/report/approach-speed-by-heading?format=json - generates a report as a json output /api/app/spm+/report/approach-speed-by-heading?format=json.file - generates a report as a json file download |
start ISO8601 Date & Time | Start date and time for the selection range of the report /api/app/spm+/report/approach-speed-by-heading?start=2025-06-16T12:15:30 |
end ISO8601 Date & Time | End date and time for the selection range of the report /api/app/spm+/report/approach-speed-by-heading?end=2025-06-16T12:15:30 |
subtype string | Which sub data type to target. Default is mean, possible values are: mean, median, min, max /api/app/spm+/report/approach-speed-by-heading?subtype=mean - fetch average speeds /api/app/spm+/report/approach-speed-by-heading?subtype=min - fetch 85th percentile speeds /api/app/spm+/report/approach-speed-by-heading?subtype=max - fetch maximum encountered speeds |
bin number,representation | The bin size, used to split the data up into smaller chunks. There are two parts to this value the size value and the representation. The size is numeric and represent can be one of the following: seconds, minutes, hours, days, months, years /api/app/spm+/report/approach-speed-by-heading?bin=1,year - to set bin sizes to 1 year /api/app/spm+/report/approach-speed-by-heading?bin=1,month - to set bin sizes to 1 month /api/app/spm+/report/approach-speed-by-heading?bin=1,hour - to set bin sizes to 1 hour /api/app/spm+/report/approach-speed-by-heading?bin=30,minutes - to set bin sizes to 30 minutes /api/app/spm+/report/approach-speed-by-heading?bin=15,minutes - to set bin sizes to 15 minutes |
HTTP/1.1 200 OK
Content-Type: {depends on format parameter}
GET /api/app/spm+/report/approach-speed-by-lane
GET /api/app/spm+/report/approach-speed-by-movement
GET /api/app/spm+/report/approach-time-by-heading
GET /api/app/spm+/report/approach-time-by-lane
GET /api/app/spm+/report/approach-time-by-movement
GET /api/app/spm+/report/approach-volume-by-heading
GET /api/app/spm+/report/approach-volume-by-lane
GET /api/app/spm+/report/approach-volume-by-movement
GET /api/app/spm+/report/departure-volume-by-heading
GET /api/app/spm+/report/departure-volume-by-lane
GET /api/app/spm+/report/departure-volume-by-movement
GET /api/app/spm+/report/turning-movement-counts-by-lane
GET /api/app/spm+/report/turning-movement-counts-by-movement
GET /api/app/spm+/report/travel-time-by-movement
GET /api/app/spm+/report/passage-time-by-movement
GET /api/app/spm+/report/individual-traveler-movement-chronology
GET /api/app/spm+/report/cycle-count
GET /api/app/spm+/report/cycle-time
GET /api/app/spm+/report/cycle-chronology
GET /api/app/spm+/report/cycle-approach-volume-by-heading
GET /api/app/spm+/report/cycle-approach-volume-by-lane
GET /api/app/spm+/report/cycle-approach-volume-by-movement
GET /api/app/spm+/report/cycle-departure-volume-by-heading
GET /api/app/spm+/report/cycle-departure-volume-by-lane
GET /api/app/spm+/report/cycle-turning-movement-counts
GET /api/app/spm+/report/arrivals-on-red-green-by-movement
GET /api/app/spm+/report/co2-by-heading
GET /api/app/spm+/report/co2-by-lane
GET /api/app/spm+/report/co2-by-movement
GET /api/app/spm+/report/fuel-by-heading
GET /api/app/spm+/report/fuel-by-lane
GET /api/app/spm+/report/fuel-by-movement
GET /api/app/spm+/report/belated-walkers-by-movement
GET /api/app/spm+/report/per-traveler-movement-chronology
GET /api/app/spm+/report/movement-state-chronology
GET /api/app/spm+/report/jaywalkers-by-movement
GET /api/app/spm+/report/red-light-runners-by-movement
GET /api/app/spm+/report/impeded-departures-by-movement
GET /api/app/spm+/report/split-failures
Edge Link (MQTT)
app/spmp/event/state/cycle [JSON]
When a new cycle starts, an event is published to this topic
{"id":1005,"timestamp":1765040710492}id number | ID of the event. This indicates what type of event it is. 1005 = cycle state event |
timestamp number | Unix epoch timestamp, in milliseconds, of when the cycle started |
app/spmp/event/state/movement/$heading/$type [JSON]
When the state of a movement changes, it's published to this topic.
$heading | The heading of the movement values: nb, sb, eb, wb |
$type | The movement type values: left, right, through, u-turn, pedestrian |
{"heading":"nb","id":1003,"indication":"green","phase":4,"ring":1,"state":"protected","timestamp":1765040735001,"type":"through"}id number | ID of the event. This indicates what type of event it is. 1003 = movement state event |
timestamp number | Unix epoch timestamp, in milliseconds, of when the state changed |
heading string | Heading of the movement Values: nb, sb, eb, wb |
type string | Type of movement Values: left, right, through, u-turn, pedestrian |
phase number | The phase controlling the state of this movement from the perspective of travelers. Value of 0 indicates no phase is controlling this movement |
ring number | The ring that this movement is active under. Value of 0 indicates this movement is not active under any rings |
indication string | What indication the movement is showing to travelers Values: none, red, yellow, green, prepare-to-go, flashing-green, flashing-yellow, flashing-red, fya, fra, dont-walk, flashing-dont-walk, walk |
state string | What state the movement is in Values: protected, permissive, permissive-after-stop, prohibited |
app/spmp/event/state/phase/$id [JSON]
When the state of a phase changes, it's published to this topic
$id | ID of the phase (e.g. 1 = phase 1, 2 = phase 2, etc.) |
{"id":1000,"pedestrian":"dont-walk","phase":4,"timestamp":1765040793495,"vehicle":"yellow"}id number | ID of the event. This indicates what type of event it is. 1000 = phase state event |
timestamp number | Unix epoch timestamp, in milliseconds, of when the state changed |
phase number | Phase number |
vehicle string | What indication the phase is showing to vehicles Values: none, red, yellow, green, fya, fra, prepare-to-go, flashing-green, flashing-yellow, flashing-red |
pedestrian string | State of pedestrian movements for this phase Values: none, dont-walk, flashing-dont-walk, walk |
app/spmp/event/state/ring/$id [JSON]
When the state of a ring changes, it's published to this topic
$id | ID of the ring (e.g. 1 = ring 1, 2 = ring 2, etc.) |
{"id":1001,"max":0,"next":0,"phase":4,"ring":1,"state":"none","termination":"none","timestamp":1765040865517}id number | ID of the event. This indicates what type of event it is. 1001 = ring state event |
timestamp number | Unix epoch timestamp, in milliseconds, of when the state changed |
ring number | ID of the ring |
phase number | Phase number this ring is controlling. 0 if not controlling any phases |
next number | Next phase this ring will be controlling, when known. If not known, will be 0 |
max number | The max green time value being used by the controller. For example, this value will be 1 if using MAX1, or 3 if using MAX3. The value is 0 if not known |
state string | State of the ring Values: none, min-green, extension, maximum, green-rest, yellow-change, red-clear, red-rest |
termination string | Termination reason for the ring Values: none, force-off, max-out, gap-out |
app/spmp/event/state/detector/$id [JSON]
When the state of a detector changes, it's published to this topic
$id | ID of the detector (e.g. 1 = detector 1, 2 = detector 2, etc.) |
{"detector":3,"id":1002,"pedestrian":"clear","timestamp":1765040898675,"vehicle":"call"}id number | ID of the event. This indicates what type of event it is. 1002 = detector state event |
timestamp number | Unix epoch timestamp, in milliseconds, of when the state changed |
detector number | Detector number |
vehicle string | State of the detector for vehicles Values: call, clear, locked |
pedestrian string | State of the detector for pedestrians Values: call, clear, locked |
app/spmp/event/object/movement/$heading/$type/arrival [JSON]
When SPM+ resolves an object's arrival location, it will be published to this topic. It will contain information on when the object entered an arrival zone (stop bar or advance, which ever is encountered first)
$heading | The heading of the movement values: nb, sb, eb, wb |
$type | The movement type values: left, right, through, u-turn |
{"duration":54693,"environmental":{"co2":0.0,"fuel":0.0},"heading":"nb","id":2000,"movement":{"certainty":"realized","heading":"nb","type":"left"},"object":{"classification":"car","id":[771,1765040858650],"heading":1.029386,"lwh":[0.8171177506446838,0.7331005930900574,1.6948033571243286],"position":[42.35623374155224,-83.06664580456571],"type":"vehicle"},"speed":1.1590951681137085,"timestamp":1765040871385,"zone":36}id number | ID of the event. This indicates what type of event it is. 2000 = object movement arrival event |
timestamp number | Unix epoch timestamp, in milliseconds, of when the object was detected arriving at the intersection |
heading string | Heading of the approach. Usually matches the movement heading as well. Values: nb, sb, eb, wb |
duration number | The duration, in milliseconds, that the object spent in the approach. Typically calculated from: passage_timestamp - arrival_timestamp = duration at the approach |
speed number | Speed of the object, in meters per second, measured when the object was detected arriving at the intersection |
zone number | ID of the stopbar or advance zone (which ever is last) that the object was detected in |
object.id array[number] | UUID of the object |
object.type string | Type of object Values: vehicle, pedestrian, cyclist, unclassified, animal, aircraft, railcar |
object.classification string | Classification of the object. This is forwarded from the detection system, and what it reports the object as |
object.lwh array[length,width,height] | Object length, width, and height. In meters |
object.position array[lat,lng] | Position of the object, in WGS-84 decimal degrees, when detected arriving at the intersection. null if not known |
object.heading number | Heading angle, in radians, of the object. 0.0 is north. null if not known |
movement object | The resolved movement for the object. This is the final movement we were able to resolve the object making while tracking it as it moved through the intersection. It has no impact on what the lanes permit, it's the real movement the object made |
movement.heading string | Heading of the movement Values: nb, sb, eb, wb |
movement.type string | Type of movement Values: left, right, through, u-turn, pedestrian |
movement.certainty string | How confident the resolved movement is. Sometimes we don't have enough information to determine the movement the object actually made, so we make an educated guess Values: realized, unrealized realized - indicates that we had enough information to resolve the actual movement the object made through the intersection unrealized - indicates that we didn't have enough information to resolve the actual movement the object made, so we used what we did know to make an educated guess |
environmental.co2 number | Estimated CO2 emitted, in kg, while in the approach |
environmental.fuel number | Estimated fuel consumed, in gal, while in the approach |
app/spmp/event/object/movement/$heading/$type/passage [JSON]
When SPM+ resolves an object's passage location, it will be published to this topic. It will contain information on when the object entered the conflict zone (middle of the intersection)
$heading | The heading of the movement values: nb, sb, eb, wb |
$type | The movement type values: left, right, through, u-turn |
{"duration":13783,"environmental":{"co2":0.0,"fuel":0.0},"heading":"nb","id":2001,"movement":{"certainty":"realized","heading":"nb","type":"left"},"object":{"classification":"car","heading":1.029386,"id":[771,1765040858650],"lwh":[0.8171177506446838,0.7331005930900574,1.6948033571243286],"position":[42.35618173403281,-83.06660956702233],"type":"vehicle"},"speed":1.106624960899353,"timestamp":1765040926078,"zone":37}id number | ID of the event. This indicates what type of event it is. 2001 = object movement passage event |
timestamp number | Unix epoch timestamp, in milliseconds, of when the object was detected entering the middle of the intersection |
heading string | Heading of the approach. Usually matches the movement heading as well. Values: nb, sb, eb, wb |
duration number | The duration, in milliseconds, that the object spent in the middle of the intersection. Typically calculated from: departure_timestamp - passage_timestamp = duration in the middle of the intersection |
speed number | Speed of the object, in meters per second, measured when the object was detected entering the middle of the intersection |
zone number | ID of the conflict zone |
object.id array[number] | UUID of the object |
object.type string | Type of object Values: vehicle, pedestrian, cyclist, unclassified, animal, aircraft, railcar |
object.classification string | Classification of the object. This is forwarded from the detection system, and what it reports the object as |
object.lwh array[length,width,height] | Object length, width, and height. In meters |
object.position array[lat,lng] | Position of the object, in WGS-84 decimal degrees, when it was detected entering the middle of the intersection. null if not known |
object.heading number | Heading angle, in radians, of the object. 0.0 is north. null if not known |
movement object | The resolved movement for the object. This is the final movement we were able to resolve the object making while tracking it as it moved through the intersection. It has no impact on what the lanes permit, it's the real movement the object made |
movement.heading string | Heading of the movement Values: nb, sb, eb, wb |
movement.type string | Type of movement Values: left, right, through, u-turn, pedestrian |
movement.certainty string | How confident the resolved movement is. Sometimes we don't have enough information to determine the movement the object actually made, so we make an educated guess Values: realized, unrealized realized - indicates that we had enough information to resolve the actual movement the object made through the intersection unrealized - indicates that we didn't have enough information to resolve the actual movement the object made, so we used what we did know to make an educated guess |
environmental.co2 number | Estimated CO2 emitted, in kg, while in the middle of the intersection |
environmental.fuel number | Estimated fuel consumed, in gal, while in the middle of the intersection |
app/spmp/event/object/movement/$heading/$type/departure [JSON]
When SPM+ resolves an object's departure location, it will be published to this topic. It will contain information on when the object entered a departure zone (zone leaving the intersection)
$heading | The heading of the movement values: nb, sb, eb, wb |
$type | The movement type values: left, right, through, u-turn |
{"duration":68476,"environmental":{"co2":0.0,"fuel":0.0},"heading":"nb","id":2002,"movement":{"certainty":"realized","heading":"nb","type":"left"},"object":{"classification":"car","heading":1.029386,"id":[771,1765040858650],"lwh":[0.8171177506446838,0.7331005930900574,1.6948033571243286],"position":[42.35589373878812,-83.0664358153236],"type":"vehicle"},"speed":1.3669003248214722,"timestamp":1765040947468,"zone":0}id number | ID of the event. This indicates what type of event it is. 2002 = object movement departure event |
timestamp number | Unix epoch timestamp, in milliseconds, of when the object was detected leaving the intersection |
heading string | Heading of the departure. If the object's movement was NB through, this would be nb but if the object's movement was NB left, this would be wbValues: nb, sb, eb, wb |
duration number | The total travel time, in milliseconds, that the object spent traversing the intersection. Typically calculated from: arrival_timestamp - departure_timestamp = total travel time |
speed number | Speed of the object, in meters per second, measured when the object was detected leaving the intersection |
zone number | ID of the departure zone |
object.id array[number] | UUID of the object |
object.type string | Type of object Values: vehicle, pedestrian, cyclist, unclassified, animal, aircraft, railcar |
object.classification string | Classification of the object. This is forwarded from the detection system, and what it reports the object as |
object.lwh array[length,width,height] | Object length, width, and height. In meters |
object.position array[lat,lng] | Position of the object, in WGS-84 decimal degrees, when it was detected leaving the intersection. null if not known |
object.heading number | Heading angle, in radians, of the object. 0.0 is north. null if not known |
movement object | The resolved movement for the object. This is the final movement we were able to resolve the object making while tracking it as it moved through the intersection. It has no impact on what the lanes permit, it's the real movement the object made |
movement.heading string | Heading of the movement Values: nb, sb, eb, wb |
movement.type string | Type of movement Values: left, right, through, u-turn, pedestrian |
movement.certainty string | How confident the resolved movement is. Sometimes we don't have enough information to determine the movement the object actually made, so we make an educated guess Values: realized, unrealized realized - indicates that we had enough information to resolve the actual movement the object made through the intersection unrealized - indicates that we didn't have enough information to resolve the actual movement the object made, so we used what we did know to make an educated guess |
environmental.co2 number | Total estimated CO2 emitted, in kg, while traversing the intersection |
environmental.fuel number | Total estimated fuel consumed, in gal, while traversing the intersection |
