This guide shows an Javascript version of the previously shown C++ based implementation of the Data Processor and the Data Generator. In this tutorial the data generation part is realized with two Javascript Filters and the processing part is realized with a QtQuick Filter - this time we do not log the calcualted values, we plot them in a custom visualization solution. The functional logic of this example is split into the following parts:
Both data generators are based on JavaScript Filters. The first one creates velocity values ranging from 5 to 100, the second Filter creates yaw rate values between 3 and 20.
This is the velocity.js
for the Velocity Generator:
// velocity data sample generator
var velocity_type = types.createDefinition("velocity")
.add("value", "tFloat32")
var output = filter.createOutputPin("velocity", velocity_type)
var velo = 10
var delta = 5
filter.createRunner("generate_velocity").trigger.connect(function(timestamp)
{
velo += delta
if(velo < 10 || velo > 95){
delta *= -1;
}
output.write(timestamp, { value: velo })
})
This is the yaw_rate.js
for the Yaw Rate Generator:
// yaw rate data sample generator
var yaw_rate_type = types.createDefinition("yaw_rate")
.add("value", "tFloat32")
var output = filter.createOutputPin("yaw_rate", yaw_rate_type)
var yaw = 6
var delta = 3
filter.createRunner("generate_yaw_rate").trigger.connect(function(timestamp)
{
yaw += delta
if(yaw < 6 || yaw > 17){
delta *= -1;
}
output.write(timestamp, { value: yaw })
})
The Display uses a QtQuick Filter to create three different kinds of displays. It receives velocity and yaw rate data samples and calculates the corresponding kappa values. The resulting kappa values are visualized using QtQuick elements which plot the data in three different charts. Have a look at the comments in the code as well.
This is the display.qml
for the Display:
import QtQuick 2.9
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.0
import QtCharts 2.0
import QtDataVisualization 1.2
// this is required for ImageItem
import Adtf 1.0
// you can define any QML component you like
Item
{
// use the onCompleted handler to setup all aspects of your Filter.
// here you can use all the functionality available within the plain javascript Filter.
Component.onCompleted:
{
var velocityInput = filter.createInputPin("velocity")
var yawRateInput = filter.createInputPin("yaw_rate")
velocityInput.sample.connect(function(velocitySample)
{
var yawRateSample = yawRateInput.getLastSample()
if (yawRateSample)
{
var timestamp = velocitySample.timestamp / 1000000000.0; // seconds
var velocity = velocitySample.data.value;
var yawRate = yawRateSample.data.value;
var kappa = yawRate / velocity;
// Kappa values are multiplied by 10 to improve visualization
var displayKappa = kappa * 10
// Update 2D Bar View
// Clear view on each update to show only current values
barSeries.clear();
barSeries.append("Velocity", [velocity]);
barSeries.append("Yaw rate", [yawRate]);
barSeries.append("Kappa", [displayKappa]);
// Update 2D Lines View
// Lines View visualizes data sequentially
// -> Always update even if the view is not active (visible)
veloSeries.append(timestamp, velocity);
yawSeries.append(timestamp, yawRate);
kappaSeries.append(timestamp, displayKappa);
// Update 3D Scatter View
scatterModel.append({ "xPos": velocity, "yPos": yawRate, "zPos": displayKappa});
}
})
}
RowLayout{
anchors.fill: parent
spacing: 5
// 2D Bar View element
// Visible on startup
ChartView {
id: barView
title: "2D Bar View"
width: 400
height: 400
BarSeries {
id: barSeries
axisX: BarCategoryAxis { categories: ["Current values" ] }
axisY: ValueAxis {
titleText: "Values"
titleVisible: true
min: 0
max: 100
}
}
}
// 2D Lines View element
// Invisible on startup
// Various LineSeries elements for each sample type
ChartView {
id: linesView
title: "2D Lines View"
width: 400
height: 400
antialiasing: true
// LineSeries for velocity sample values
LineSeries {
id: veloSeries
name: "Velocity"
axisX: ValueAxis {
titleText: "Timestamp"
titleVisible: true
min: 0
max: 60
}
axisY: ValueAxis {
titleText: "Values"
titleVisible: true
min: 0
max: 100
}
}
// LineSeries for yaw rate sample values
LineSeries {
id: yawSeries
name: "Yaw rate"
}
// LineSeries for kappa sample values
LineSeries {
id: kappaSeries
name: "Kappa"
}
}
// 3D Scatter View element
// Invisible on startup
// Uses a model to organize data
Scatter3D {
id: scatterView
width: 400
height: 400
// Custom axis elements
axisX: ValueAxis3D {
title: "Velocity"
titleVisible: true
}
axisY: ValueAxis3D {
title: "Yaw rate"
titleVisible: true
}
axisZ: ValueAxis3D {
title: "Kappa"
titleVisible: true
}
Scatter3DSeries {
ItemModelScatterDataProxy {
itemModel: scatterModel
// Mapping model roles to scatter series item coordinates.
xPosRole: "xPos"
yPosRole: "yPos"
zPosRole: "zPos"
}
}
}
ListModel {
id: scatterModel
}
}
}
In this step we create a new project.
We add all three Filters to the default Filter Graph and connect the Filter as you can see in figure "Filter Graph".
Velocity Generator
as Javascript Filter: set the velocity.js
using right-click on the ADTF Component and Open EditorYaw Rate Generator
as Javascript Filter: set the yaw_rate.js
using right-click on the ADTF Component and Open EditorDisplay
as QtQuick Filter: set the display.qml
using right-click on the ADTF Component and Open Editor
Then add a Timer Runner
and connect it to both Generators
.
Finally connect both Generators
to the respective InPin
of the Display
with a Sample Stream between.
-e connect
in combination with argument --runner-pin
, in this example:
adtf_config_tool.exe --session "path/to/your/session/default_session.adtfsession" -e connect "default_graph/graphs/default_filter_graph/active_runners/Timer Runner" "default_graph/graphs/default_filter_graph/filters/Velocity Generator" --runner-pin generate_velocity
adtf_config_tool.exe --session "path/to/your/session/default_session.adtfsession" -e connect "default_graph/graphs/default_filter_graph/active_runners/Timer Runner" "default_graph/graphs/default_filter_graph/filters/Yaw Rate Generator" --runner-pin generate_yaw_rate
Note that you have to set up the graph with all ADTF Components ignoring the runner connection, save your work and close the ADTF Configuration Editor.
After editing the the ADTF Session by using the ADTF Config Tool as explained, you can restart the ADTF Configuration Editor, reload the ADTF Project
and the ADTF Filter Graph will look as displayed.
Because we make use of Qt Charts
and Qt Data Visualization
which are both not part of the 3rd party Qt runtime binaries within the ADTF delivery due to license issues
we have to copy the required dependencies and need to reference the missing libraries in the section Platform dependencies within the System Editor for running this example.
A complete Qt installation contains the missing files within its bin
folder. Have a look here which version is required and where you can get a full Qt developer package.
Afterwards you can copy the missing libraries to your <ADTF_DIR>/3rdparty/qt5
folder, which are:
<QT_DIR>/bin/Qt5Charts.dll
to <ADTF_DIR>/3rdparty/qt5/Qt5Charts.dll
<QT_DIR>/bin/Qt5Chartsd.dll
to <ADTF_DIR>/3rdparty/qt5/Qt5Chartsd.dll
<QT_DIR>/bin/Qt5DataVisualization.dll
to <ADTF_DIR>/3rdparty/qt5/Qt5DataVisualization.dll
<QT_DIR>/bin/Qt5DataVisualizationd.dll
to <ADTF_DIR>/3rdparty/qt5/Qt5DataVisualizationd.dll
And to access within qml script we need to copy as well:
<QT_DIR>/qml/Qt5Charts/
to <ADTF_DIR>/3rdparty/qt5/qml/Qt5Charts/
<QT_DIR>/qml/Qt5DataVisualization/
to <ADTF_DIR>/3rdparty/qt5/qml/Qt5DataVisualization/
Then we have to reference the libraries within our ADTF System. First we add the following two libraries to your <platform>_release:
<ADTF_DIR>/bin/debug/adtf_launcher.exe
), dependencies defined in <platform>_debug
are available otherwise the dependencies defined in <platform>_release are used.
Finally, we can run the session and visualize our data.
Have a look at the ADTF 2 Support Toolbox to see how to connect ADTF 3 and ADTF 2 Filters.