Skip to content

Data processing

JG edited this page Sep 29, 2023 · 11 revisions

Computation of Vehicle Position, Speed, and Heading

This page describes the backend computation for positions, speed, heading, and other related values for a vehicle. This is an important step in the data pipeline, because these values are not saved anywhere and need to be calculated from raw data. Because of that no consistency checks are possible and the final position can jump back and forth for different calculations (this was already observed in practice while not moving). The calculation for this can be found in the vehicle service and can be divided into several steps:

  1. Data acquisition: The first step fetches the raw data from the database. There are two types of data (both associated with the requested vehicle):

    • logs of trackers: Sent by stationary trackers, which are mounted on the vehicle. Only the last log of every tracker is taken into account, because at the moment their sending frequency is quite low and therefore not very useful for real time tracking. This could be changed in the future as the tracker data is probably more reliable than the second type.
    • logs of app instances: Sent by apps of the passengers with enabled GPS. Those logs can be sent more often and thus are closer to real time. For testing this is the better source of data (because of the low sending frequency of the stationary trackers). Therefore the logs of the last 30 seconds are included for the calculation. This timeframe might be quite big for a realtime application, but it should improve gaps in the timeline (maybe caused by bad connection) and make up for inaccuracies in the data. To improve performance of all further computations these logs are sampled down. This is done by filtering the logs in a way, that there are at least 2-3 seconds (randomized for better sampling) between them. This also avoids redundant calculations if multiple apps send (almost) the same data for one vehicle.

    Each log contains a heading (0-359°), speed and coordinates measured by the sending device as well as a timestamp, when the data was sent (not received!). The database model page contains more information on the fields and relations of logs.

  2. Vehicle traveling direction: The traveling direction of the vehicle needs to be computed next. This is a value of 1 or -1 representing the vehicle traveling to the end and the start of the track, respectively. This is needed for further calculation, but is included in the final response as well to detect vehicles traveling towards each other. For this all logs of both types (app- and tracker-logs) are concatenated. There are two cases, how this is calculated:

    • If there is only one log (this could happen e.g. if no app instance is sending data for a vehicle), we first need to compute the progress of the vehicle on the track. Therefore the position of this log is projected onto the track to obtain the track kilometer. With this value the orientation (0-359°) of the track at that position can be computed. Together with the heading of the vehicle from its only log the traveling direction can be computed. All computations made with only one log cannot be particularly precise.
    • If there are at least two logs, all track kilometers are obtained from them in a similar way as above. Instead of looking up the orientation of the track at this point, the traveling direction can be computed directly from the track kilometers by simple subtraction and checking if the result is positive: $\sum\nolimits_{i=1}^{\text{logs.length}} \text{logs}_{i-1} - \text{logs}_i$. As an advantage there is no heading of the vehicle needed and it is probably more robust.
    • For the sake of completeness it should be mentioned, that it is not possible to compute anything, if there are no logs at all (the response is a 404 in this case).
  3. Vehicle speed: To compute the vehicle speed all logs of both types (tracker and app) are concatenated together again. If there is only one log in total, the speed of it is just multiplied with the decelerating factor stated below. If not, the track kilometer values are obtained as described above from the two most recent logs $l_0$ and $l_1$. By subtracting those two values and using the timestamps of them it is possible to get a good approximation of the speed: $\frac{l_0\text{.trackKilometer} - l_1\text{.trackKilometer}}{l_0\text{.timestamp} - l_1\text{.timestamp}}$. Then all three speeds - the two from the logs and the third, that just got calculated - are averaged again to get an even better approximation. Lastly, this factor is multiplied to the result to take the passed time into account: $-e^{\frac{t-30}{6}} + 1$ where $t$ is the passed time to the latest log. If the final speed is negative, it is set to 0. This leads to a slowly decelerating vehicle, which stops after 30 seconds (if no new logs come in).

  4. Vehicle position: Next the current vehicle position is calculated. This can be divided into different steps again:

    1. Weighting the logs: This step should give each $\text{log}$ a weight representing its certainty. Those weights are composed of two different factors, which have a value between 0 and 1:
      • The temporal factor: Represents how long ago the log was received. $\frac{c - (t - \text{log.timestamp)}}{c}$, where $t$ is the current time in seconds since epoch and $c$ is a cutoff value in seconds (30 for logs of apps and 180 for logs of trackers corresponding to their sending frequency). If this value is negative, it is set to 0. Also note, that $t - \text{log.timestamp}$ is always positive as the log was received before the calculation uses it here.
      • The accuracy factor: Turf calculates the distance to the track and then a similar formula as above is applied. The cutoff values for this are 15 meters for app logs and 50 meters for logs of trackers. This allows the trackers to be more inaccurate, because there is at most only one log of the tracker used in this calculation.
    2. Predicting current positions: Then the current position of the vehicle is predicted for each $\text{log}$ by obtaining the track kilometer of it and calculating the traveled distance. This is a simple calculation involving the already computed values of the vehicle's speed and traveling direction: $\text{log.trackKilometer} + \text{speed} * (t - \text{log.timestamp}) * \text{travelingDirection}$
    3. Building the weighted average: Finally the current position is determined by building the average on all predicted track kilometer values based on their weight: $$\sum \text{log}_i\text{.trackKilometer} * \frac{\text{log}_i\text{.weight}}{\sum \text{log}_i\text{.weight}}$$ Thus the final position is always mapped on the track. If the weights are all 0 (so the logs were not recent or accurate enough), the last known position is just returned.
  5. Vehicle progress on its track: This small step calculates the relative vehicle location in relation to its track (0-100%). This is a really simple calculation by obtaining the current track kilometer (already a result of the position computation) and the total track length. It is noteworthy, that the track kilometer value will be included in the final result two.

  6. Vehicle heading: The last step computes the heading of the vehicle. In contrast to the traveling direction this represents the real orientation of the vehicle in degrees (0-359) and not just to which end of the track the vehicle is traveling at the moment. This heading is displayed to the user in the frontend as a little arrow indicating the facing of the vehicle. To get a meaningful value here the heading is mapped onto the track's orientation. This can be done by using the current track kilometer and the traveling direction of the vehicle. The latter is used to check if the original track orientation is returned or if it needs to be reversed (+180°).