Skip to content

Aircraft example

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 aircraft.stl file you can see how we inherit from rpr.Aircraft, and in the implementation dummy_aircraft.cpp you can see a (dummy) implementation of the update function.

import "rpr/RPR-Physical_v2.0.xml"

package aircrafts;

quantity<f64, m_per_s> Speed;

// A dummy implementation of an aircraft
class DummyAircraft : extends rpr.Aircraft
{
  var speed : Speed [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/dead_reckoner.h"
#include "sen/util/dr/settable_dead_reckoner.h"

// std
#include <cmath>
#include <memory>

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 DeadReckoner = sen::util::DeadReckoner<rpr::BaseEntityBase<>>;

public:
  using DummyAircraftBase<>::DummyAircraftBase;
  ~DummyAircraftImpl() override = default;

public:
  void registered(sen::kernel::RegistrationApi& /*api*/) override
  {
    // the DeadReckoner is used to update the world location of the aircraft given the configured speed. This is not
    // the regular use of the DeadReckoner but it comes in handy for the example.
    deadReckoner_ = std::make_unique<DeadReckoner>(*this);

    // the SettablaDeadReckoner is used to update the Spatial field of the aircraft
    settableDeadReckoner_ = std::make_unique<SettableDr>(*this);
  }

  void update(sen::kernel::RunApi& api) override
  {
    // move the entity using the dead reckoner with the specified speed
    auto situation = deadReckoner_->geodeticSituation(api.getTime());

    // initialize the situation of the entity in the first iteration
    if (!init_)
    {
      situation.worldLocation = {40.0, 0.0, 10000.0};
      init_ = true;
    }

    // update the speed (can be changed while the model is running)
    situation.velocityVector = {getSpeed(), 0, 0};

    // update the spatial using the settable dead reckoner
    settableDeadReckoner_->setSpatial(situation);
  }

private:
  bool init_ = false;
  std::unique_ptr<DeadReckoner> deadReckoner_;
  std::unique_ptr<SettableDr> settableDeadReckoner_;
};

SEN_EXPORT_CLASS(DummyAircraftImpl)

}  // namespace aircrafts

How to run it

Let's define what we want to run in our Sen kernel.

Configuration file
# $schema: ../base/schema.json
include:
  - ../base/shell.yaml

build:
  - name: myComponent
    freqHz: 30
    imports: [aircrafts]
    group: 3
    objects:
      - class: aircrafts.DummyAircraftImpl
        name: myAircraft
        bus: my.tutorial
        speed: 250
        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:

sen run config/3_aircraft/1_aircraft.yaml

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.

sen run config/3_aircraft/2_aircraft_exp.yaml

Using virtualized time

Let's now start the kernel in virtualized time mode by changing our config file.

Configuration 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:

sen run config/3_aircraft/3_aircraft_virtual_time.yaml

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.

my.tutorial.master.step
my.tutorial.myAircraft.getSpatial

You can see how the component got triggered.

If you want to advance the time more, say 60 seconds:

my.tutorial.master.advanceTime "60 s"

This should run relatively fast, as we are not performing expensive computations.