Aircraft example¶
Prerequisites: 2 - Inheritance (class hierarchy, STL inheritance). Familiarity with HLA FOMs is helpful but not required.
Here you can see how to use an HLA-based definition to implement a simple aircraft simulation using
the update() method.
Interface and implementation¶
In the dummy_aircraft.stl file you can see how we inherit from rpr.Aircraft, and in the
implementation dummy_aircraft_impl.cpp you can see a (dummy) implementation of the update
function.
import "rpr/RPR-Physical_v2.0.xml"
package aircrafts;
// speed quantity
quantity<f64, m_per_s> Speed;
// speed vector in NED coordinates
struct NedSpeedStruct
{
north : Speed,
east : Speed,
down : Speed
}
// A dummy implementation of an aircraft
class DummyAircraft : extends rpr.Aircraft
{
var speed : NedSpeedStruct [writable];
}
// === dummy_aircraft_impl.cpp =========================================================================================
// Sen Infrastructure
// Released under the Apache License v2.0 (SPDX-License-Identifier Apache-2.0).
// See the LICENSE.txt file for more information.
// © Airbus SAS, Airbus Helicopters, and Airbus Defence and Space SAU/GmbH/SAS.
// =====================================================================================================================
// generated code
#include "rpr/rpr-base_v2.0.xml.h"
#include "stl/dummy_aircraft.stl.h"
// sen
#include "sen/core/base/compiler_macros.h"
#include "sen/core/meta/class_type.h"
#include "sen/kernel/component_api.h"
#include "sen/util/dr/detail/dead_reckoner_base.h"
#include "sen/util/dr/settable_dead_reckoner.h"
// std
#include <cmath>
#include <memory>
#include <utility>
namespace aircrafts
{
// An aircraft that updates its position.
class DummyAircraftImpl: public DummyAircraftBase<>
{
SEN_NOCOPY_NOMOVE(DummyAircraftImpl)
// type alias
using SettableDr = sen::util::SettableDeadReckoner<rpr::BaseEntityBase<>>;
using DeadReckonerBase = sen::util::DeadReckonerBase;
public:
using DummyAircraftBase<>::DummyAircraftBase;
~DummyAircraftImpl() override = default;
public:
void registered(sen::kernel::RegistrationApi& api) override
{
std::ignore = api;
// used to update the spatial field of the aircraft
settableDeadReckoner_ =
std::make_unique<SettableDr>(*this, sen::util::DrThreshold {0.0, 0.0f, sen::util::ReferenceSystem::world});
// commanded speed can be modified by the user; the next update applies it at its own time
speedGuard_ = onSpeedChanged({this, [this]() { speedChanged_ = true; }});
}
void update(sen::kernel::RunApi& runApi) override
{
const auto currentTime = runApi.getTime();
// seed the dead reckoner on the first update
if (!started_)
{
started_ = true;
speedChanged_ = false;
sen::util::GeodeticSituation initialSituation;
initialSituation.timeStamp = currentTime;
initialSituation.worldLocation = {40.0, 0.0, 10000.0};
const auto& [north, east, down] = getSpeed();
initialSituation.velocityVector = {north, east, down};
deadReckoner_.updateGeodeticSituation(std::move(initialSituation));
}
else if (speedChanged_)
{
speedChanged_ = false;
auto situation = deadReckoner_.geodeticSituation(currentTime);
const auto& [north, east, down] = getSpeed();
situation.velocityVector = {north, east, down};
deadReckoner_.updateGeodeticSituation(std::move(situation));
}
// move the entity using the dead reckoner with the specified speed
auto situation = deadReckoner_.geodeticSituation(currentTime);
// update the spatial using the settable dead reckoner
settableDeadReckoner_->setSpatial(situation);
}
private:
DeadReckonerBase deadReckoner_;
std::unique_ptr<SettableDr> settableDeadReckoner_;
sen::ConnectionGuard speedGuard_;
bool started_ = false;
bool speedChanged_ = false;
};
SEN_EXPORT_CLASS(DummyAircraftImpl)
} // namespace aircrafts
How to run it¶
Let's define what we want to run in our Sen kernel.
# $schema: ../base/schema.json
include:
- ../base/term.yaml
build:
- name: myComponent
freqHz: 30
imports: [aircrafts]
group: 3
objects:
- class: aircrafts.DummyAircraftImpl
name: myAircraft
bus: my.tutorial
speed:
north: 250
east: 0
down: 0
entityType: &theType
entityKind: 1
domain: 2
countryCode: 198
category: 1
subcategory: 3
specific: 0
extra: 0
alternateEntityType: *theType
entityIdentifier:
entityNumber: 1
federateIdentifier:
siteID: 1
applicationID: 1
This will open the term and tell Sen to instantiate one aircraft in the my.tutorial bus.
To run it, let's call sen run:
You should be able to repeatedly call my.tutorial.myAircraft.getSpatial and see the evolution of
the position.
Using the explorer¶
You can also load the explorer to have an easier time following the updates.
Using virtualized time¶
Let's now start the kernel in virtualized time mode by changing our config file.
# $schema: ../base/schema.json
include:
- 1_aircraft.yaml
# In this example we start the kernel with the virtualized time.
# Here the time will not advance until we explicitly request it.
# You will find that the kernel publishes an object called 'master'
# in the 'my.tutorial' bus, that you can use to control the virtual
# time (step, steps, advanceTime) and see how components advance
# accordingly. 'clock' beside it is the kernel's own clock.
kernel:
runMode: virtualTime
clockMaster: true
clockBus: my.tutorial
We can run it with the following command:
Now, if you execute my.tutorial.myAircraft.getSpatial you will see that there are no updates.
Let's now advance one cycle by using the clock API.
You can see how the component got triggered. What the term reads is what the aircraft last committed, so after the very first step it still shows the aircraft before its first update; step again, or advance by a second, and it is in place.
If you want to advance the time more, say 60 seconds:
This should run relatively fast, as we are not performing expensive computations.