Installing JSBSim

These are my notes on setting up JSBSim. I’m using JSBSim version 1.2.3 and Windows 11, but I’ll probably update this for Linux later.

Source code

Download and unzip https://github.com/JSBSim-Team/jsbsim/archive/refs/tags/v1.2.3.zip so that there’s a C:\jsbsim-1.2.3 folder.

Prerequisites

  • Windows 11
  • Visual Studio 2022 with C++ support
  • cmake v3

Build and install

Open a command prompt and run the following commands:

cd C:\jsbsim-1.2.3
mkdir build
cd build
cmake .. -DBUILD_SHARED_LIBS=ON -DUSE_SIMGEAR=OFF -DCMAKE_INSTALL_PREFIX=../install
cmake --build . --config Release
cmake --install . --config Release

Notes

If there are warnings about Cython and/or CxxTest, these can be ignored (unless you want to build the Python module or want to run the test suite). For my purposes, I’m just using the JSBSim API in C++ apps. I’m setting an environment variable JSBSIMDIR to C:\jsbsim-1.2.3\install and using it in my CMakeLists.txt file.

Example main.cpp using JSBSim

This mimics the JSBSim binary in a minimal way by allowing the selection and execution of a JSBSim XML script. But it gives me a starting point for running something basic in a realtime loop where I can query status of the simulated aircraft. Here, I’m just printing some basic information to the console.

#include <FGFDMExec.h>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <thread>
#include <string>

const double RAD2DEG = 180.0 / M_PI;
const double DEG2RAD = M_PI / 180.0;
const double dtSim = 0.01;  // 100Hz = 0.01s per step
const double publishInterval = 1.0; // 1Hz

using namespace std::chrono;
using std::cout, std::endl, std::cerr, std::setprecision, std::string, 
std::this_thread::sleep_until;

static void printUsage(std::string appName)
{
  cout
    << "Usage:\n" 
    << "  " << appName << "\n" 
    << "    --rootDir <rootDir>\n"
    << "    --script <script>\n  "
    << "    [--airCraftPath <airCraftPath> --enginePath <enginePath>]\n\n"
    << "Example: " 
    << "  " << appName << "\n"
    << "    --rootDir C:/jsbsim-1.2.3\n"
    << "    --script scripts/f16_test.xml\n"
    << "    --airCraftPath aircraft\n"
    << "    --enginePath engine\n"
    << endl;
}

int main(int argc, char* argv[])
{
  if (argc == 1)
  {
    printUsage(argv[0]);
    return 0;
  }
  
  string rootDir, script, airCraftPath{ "aircraft" }, enginePath{"engine"};

  for (int i = 1; i < argc - 1; ++i) 
  {
    if (string(argv[i]) == "--help" || string(argv[i]) == "--help")
    {
      printUsage(argv[0]);
      return 1;
    }
    if (string(argv[i]) == "--rootDir")
    {
      rootDir = argv[i + 1];
      cout << "Setting rootDir to " << rootDir << endl;
    }
    if (string(argv[i]) == "--script")
    {
      script = argv[i + 1];
      cout << "Setting script to " << script << endl;
    }
    if (string(argv[i]) == "--airCraftPath")
    {
      airCraftPath = argv[i + 1];
      cout << "Setting airCraftPath to " << airCraftPath << endl;
    }
    if (string(argv[i]) == "--enginePath")
    {
      enginePath = argv[i + 1];
      cout << "Setting enginePath to " << enginePath << endl;
    }
  }
  if (rootDir == "" || script == "")
  {
    cerr << "rootDir or script missing" << endl;
    printUsage(argv[0]);
    return 1;
  }

  JSBSim::FGFDMExec fdm;

  fdm.SetRootDir(SGPath(rootDir));
  fdm.SetAircraftPath(SGPath(airCraftPath));
  fdm.SetEnginePath(SGPath(enginePath));

  // Load the simulation script (relative to rootDir)
  if (!fdm.LoadScript(SGPath(script))) 
  {
    cerr << "Failed to load script" << endl;
    return 1;
  }

  fdm.RunIC();  // Initialize aircraft

  fdm.Setdt(dtSim);
  auto wallStartTime = steady_clock::now();
  double simTime = 0.0;
  double nextStatusUpdateTime = 0.0;

  while (fdm.Run()) 
  {
    simTime += dtSim;

    // Print status every second
    if (simTime >= nextStatusUpdateTime) 
    {
      double time_s = fdm.GetSimTime();
      double lat_deg = fdm.GetPropertyValue("position/lat-gc-deg");
      double lon_deg = fdm.GetPropertyValue("position/long-gc-deg");
      double alt_ft = fdm.GetPropertyValue("position/h-sl-ft");
      double heading_rad = fdm.GetPropertyValue("attitude/psi-rad");
      double pitch_rad = fdm.GetPropertyValue("attitude/theta-rad");
      double roll_rad = fdm.GetPropertyValue("attitude/phi-rad");
      double speed_fps = fdm.GetPropertyValue("velocities/u-fps");

      cout 
        << "T: " << setprecision(0)  << time_s << ", "
        << "P: [" << setprecision(3) << lat_deg << ", " << lon_deg << "] @ "
        << setprecision(0) << alt_ft << " ft, "
        << "H: " << heading_rad * RAD2DEG << " deg, "
        << "P: " << pitch_rad * RAD2DEG << " deg, "
        << "R: " << roll_rad * RAD2DEG << " deg, "
        << "V: " << speed_fps << " fps"
        << endl;

      nextStatusUpdateTime += publishInterval;
    }

    // Sync to wall-clock time
    sleep_until(wallStartTime + duration<double>(simTime));
  }
  return 0;
}

Example CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(JSBSimExample)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(JSBSIM_INSTALL_DIR "C:/jsbsim-1.2.3/install")
include_directories(${JSBSIM_INSTALL_DIR}/include/JSBSim)
link_directories(${JSBSIM_INSTALL_DIR}/lib)
add_executable(JSBSimExample main.cpp)
target_link_libraries(JSBSimExample jsbsim)

Additional aircraft models

To download flightgear aircraft models:

git clone https://git.code.sf.net/p/flightgear/fgdata flightgear-fgdata