đź“„ SPM+

SPM+ (v2.84)

SPM+ is our next-generation application designed to deliver comprehensive insights into how traffic signals perform and how travelers (vehicles, pedestrians, cyclists, and more) interact with the intersection.

We took traditional Signal Performance Measures (SPM) systems and significantly enhanced them — that's why we call it SPM+. It goes beyond basic efficiency metrics to also provide critical safety data, giving agencies a fuller picture of intersection operations.

What truly sets SPM+ apart is its exceptionally high resolution — especially when paired with LiDAR sensors. This integration enables persistent, precise tracking of every traveler throughout their entire journey through the intersection, capturing detailed movements that legacy systems simply can't match.

Concepts

Event Log

The Integrator-AI builds on traditional event logging found in Advanced Transportation Controllers (ATC), which typically records timestamped entries (ID, date, data) for signal operations, faults, and basic state changes.

Integrator-AI extends this format with custom entry types for unprecedented detail and insight—without requiring a full ATC cabinet. Our edge-based solution generates rich, high-resolution event data from virtually any traffic controller, using advanced sensing and processing. This information includes:

  • 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)
You can access the raw event log via our REST API, Edge Link, or detailed chronology reports—enabling advanced forensics, performance auditing, safety analysis, and proactive optimization.

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.

đź’ˇ

Legacy fallback: if true movement does not have enough information about a traveler's path, it will fallback to legacy mode. This will tag the traveler with the primary movement from their approach lane.


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

  1. Total count of arrivals and passages for that cycle
  2. Start and end time for that cycle
  3. Duration of the cycle
  4. Elapsed time since the most recently completed cycle
  5. Arrival volume by approach heading
  6. Arrival volume by approach lane
  7. Arrival volume by heading
  8. Crosswalk volume by heading
  9. Departure volume by departure heading
  10. Departure volume by movement
  11. Departure volume by heading
  12. Emssions by approach
  13. Fuel consumption by approach
  14. Average travel times by movement broken down by: approach, passage, and total time
đź’ˇ

The inner slices of the departure and arrival charts are aligned with the outter slices that they're influencing. 

  1. Number of cycles to include before the current cycle
  2. Movement
  3. Movement state (permissive) with indication color
  4. Movement state (protected) with indication color
  5. Tooltip with details of what the mouse is hovering over
  6. Cycle barrier
  7. Traveler interaction indicator

    1. blue - arrival
    2. green - passage
    3. grey - departure
  8. Traveler filter
  9. Number of cycles to include after the current cycle

  1. Object information
  2. Movement made by the object
  3. Arrival details
  4. Passage details
  5. Departure details

  1. Move to previous cycle
  2. Move to a specific cycle (you choose the date/time)
  3. Move to the next cycle
  4. Move to the most recent cycle
  5. Prune (delete) data
  6. App settings
  7. Generate a report

Settings

  1. Enable/disable the app
  2. The traffic signal interface to source state and configurations from. SPM+ will automatically import settings from this signal to generate it's information
  3. Maximum age of event log records. Records older than this age will automatically be deleted. A value of 0 indicates no age limit. 
  4. Object filter to help with detection input noise
  5. 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)

Generated when a phase changes state. State changes occur when the vehicle or pedestrian indications for the phase have changed.
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)

Generated when a ring changes state. State changes occur when the ring changes state, the phase it's servicing, a termination reason is resolved, the next phase is available, or max time changes
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)

Generated when the state of a detector changes. State changes occur when the detector's call status for vehicles or peds changes
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)

Generated when the state of a movement changes. State changes occur when the movement's state, indication, phase, or ring change. Movements are a new concept that are unique to our Integrator-AI product. They describe the state of each movement at the intersection by resolving what travelers executing that movement are allowed to do during each phase and what indications are being presented to travelers. Essentially based around the perspective of the traveler and how they interpret what they're allowed to do with their movement. It also factors in movements that are controlled by more than one phase (e.g. left turn that is protected on phase 5 but permissive on phase 2), as those influence the state of the movement.
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)

Generated when a new cycle starts. This is effectively a marker to indicate when cycles change. A cycle change occurs when the traffic controller returns to the first barrier of its phase sequences.
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)

Provides information on a single traveler and how they arrived at the intersection (approach information). This event includes the resolved movement that the traveler makes after leaving the intersection.
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)

Provides information on a single traveler and their time spent in the middle of the intersection (conflict zone). This event includes the resolved movement that the traveler makes after leaving the intersection.
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. false indicates unknown

crosswalk
string
Name of the crosswalk the pedestrian used. Only available for pedestrian movements

Object Movement Departure Event (2002)

Provides information on a single traveler and their departure of the intersection. This event includes the resolved movement that the traveler makes after leaving the intersection.
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. false indicates unknown

lane
string
Name of the lane that the object departed in. Only available for vehicle movements

Jaywalking Condition Event (3000)

Provides information on when a pedestrian is detected jaywalking. Additional details of the incident can be fetched from our AID application.
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. false indicates unknown

Near Miss Condition Event (3001)

Provides information on when a near miss is detected. Additional details of the incident can be fetched from our AID application.
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. false indicates unknown

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. false indicates unknown

Red Light Runner Condition Event (3002)

Provides information on when a traveler runs a red light. Additional details of the incident can be fetched from our AID application.
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. false indicates unknown

Belated Walker Condition Event (3005)

Provides information on when a VRU does not complete their crossing in a crosswalk before it changes to prohibited (dont walk). Additional details of the incident can be fetched from our AID application.
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. false indicates unknown

Impeded Departure Condition Event (3006)

Provides information on when a traveler is impeded from departing the intersection. Usually an indication that an adjacent intersection or something bordering the intersection is impacting outflow. Additional details of the incident can be fetched from our AID application.
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. false indicates unknown

Split Failure Condition Event (3007)

Provides information on when a split failure is detected on a movement. Usually an indication that a movement didn't have enough time to service all the demand.
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.

âś…

If there is a report that you don't see we support, please contact us and we'll be happy to add it to our development log

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}

Fetch event log entries between a specific date and time
⚠️
This is raw log data. So the responses can be quite large depending on your selection criteria.
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 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
start
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}

Fetch event log entries before this date and time
⚠️
This is raw log data. So the responses can be quite large depending on your selection criteria.
GET /api/app/spm+/events?before={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
before
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}

Fetch event log entries after this date and time
⚠️
This is raw log data. So the responses can be quite large depending on your selection criteria.
GET /api/app/spm+/events?after={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
after
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 event log entries after this date and time
DELETE /api/app/spm+/events?after={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
after
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 event log entries before this date and time
DELETE /api/app/spm+/events?before={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
before
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 event log entries between the provided date and times
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 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
start
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

Fetch data for the most recently completed cycle
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}

Fetch cycles that fall within this time and date
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 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
start
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}

Fetch the cycle that this date and time falls within
GET /api/app/spm+/cycles?at={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
at
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}

Fetch next cycle that is after this date and time
GET /api/app/spm+/cycles?after={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
after
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}

Fetch next cycle that is before this date and time
GET /api/app/spm+/cycles?before={iso8601} HTTP/1.1
Host: 127.0.0.1
Accept: application/json
Content-Type: application/json
Authorization: bearer 109b893ceb1c26e3e64e9abf7cdd2aa1c0d25f61
before
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 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

TBD

GET /api/app/spm+/report/approach-speed-by-movement

TBD

GET /api/app/spm+/report/approach-time-by-heading

TBD

GET /api/app/spm+/report/approach-time-by-lane

TBD

GET /api/app/spm+/report/approach-time-by-movement

TBD

GET /api/app/spm+/report/approach-volume-by-heading

TBD

GET /api/app/spm+/report/approach-volume-by-lane

TBD

GET /api/app/spm+/report/approach-volume-by-movement

TBD

GET /api/app/spm+/report/departure-volume-by-heading

TBD

GET /api/app/spm+/report/departure-volume-by-lane

TBD

GET /api/app/spm+/report/departure-volume-by-movement

TBD

GET /api/app/spm+/report/turning-movement-counts-by-lane

TBD

GET /api/app/spm+/report/turning-movement-counts-by-movement

TBD

GET /api/app/spm+/report/travel-time-by-movement

TBD

GET /api/app/spm+/report/passage-time-by-movement

TBD

GET /api/app/spm+/report/individual-traveler-movement-chronology

TBD

GET /api/app/spm+/report/cycle-count

TBD

GET /api/app/spm+/report/cycle-time

TBD

GET /api/app/spm+/report/cycle-chronology

TBD

GET /api/app/spm+/report/cycle-approach-volume-by-heading

TBD

GET /api/app/spm+/report/cycle-approach-volume-by-lane

TBD

GET /api/app/spm+/report/cycle-approach-volume-by-movement

TBD

GET /api/app/spm+/report/cycle-departure-volume-by-heading

TBD

GET /api/app/spm+/report/cycle-departure-volume-by-lane

TBD

GET /api/app/spm+/report/cycle-turning-movement-counts

TBD

GET /api/app/spm+/report/arrivals-on-red-green-by-movement

TBD

GET /api/app/spm+/report/co2-by-heading

TBD

GET /api/app/spm+/report/co2-by-lane

TBD

GET /api/app/spm+/report/co2-by-movement

TBD

GET /api/app/spm+/report/fuel-by-heading

TBD

GET /api/app/spm+/report/fuel-by-lane

TBD

GET /api/app/spm+/report/fuel-by-movement

TBD

GET /api/app/spm+/report/belated-walkers-by-movement

TBD

GET /api/app/spm+/report/per-traveler-movement-chronology

TBD

GET /api/app/spm+/report/movement-state-chronology

TBD

GET /api/app/spm+/report/jaywalkers-by-movement

TBD

GET /api/app/spm+/report/red-light-runners-by-movement

TBD

GET /api/app/spm+/report/impeded-departures-by-movement

TBD

GET /api/app/spm+/report/split-failures

TBD

Edge Link (MQTT)

đź’ˇ

Due to the reserved + character in MQTT topic names, we've replaced the + in spm+ with a p character instead

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)

⚠️

lt's important to note, that this event is not generated immediately when an object arrives at the intersection. This event is only generated once we know all information about the object's path through the intersection. So by the time you receive this event the object's path has already been resolved and it's likely already gone. The timestamp in the event will correctly indicate the real time the object arrived at the intersection however, so you can use this to determine when that was.

$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)

⚠️

lt's important to note, that this event is not generated immediately when an object enters the intersection. This event is only generated once we know all information about the object's path through the intersection. So by the time you receive this event the object's path has already been resolved and it's likely already gone. The timestamp in the event will correctly indicate the real time the object entered the intersection however, so you can use this to determine when that was.

$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)

⚠️

lt's important to note, that this event is not generated immediately when an object departs the intersection. This event is only generated once we know all information about the object's path through the intersection. So by the time you receive this event the object's path has already been resolved and it's likely already gone. The timestamp in the event will correctly indicate the real time the object began departing the intersection however, so you can use this to determine when that was.

$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 wb

Values: 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