Skip to content

Add FGNullAtmosphere and extend LoadPlanet atmosphere factory#1462

Merged
seanmcleod70 merged 9 commits into
JSBSim-Team:masterfrom
PteroLabsAI:feature/planetary-atmosphere-support
May 25, 2026
Merged

Add FGNullAtmosphere and extend LoadPlanet atmosphere factory#1462
seanmcleod70 merged 9 commits into
JSBSim-Team:masterfrom
PteroLabsAI:feature/planetary-atmosphere-support

Conversation

@AlexanderRex

Copy link
Copy Markdown
Contributor

Adds atmosphere support so JSBSim can simulate vehicles on Mars and the Moon.

What changes

  • <atmosphere model="Mars"/> and <atmosphere model="Null"/> are now valid in LoadPlanet, alongside the existing "MSIS".
  • New FGNullAtmosphere class: a vacuum. Pressure and density are zero; temperature is set to the cosmic microwave background (~2.7 K) just so reads return a finite value. Use it for the Moon, Mercury, asteroids.

Bugs found while wiring this up

  • LoadPlanet was unbinding the old atmosphere after constructing the new one. But FGAtmosphere's constructor calls bind(), and at that point the old model still owned atmosphere/T-R, atmosphere/P-psf, etc., so the new model's ties silently failed, and the FDM kept reading stale values from the old instance. Fixed by unbinding first.
  • FGMars::Calculate was returning temperature in °F, but Density = P / (Reng·T) needs absolute temperature. Surface T came out around −26 °F → negative absolute → negative density → NaN on the first step. Added the +459.67 F→R offset to both lapse branches.
  • FGMars and FGNullAtmosphere constructors were calling bind() themselves, but FGAtmosphere's base constructor already does. The duplicate was harmless at FDM startup but produced a flood of Failed to tie property warnings once the factory started swapping atmosphere models at runtime. Removed.

Tests

Three new cases in tests/TestPlanet.py:

  • test_mars_atmosphere: via the planet <include> path; checks T = 433.99 R, P = 14.62 psf, ρ ≈ 1.43e-5 slugs/ft³, plus Mars radius and surface gravity.
  • test_load_Mars_atmosphere: same expected values via runtime LoadPlanet.
  • test_load_Null_atmosphere: vacuum, P = ρ = 0, T = 4.905 R.

tests/moon.xml now declares <atmosphere model="Null"/> (it's the Moon, no atmosphere). New tests/mars.xml. Full TestPlanet suite (8/8) passes.

- FGNullAtmosphere: vacuum model for airless bodies (Moon)
- Atmosphere factory supports "Mars" and "Null" in planet XML
- Planet configs: moon.xml, mars.xml
The Mars atmosphere model computed temperature in Fahrenheit but passed
it directly to the ideal gas law (Density = P/(R*T)) which requires
Rankine. This produced negative density at all altitudes, causing NaN
on the first simulation step.

Add +459.67 offset (F→R conversion) before density calculation.
- test_mars_atmosphere: include planet, verify surface T/P/density and gravity
- test_load_Mars_atmosphere: same via runtime LoadPlanet
- test_load_Null_atmosphere: vacuum P=0, rho=0, T~2.725 K
AlexanderRex added a commit to PteroLabsAI/jsbsim that referenced this pull request May 18, 2026
- LoadPlanet: unbind old atmosphere before constructing the replacement.
  FGAtmosphere's ctor calls bind(), so the previous order silently failed
  to tie the new model's properties (atmosphere/T-R, P-psf, etc.) and the
  FDM kept reading stale values from the old instance.
- FGMars / FGNullAtmosphere: remove redundant bind() in ctor. FGAtmosphere's
  base ctor already calls it; the duplicate produced 'Failed to tie' warnings
  once the factory began swapping models at runtime.
- TestPlanet: add Mars and Null atmosphere tests.
@codecov

codecov Bot commented May 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 25.10%. Comparing base (e5446e1) to head (6d2e26a).

Files with missing lines Patch % Lines
src/FGFDMExec.cpp 0.00% 9 Missing ⚠️
src/models/atmosphere/FGMars.h 0.00% 3 Missing ⚠️
src/models/atmosphere/FGMars.cpp 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1462      +/-   ##
==========================================
- Coverage   25.15%   25.10%   -0.05%     
==========================================
  Files         169      171       +2     
  Lines       18811    18843      +32     
==========================================
  Hits         4731     4731              
- Misses      14080    14112      +32     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@bcoconni

Copy link
Copy Markdown
Member

Thanks for the PR @AlexanderRex.

Even though it makes sense to restore the Mars atmosphere, I am not so sure about adding the "null" atmosphere. In JSBSim, an empty <aerodynamics/> section makes the vehicle behave as if there were no atmosphere. And even with the null atmosphere, you would not specify an <aerodynamics/> section anyway so I am not sure about the added value of the null atmosphere model beyond being explicit ? But maybe I am missing something ?

And also I am bit worried about setting the pressure and density to zero. Has the code been reviewed to make sure that this would not lead to divisions by zero or any other similar problems ?

@bcoconni bcoconni left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest a few minor changes in addition to the points I have mentioned in another comment.

Comment thread src/FGFDMExec.cpp Outdated
Comment thread src/FGFDMExec.cpp
Comment thread src/FGFDMExec.cpp Outdated
@bcoconni

bcoconni commented May 19, 2026

Copy link
Copy Markdown
Member

Bugs found while wiring this up

LoadPlanet was unbinding the old atmosphere after constructing the new one. But FGAtmosphere's constructor calls bind(), and at that point the old model still owned atmosphere/T-R, atmosphere/P-psf, etc., so the new model's ties silently failed, and the FDM kept reading stale values from the old instance. Fixed by unbinding first.

I am not sure about what you exactly mean by that ? Your statement is worded as if you had found an error in JSBSim, but the code in master branch is already meeting this requirement (unbinding before constructing):

jsbsim/src/FGFDMExec.cpp

Lines 792 to 795 in 5e23ead

// Replace the existing atmosphere model
instance->Unbind(Models[eAtmosphere]);
Models[eAtmosphere] = std::make_shared<FGMSIS>(this);
Atmosphere = static_cast<FGAtmosphere*>(Models[eAtmosphere].get());

And would the readings be frozen then the test test_load_MSIS_atmosphere would fail

def test_load_MSIS_atmosphere(self):
tripod = FlightModel(self, 'tripod')
MSIS_file = self.sandbox.path_to_jsbsim_file('tests/MSIS.xml')
self.fdm = tripod.start()
self.fdm.load_planet(MSIS_file, False)
self.fdm['ic/h-sl-ft'] = 0.0
self.fdm['ic/long-gc-deg'] = -70.0
self.fdm['ic/lat-geod-deg'] = 60.0
self.fdm.run_ic()
self.assertAlmostEqual(self.fdm['atmosphere/T-R']*5/9, 281.46476, delta=1E-5)
self.assertAlmostEqual(self.fdm['atmosphere/rho-slugs_ft3']/0.001940318, 1.263428, delta=1E-6)
self.assertAlmostEqual(self.fdm['atmosphere/P-psf'], 2132.294, delta=1E-3)

@AlexanderRex

Copy link
Copy Markdown
Contributor Author

Thanks for the PR @AlexanderRex.

Even though it makes sense to restore the Mars atmosphere, I am not so sure about adding the "null" atmosphere. In JSBSim, an empty <aerodynamics/> section makes the vehicle behave as if there were no atmosphere. And even with the null atmosphere, you would not specify an <aerodynamics/> section anyway so I am not sure about the added value of the null atmosphere model beyond being explicit ? But maybe I am missing something ?

And also I am bit worried about setting the pressure and density to zero. Has the code been reviewed to make sure that this would not lead to divisions by zero or any other similar problems ?

Hi @bcoconni, thanks for your review!

The unbind/construct order on master is already correct. What I described
as a "bug" was actually a regression introduced by my own refactor (I
pulled make_shared out of the if (model == "MSIS") block and ran it
before Unbind); I caught it during testing and reverted to master's
order, but the PR description framed the reversal as a JSBSim fix.
Sorry for the confusion, I will update the wording.

I also check NullAtmosphere again and yeah it will trigger zero division, so I think to remove it from this PR and and keep this change focused on restoring Mars.
We can discuss a vacuum model separately if there's interest :) (I thought to bring moon demo with Apollo model)

Will implement your suggestions and remove NullAtmosphere

AlexanderRex and others added 3 commits May 20, 2026 10:13
Co-authored-by: Bertrand Coconnier <bcoconni@users.noreply.github.com>
Co-authored-by: Bertrand Coconnier <bcoconni@users.noreply.github.com>
Co-authored-by: Bertrand Coconnier <bcoconni@users.noreply.github.com>
@seanmcleod70

Copy link
Copy Markdown
Contributor

We can discuss a vacuum model separately if there's interest :) (I thought to bring moon demo with Apollo model)

If you mean just the lunar module descending to and taking off from the Moon then as @bcoconni mentioned you don't necessarily require a vacuum atmosphere model. You can set the planet to the Moon and leave the default earth atmosphere model and simply have no force/moment entries in the <aerodynamics/> section, so JSBSim would just be modelling Moon's gravity plus the lunar module's thrust.

If you're talking about modelling the full Apollo mission from earth to the moon and back, then the issue is that there isn't a mechanism to swap/fade out/fade in one planet to another and one atmosphere to another.

The closest I've seen I guess is the JSBSim/FlightGear Space Shuttle model, which needs aerodynamic forces/moments for take-off and landing, but needs the aerodynamic forces/moments to pretty much go to 0 while in orbit. Actually not sure if they use the MSIS atmosphere model, given it's defined from 0-1000km. Space Shuttle's orbit was between 320km - 620km.

Or whether they use the standard ISA, and even though it's not defined above 87km, I'm guessing the density drops close enough to 0 that the Space Shuttle ends up with close to 0 aerodynamic forces/moments?

Per maintainer feedback (JSBSim-Team#1462 review by @bcoconni, @seanmcleod70):
- Empty <aerodynamics/> section already gives zero aero forces on
  airless bodies, removing the practical need for a vacuum model.
- Setting Pressure and Density to zero risks division-by-zero in
  callers that read these (e.g. FGAtmosphere::Calculate computes
  Viscosity / Density, FGAuxiliary divides by pressure).

This commit drops FGNullAtmosphere from the PR scope. Keeping only
the Mars atmosphere restoration in LoadPlanet.

Also fixes a typo in the previously-applied suggestion ("miss"
should have been "msis") that prevented the MSIS atmosphere from
being constructed at runtime, and adds the missing string_utilities.h
include for to_lower().

- Delete src/models/atmosphere/FGNullAtmosphere.{cpp,h}
- Remove FGNullAtmosphere from src/models/atmosphere/CMakeLists.txt
- Remove FGNullAtmosphere from JSBSimForUnreal.vcxproj
- Remove the "null" branch from LoadPlanet factory
- Restore tests/moon.xml to upstream version
- Remove test_load_Null_atmosphere from TestPlanet

@bcoconni bcoconni left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

@seanmcleod70
seanmcleod70 merged commit 9de5fe4 into JSBSim-Team:master May 25, 2026
32 checks passed
@AlexanderRex

Copy link
Copy Markdown
Contributor Author

We can discuss a vacuum model separately if there's interest :) (I thought to bring moon demo with Apollo model)

If you mean just the lunar module descending to and taking off from the Moon then as @bcoconni mentioned you don't necessarily require a vacuum atmosphere model. You can set the planet to the Moon and leave the default earth atmosphere model and simply have no force/moment entries in the <aerodynamics/> section, so JSBSim would just be modelling Moon's gravity plus the lunar module's thrust.

If you're talking about modelling the full Apollo mission from earth to the moon and back, then the issue is that there isn't a mechanism to swap/fade out/fade in one planet to another and one atmosphere to another.

The closest I've seen I guess is the JSBSim/FlightGear Space Shuttle model, which needs aerodynamic forces/moments for take-off and landing, but needs the aerodynamic forces/moments to pretty much go to 0 while in orbit. Actually not sure if they use the MSIS atmosphere model, given it's defined from 0-1000km. Space Shuttle's orbit was between 320km - 620km.

Or whether they use the standard ISA, and even though it's not defined above 87km, I'm guessing the density drops close enough to 0 that the Space Shuttle ends up with close to 0 aerodynamic forces/moments?

To be honest I thought to split it into the stages like:

  1. Land on the Lunar surface with Appolo first using custom controller or RL policy
  2. Simulate process starting from Earth

But at the moment Mars atmosphere is more necessary for me, so thats why I start fixing it :)
And thank you so much for merging my PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants