Skip to main content

Combining CAN bus streams with other sensors



This page uses Studio code blocks so you can run the examples directly in the browser. You only need to sign up for SA Studio (it's free). Once you have done that you can execute the code blocks on this page.

Sometimes we want to use data from other sensors in combination with data from the CAN bus. Here we will show you how to combine streams from multiple sources into a single stream.

Let's say that we have both a GPS sensor and a CAN bus and that we want to combine the GPS readings with data from the CAN bus.

Simulate a GPS sensor stream​

To illustrate this we have provided a file gps-data.csv with recorded GPS values. Start by downloading the gps file:

http:download_file(
"https://assets.streamanalyze.com/docs/models/canbus-guide/j1939.dbc",
{}, sa_home() + "models/canbus-guide/gps-data.csv");
Not connected

To run this code block you must be logged in and your studio instance must be started.

We can simulate a GPS sensor by reading the GPS values from the file and emitting a stream of values with one measurement every 0.5 seconds.

create function gps_stream() -> Stream of Vector
as select Stream of gps
from Vector gps
where gps in csv:file_stream(sa_home() +
"models/canbus-guide/gps-data.csv",
"read",
0.5);
Not connected

To run this code block you must be logged in and your studio instance must be started.

You can try it by executing the following query:

gps_stream();
Not connected

To run this code block you must be logged in and your studio instance must be started.

Simulate a CAN bus data stream​

Now we need to simulate a CAN bus stream. We'll quickly step through the queries needed. If you have read the previous chapters in this guide you'll be familiar with most steps.

First load the CAN bus model.

system_models:load("canbus");
Not connected

To run this code block you must be logged in and your studio instance must be started.

Translate the DBC file to OSQL format.

can:import_dbc(sa_home() + "models/canbus-guide/j1939.dbc",
sa_home() + "models/canbus-guide/j1939_dbc.osql",
[]);
Not connected

To run this code block you must be logged in and your studio instance must be started.

Load the translated signal definitions.

load_osql(sa_home() + "models/canbus-guide/j1939_dbc.osql");
Not connected

To run this code block you must be logged in and your studio instance must be started.

Create a function that generates a data stream from a recorded CAN file.

create function j1939_can_data_stream() -> Stream of Vector
as can:playback_socketcan(sa_home() +
'models/canbus-guide/j1939-can-data.log');
Not connected

To run this code block you must be logged in and your studio instance must be started.

Create a wrapper function that makes the CAN bus emit recorded values every 0.1 seconds.

create function can_data_stream_wrapper() -> Stream of Vector
as select stream of v
from Vector v
where v in heartbeat_wrap(j1939_can_data_stream(), 0.1);
Not connected

To run this code block you must be logged in and your studio instance must be started.

Then we need to set the CAN data source.

set bus(typenamed("can:signal")) = #'can_data_stream_wrapper';
Not connected

To run this code block you must be logged in and your studio instance must be started.

Now we can try our simulated CAN bus.

can:ts_signal_stream(["EEC1_EngineSpeed", "CCVS1_WheelBasedVehicleSpeed"]);
Not connected

To run this code block you must be logged in and your studio instance must be started.

Combining the CAN bus and GPS streams​

Now we have two data streams can:ts_signal_stream() and gps_stream(). The CAN bus stream has a timestamp but we will provide new timestamps for our combined stream, so we create a wrapper function that strips the CAN bus stream of its timestamp.

create function can_stream() -> Stream of Vector
as select Stream of can
from Vector can, Timeval of Vector tv
where tv in can:ts_signal_stream(["EEC1_EngineSpeed", "CCVS1_WheelBasedVehicleSpeed"])
and can = value(tv);
Not connected

To run this code block you must be logged in and your studio instance must be started.

Check that you only get the CAN bus values now by executing the following query:

can_stream();
Not connected

To run this code block you must be logged in and your studio instance must be started.

Now we can use pivot() to combine the two streams. We create a function that combines the two streams and adds a timestamp to the resulting stream.

create function can_and_gps() -> Stream of Timeval of Vector
as select Stream of ts(concat(can,gps))
from Vector can, Vector gps, Vector v
where v in pivot([can_stream(), gps_stream()], [[-1],[-1]])
and [can, gps] = v;
Not connected

To run this code block you must be logged in and your studio instance must be started.

Some details of what the function does:

2: Construct the output by concatenating the CAN vector with the GPS vector and puth the result in a Timeval with ts().

4: Use pivot() to combine the stream from can_stream() with the stream from gps_stream() and use [-1] as default value for both streams (used if a stream has not emitted any value yet).

5: Use multi bind to assign can to the vector from the CAN bus stream and gps to the vector from the GPS stream.

Now try the combined stream.

can_and_gps();
Not connected

To run this code block you must be logged in and your studio instance must be started.

The output from can_and_gps() is a stream of Timeval where the data vector is the values from the CAN bus and GPS streams.

             ┌─── Timestamp
│ ┌─── EEC1_EngineSpeed
│ │ ┌─── CCVS1_WheelBasedVehicleSpeed
│ │ │ ┌─── Latitude
│ │ │ | ┌─── Longitude
↓ ↓ ↓ ↓ ↓
ts(|2022-03-28T19:47:22.556Z|, [1424.625,19.8046875,17.636865,59.845127])