{ "cells": [ { "cell_type": "markdown", "id": "a1b2c3d4-0001-4000-8000-000000000001", "metadata": {}, "source": [ "# JSBSim Hello World\n", "\n", "This notebook provides a minimal introduction to the [JSBSim](https://github.com/JSBSim-Team/jsbsim) Python API.\n", "\n", "JSBSim is a fully featured, open-source, multi-platform, flight dynamics model (FDM) written in C++ and\n", "exposed to Python via a Cython wrapper. The same FDM is used in FlightGear, OpenPilot, Paparazzi UAV,\n", "and many other simulators.\n", "\n", "## What we will do\n", "\n", "1. Import the `jsbsim` Python module.\n", "2. Create an `FGFDMExec` instance – the top-level JSBSim object.\n", "3. Load the bundled **Cessna 172P** aircraft model.\n", "4. Set initial conditions and trim the aircraft.\n", "5. Step the simulation and read back properties.\n", "6. Print a summary of the simulation state.\n", "\n", "### Install\n", "\n", "```bash\n", "pip install jsbsim\n", "```" ] }, { "cell_type": "markdown", "id": "a1b2c3d4-0001-4000-8000-000000000002", "metadata": {}, "source": [ "## 1. Import and version check" ] }, { "cell_type": "code", "execution_count": 1, "id": "colab-install", "metadata": {}, "outputs": [], "source": [ "# If running on Google Colab, install the required packages.\n", "\n", "import sys\n", "\n", "if 'google.colab' in sys.modules:\n", " print('Running on Google Colab \\u2013 installing jsbsim \\u2026')\n", " !pip install jsbsim" ] }, { "cell_type": "code", "execution_count": 2, "id": "a1b2c3d4-0001-4000-8000-000000000003", "metadata": { "execution": { "iopub.execute_input": "2026-04-03T12:36:19.903261Z", "iopub.status.busy": "2026-04-03T12:36:19.903081Z", "iopub.status.idle": "2026-04-03T12:36:19.961000Z", "shell.execute_reply": "2026-04-03T12:36:19.959242Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "JSBSim version: 1.3.0\n" ] } ], "source": [ "import jsbsim\n", "\n", "print(f\"JSBSim version: {jsbsim.__version__}\")" ] }, { "cell_type": "markdown", "id": "a1b2c3d4-0001-4000-8000-000000000004", "metadata": {}, "source": [ "## 2. Create the Flight Dynamics Model executor\n", "\n", "`FGFDMExec` is the main JSBSim class. Passing `None` as the `root_dir`\n", "argument makes it use the aircraft and engine data bundled with the Python wheel." ] }, { "cell_type": "code", "execution_count": 3, "id": "a1b2c3d4-0001-4000-8000-000000000005", "metadata": { "execution": { "iopub.execute_input": "2026-04-03T12:36:19.962988Z", "iopub.status.busy": "2026-04-03T12:36:19.962762Z", "iopub.status.idle": "2026-04-03T12:36:19.969001Z", "shell.execute_reply": "2026-04-03T12:36:19.968010Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", " JSBSim Flight Dynamics Model v1.3.0 Apr 9 2026 10:00:08\n", " [JSBSim-ML v2.0]\n", "\n", "JSBSim startup beginning ...\n", "\n", "FDM created successfully\n" ] } ], "source": [ "fdm = jsbsim.FGFDMExec(None) # None → use data bundled with the pip package\n", "fdm.set_debug_level(0) # Suppress verbose JSBSim console output\n", "\n", "if fdm is not None:\n", " print(\"FDM created successfully\")\n", "else:\n", " print(\"Failed to create FDM\")" ] }, { "cell_type": "markdown", "id": "a1b2c3d4-0001-4000-8000-000000000006", "metadata": {}, "source": [ "## 3. Load an aircraft model\n", "\n", "The JSBSim wheel ships with many aircraft models. Here we use the **c172p** (Cessna 172P)." ] }, { "cell_type": "code", "execution_count": 4, "id": "a1b2c3d4-0001-4000-8000-000000000007", "metadata": { "execution": { "iopub.execute_input": "2026-04-03T12:36:19.970637Z", "iopub.status.busy": "2026-04-03T12:36:19.970466Z", "iopub.status.idle": "2026-04-03T12:36:19.980358Z", "shell.execute_reply": "2026-04-03T12:36:19.979381Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "load_model('c172p') → True\n" ] } ], "source": [ "aircraft = 'c172p'\n", "result = fdm.load_model(aircraft)\n", "\n", "print(f\"load_model('{aircraft}') → {result}\")" ] }, { "cell_type": "markdown", "id": "a1b2c3d4-0001-4000-8000-000000000008", "metadata": {}, "source": [ "## 4. Set initial conditions\n", "\n", "JSBSim exposes all model state through a *property tree*. Initial-condition\n", "properties are prefixed with `ic/`." ] }, { "cell_type": "code", "execution_count": 5, "id": "a1b2c3d4-0001-4000-8000-000000000009", "metadata": { "execution": { "iopub.execute_input": "2026-04-03T12:36:19.982656Z", "iopub.status.busy": "2026-04-03T12:36:19.981923Z", "iopub.status.idle": "2026-04-03T12:36:19.987466Z", "shell.execute_reply": "2026-04-03T12:36:19.986553Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Initial conditions applied\n" ] } ], "source": [ "# Initial altitude above sea level [ft]\n", "fdm['ic/h-sl-ft'] = 3000.0\n", "\n", "# Initial calibrated airspeed [knots]\n", "fdm['ic/vc-kts'] = 80.0\n", "\n", "# Flight path angle [deg] (0 = level flight)\n", "fdm['ic/gamma-deg'] = 0.0\n", "\n", "# Heading [deg]\n", "fdm['ic/psi-true-deg'] = 45.0\n", "\n", "# Apply the initial conditions\n", "fdm.run_ic()\n", "\n", "print(\"Initial conditions applied\")" ] }, { "cell_type": "markdown", "id": "a1b2c3d4-0001-4000-8000-000000000010", "metadata": {}, "source": [ "## 5. Trim the aircraft\n", "\n", "Trimming finds a steady-state flight condition where the net forces and moments\n", "acting on the aircraft are (approximately) zero. JSBSim provides a built-in\n", "full trim algorithm." ] }, { "cell_type": "code", "execution_count": 6, "id": "a1b2c3d4-0001-4000-8000-000000000011", "metadata": { "execution": { "iopub.execute_input": "2026-04-03T12:36:19.989308Z", "iopub.status.busy": "2026-04-03T12:36:19.989131Z", "iopub.status.idle": "2026-04-03T12:36:20.002627Z", "shell.execute_reply": "2026-04-03T12:36:20.001651Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Trim complete\n", " Throttle : 62.157 (perc.)\n", " Elevator trim : 5.2799 (perc.)\n", " Angle of attack : 2.53 deg\n" ] } ], "source": [ "# Start engines\n", "fdm['propulsion/set-running'] = -1\n", "\n", "# Request a full trim (blocks until convergence)\n", "fdm['simulation/do_simple_trim'] = 1\n", "\n", "print(\"Trim complete\")\n", "print(f\" Throttle : {100.*fdm['fcs/throttle-cmd-norm[0]']:.3f} (perc.)\")\n", "print(f\" Elevator trim : {100.*fdm['fcs/pitch-trim-cmd-norm']:.4f} (perc.)\")\n", "print(f\" Angle of attack : {fdm['aero/alpha-deg']:.2f} deg\")" ] }, { "cell_type": "markdown", "id": "a1b2c3d4-0001-4000-8000-000000000012", "metadata": {}, "source": [ "## 6. Step the simulation and read properties\n", "\n", "`fdm.run()` advances the simulation by one time step (`fdm.get_delta_t()` seconds,\n", "typically 1/120 s). Properties are read with dictionary-style access." ] }, { "cell_type": "code", "execution_count": 7, "id": "a1b2c3d4-0001-4000-8000-000000000013", "metadata": { "execution": { "iopub.execute_input": "2026-04-03T12:36:20.004305Z", "iopub.status.busy": "2026-04-03T12:36:20.004131Z", "iopub.status.idle": "2026-04-03T12:36:20.012647Z", "shell.execute_reply": "2026-04-03T12:36:20.011679Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Time step Delta t = 0.008333 s (120.0 Hz)\n", "\n", "Simulation state after 1 second:\n", " Simulation time : 1.000 s\n", " Altitude (MSL) : 3000.0 ft\n", " True airspeed : 141.1 fps\n", " Pitch angle : 2.53 deg\n", " Roll angle : 0.06 deg\n", " Heading : 45.0 deg\n" ] } ], "source": [ "dt = fdm.get_delta_t()\n", "print(f\"Time step Delta t = {dt:.6f} s ({1/dt:.1f} Hz)\")\n", "\n", "# Run for 1 second\n", "n_steps = int(1.0 / dt)\n", "for _ in range(n_steps):\n", " fdm.run()\n", "\n", "print(\"\\nSimulation state after 1 second:\")\n", "print(f\" Simulation time : {fdm['simulation/sim-time-sec']:.3f} s\")\n", "print(f\" Altitude (MSL) : {fdm['position/h-sl-ft']:.1f} ft\")\n", "print(f\" True airspeed : {fdm['velocities/vt-fps']:.1f} fps\")\n", "print(f\" Pitch angle : {fdm['attitude/theta-deg']:.2f} deg\")\n", "print(f\" Roll angle : {fdm['attitude/phi-deg']:.2f} deg\")\n", "print(f\" Heading : {fdm['attitude/psi-deg']:.1f} deg\")" ] }, { "cell_type": "markdown", "id": "a1b2c3d4-0001-4000-8000-000000000014", "metadata": {}, "source": [ "## 7. Explore the property tree\n", "\n", "JSBSim exposes hundreds of properties. The `query_property_catalog` method\n", "searches by substring." ] }, { "cell_type": "code", "execution_count": 8, "id": "a1b2c3d4-0001-4000-8000-000000000015", "metadata": { "execution": { "iopub.execute_input": "2026-04-03T12:36:20.014423Z", "iopub.status.busy": "2026-04-03T12:36:20.014252Z", "iopub.status.idle": "2026-04-03T12:36:20.020425Z", "shell.execute_reply": "2026-04-03T12:36:20.019469Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found 40 velocity properties (first 15):\n", " velocities/h-dot-fps = 0.0013\n", " velocities/v-north-fps = 99.7897\n", " velocities/v-east-fps = 99.7803\n", " velocities/v-down-fps = -0.0013\n", " velocities/u-fps = 140.9793\n", " velocities/v-fps = -0.0000\n", " velocities/w-fps = 6.2395\n", " velocities/p-rad_sec = 0.0000\n", " velocities/q-rad_sec = 0.0000\n", " velocities/r-rad_sec = -0.0000\n", " velocities/pi-rad_sec = 0.0001\n", " velocities/qi-rad_sec = -0.0000\n", " velocities/ri-rad_sec = 0.0000\n", " velocities/eci-x-fps = -0.1255\n", " velocities/eci-y-fps = 1625.9213\n" ] } ], "source": [ "# Find all velocity-related properties\n", "catalog_str = fdm.query_property_catalog('velocities')\n", "velocity_props = [p for p in catalog_str.split('\\n') if p.strip()]\n", "\n", "print(f\"Found {len(velocity_props)} velocity properties (first 15):\")\n", "for p in velocity_props[:15]:\n", " name = p.strip().split(' ')[0] # strip access flag suffix\n", " try:\n", " value = fdm[name]\n", " print(f\" {name:<45s} = {value:.4f}\")\n", " except Exception:\n", " print(f\" {name:<45s} (unreadable)\")" ] }, { "cell_type": "markdown", "id": "a1b2c3d4-0001-4000-8000-000000000016", "metadata": {}, "source": [ "## Summary\n", "\n", "In this notebook you learned how to:\n", "\n", "* Create an `FGFDMExec` instance.\n", "* Load an aircraft model bundled with the JSBSim Python wheel.\n", "* Set initial conditions and perform a trim.\n", "* Advance the simulation with `fdm.run()` and read properties.\n", "* Query the property tree.\n", "\n", "**Next:** [02_jsbsim_flight_simulation.ipynb](02_jsbsim_flight_simulation.ipynb) – a longer simulation with time-history plots." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.15" } }, "nbformat": 4, "nbformat_minor": 5 }