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
speedGuard_ = onSpeedChanged({this,
[this]()
{
auto situation = deadReckoner_.geodeticSituation(api_->getTime());
const auto& [north, east, down] = getSpeed();
situation.velocityVector = {north, east, down};
deadReckoner_.updateGeodeticSituation(std::move(situation));
}});
}
void update(sen::kernel::RunApi& runApi) override
{
api_ = &runApi;
if (const auto currentTime = runApi.getTime(); currentTime == runApi.getStartTime())
{
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));
}
// move the entity using the dead reckoner with the specified speed
auto situation = deadReckoner_.geodeticSituation(runApi.getTime());
// update the spatial using the settable dead reckoner
settableDeadReckoner_->setSpatial(situation);
}
private:
DeadReckonerBase deadReckoner_;
std::unique_ptr<SettableDr> settableDeadReckoner_;
sen::ConnectionGuard speedGuard_;
sen::kernel::RunApi* api_ = nullptr;
};
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 a shell 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 will be publishing an object called
# 'clock' in the 'my.tutorial' bus, that you can use to control the
# virtual time and see how components advance accordingly.
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.
If you want to advance the time more, say 60 seconds:
This should run relatively fast, as we are not performing expensive computations.