JSBSim-Team / jsbsim

An open source flight dynamics & control software library
GNU Lesser General Public License v2.1
1.22k stars 420 forks source link

Fixing the mixture for JSBSim piston aircraft #1037

Closed hbeni closed 2 months ago

hbeni commented 2 months ago

Hi, David Megginson found (and fixed) a bug in the mixture control.

See: https://forum.flightgear.org/viewtopic.php?f=49&t=42055

Over on the flightgear-devel mailing list, I've been discussing a patch to fix the mixture input for JSBSim piston-engine aircraft.

Right now, the mixture control in src/FDM/JSBSim/models/propulsion/FGPiston.cpp is incorrectly clamped so that 1.0 (full rich) sets the mixture to peak power efficiency. In real life, the mixture control continues a ways rich of peak power to avoid predetonation at high power/low density altitude, which is why you have to lean a little for takeoff above 3,000 ft DA (or so). Anyone who's flown a simple trainer like a Cessna 172 or Piper PA-28 also knows how the RPM will initially increase as you start leaning, before it decreases again, which doesn't happen with our current JSBSim piston aircraft.


diff --git a/src/FDM/JSBSim/models/propulsion/FGPiston.cpp b/src/FDM/JSBSim/models/propulsion/FGPiston.cpp
index 1e73a503d..c1c75bd57 100644
--- a/src/FDM/JSBSim/models/propulsion/FGPiston.cpp
+++ b/src/FDM/JSBSim/models/propulsion/FGPiston.cpp
@@ -752,7 +752,8 @@ void FGPiston::doAirFlow(void)

 void FGPiston::doFuelFlow(void)
 {
-  double thi_sea_level = 1.3 * in.MixturePos[EngineNumber]; // Allows an AFR of infinity:1 to 11.3075:1
+  // use square root to increase sensitivity in the higher end
+  double thi_sea_level = 1.65 * sqrt(in.MixturePos[EngineNumber]); // Allows an AFR of infinity:1 to (no longer accurate) 11.3075:1
   equivalence_ratio = thi_sea_level * 101325.0 / p_amb;
   m_dot_fuel = (m_dot_air * equivalence_ratio) / 14.7;
   FuelFlowRate =  m_dot_fuel * 2.2046;  // kg to lb
bcoconni commented 2 months ago

Thanks @hbeni for submitting this issue.

I'm a bit curious about where this equation is coming from ? Can you document it ? At the moment, as far as I can tell (I'm no specialist in internal combustion engines) this diff is replacing some magic numbers by other magic numbers. Are the new values applicable to all piston engines under the sun ?

seanmcleod commented 2 months ago

So you're proposing to change thi_sea_level from f2 to f1 below? As @bcoconni asked, where is this equation coming from?

image

bcoconni commented 2 months ago

Yep, in addition I don't see the point in modifying the C++ code as the same result could be achieved using a <channel> in the aircraft FCS.

  <channel name="Mixture control">
    <fcs_function>
      <function>
        <product>
          <value>1.27</value> <!-- 1.65 / 1.3 -->
          <sqrt>
            <property>fcs/mixture-cmd-norm</property>
          </sqrt>
        </product>
      </function>
      <output>fcs/mixture-pos-norm</output>
    </fcs_function>
  </channel>
bcoconni commented 2 months ago

@dany93 could you comment on this topic ?

bcoconni commented 2 months ago

The same result could be achieved using a <channel> in the aircraft FCS.

I'm not inventing anything here as this is done in a number of our aircraft. See the 6 examples below: https://github.com/JSBSim-Team/jsbsim/blob/92daf8953c32a0a602ab0f48240a07c4a04aadb9/aircraft/c310/c310.xml#L521-L551 https://github.com/JSBSim-Team/jsbsim/blob/92daf8953c32a0a602ab0f48240a07c4a04aadb9/aircraft/Camel/Systems/automixture.xml#L7-L67 https://github.com/JSBSim-Team/jsbsim/blob/92daf8953c32a0a602ab0f48240a07c4a04aadb9/aircraft/p51d/Systems/mixture-control.xml#L27-L61 https://github.com/JSBSim-Team/jsbsim/blob/92daf8953c32a0a602ab0f48240a07c4a04aadb9/aircraft/Short_S23/Systems/engines.xml#L145-L170 https://github.com/JSBSim-Team/jsbsim/blob/92daf8953c32a0a602ab0f48240a07c4a04aadb9/aircraft/ZLT-NT/Systems/engine-control.xml#L263-L278 https://github.com/JSBSim-Team/jsbsim/blob/92daf8953c32a0a602ab0f48240a07c4a04aadb9/check_cases/piston_takeoff/aircraft/c172x/c172x.xml#L237-L257

hbeni commented 2 months ago

I did post this back to the discussion on the forums: https://forum.flightgear.org/viewtopic.php?p=419011#p419011 Please note, I'm just the messenger here; David Megginson is the original source for this issue. I think he just has no access to github or so.

dany93 commented 2 months ago

I have some experience on ultralights only, I fully trust David Megginson on the mixture subject.

However, I notice: @bcoconni wrote

Are the new values applicable to all piston engines under the sun ?

Yep, in addition I don't see the point in modifying the C++ code as the same result could be achieved using a \<channel> in the aircraft FCS.

I would rather agree with @bcoconni. Modifying the C++ code can have negative effects on current aircraft. Using the FCS and adding some explanations in the wiki might be sufficient and less risky. With a piece of code as an example. Moreover, it is more flexible.

hbeni commented 2 months ago

The issue with the current c++ code is, you cant go richer than the current 1.0 setting. Thus, you cant fix it using a channel ☝️

Davids patch fixes this.

hbeni commented 2 months ago

Maybe another alternative, if you dont want to change it directly in the code for all aircraft, add in a factor read optionally from another new property, so aircraft can implement this.

bcoconni commented 2 months ago

The issue with the current c++ code is, you cant go richer than the current 1.0 setting.

Not sure what you mean by that. But the properties fcs/mixture-cmd-norm and fcs/mixture-pos-norm can be set to any real value, including higher than 1 or even lower than 0. AFAICS there is no code in JSBSim that caps the mixture properties to the [0;1] range.

I've checked by modifying the script scripts/c1721.xml as below. After having run the script, the properties fcs/mixture-pos-norm and fcs/mixture-cmd-norm return whatever value that they have been set to (-15.3 in the example below but 10.0 or 1.5345 work just as well).

diff --git a/scripts/c1721.xml b/scripts/c1721.xml
index 461cb93e..a02eea93 100644
--- a/scripts/c1721.xml
+++ b/scripts/c1721.xml
@@ -8,10 +8,19 @@
     <event name="event name">
       <condition>simulation/sim-time-sec  ge  0.25</condition>
       <set name="fcs/aileron-cmd-norm" action="FG_STEP" value="0.25" tc="0.25"/>
+      <set name="fcs/mixture-cmd-norm" action="FG_STEP" value="-15.3" tc="0.25"/>
+      <notify>
+        <property>fcs/mixture-cmd-norm</property>
+        <property>fcs/mixture-pos-norm</property>
+      </notify>
     </event>
     <event name="event name">
       <condition>simulation/sim-time-sec  ge  0.5</condition>
       <set name="fcs/aileron-cmd-norm" action="FG_EXP" type="FG_DELTA" value="0.5" tc="0.5"/>
+      <notify>
+        <property>fcs/mixture-cmd-norm</property>
+        <property>fcs/mixture-pos-norm</property>
+      </notify>
     </event>
     <event name="event name">
       <condition>simulation/sim-time-sec  ge  1.5</condition>

Script output:

event name (Event 0) executed at time: 0.258323
    fcs/mixture-cmd-norm = -15.300000
    fcs/mixture-pos-norm = 0.000000

event name (Event 1) executed at time: 0.508313
    fcs/mixture-cmd-norm = -15.300000
    fcs/mixture-pos-norm = -15.300000
hbeni commented 2 months ago

Okay, understood. Thank you for elaborating - but more info on the wiki would be good. Just tested this on the 172p, works as described.

I think there just was a misconception about the clamping - the clamping in the 172p comes from the direct transmisison of the lever position (which is clamped to 1.0 for animation reasons).


In c172p.xml:

<fcs_function name="fcs/mixture-unit-cmd">
    <function>
        <product>
        <value>1.27</value> <!-- 1.65 / 1.3 -->
        <sqrt>
            <!--<property>fcs/mixture-cmd-norm</property>-->
            <property>/controls/engines/current-engine/mixture</property>
        </sqrt>
        </product>
    </function>
    <output>fcs/mixture-cmd-norm[0]</output>
    <output>fcs/mixture-cmd-norm[1]</output>
</fcs_function>

Does what the C++ patch would do. (It mus be done to mixture-cmd-norm, because the priming code currently already occupies the pos-norm property)

hbeni commented 2 months ago

From the Forums:

You should need to lean to get max power at sea level — that's what I think confused whoever wrote that JSBSim code, who just assumed, wrongly but understandably, that you'd want the most-efficient fuel-air mixture when the lever/knob is at its maximum setting.

Aircraft piston engines have long been set up so that full-rich mixture is inefficient — you're burning more fuel than you should for the power you're producing, or, to put it another way, you're producing less power than you should for the amount of air being sucked into the engine. It's most obvious with a fixed-pitch propeller at full throttle, because you can see it on the tachometer. As you start to lean, the RPM will initially increase, then it will start to fall again as you keep pulling the mixture back.

That must seem confusing to a physicist, but there's a sound engineering reason behind it. Above about 75% power, you need to operate the engine inefficiently (excessively rich fuel/air mixture) to keep internal pressures from getting high enough to cause predetonation, where the fuel/air mixture ignites before the spark, throws off the engine timing, and can eventually tear the engine apart (or at least cause tens of thousands of dollars in premature wear).

So nearly all aircraft piston engines over the past century are designed in such a way that if the pilot simply pushes the throttle and mixture all the way in, they will get that inefficient mixture that keeps the pressure below limits when the engine is producing > 75% power for takeoff and initial climb (the exception would be the very few aircraft piston engines that have FADEC or some other kind of automated mixture control). I have hear that many Lycomings and Continentals also have an "enrichment" circuit that adds even more to that when the throttle is right at the stop, but I don't know about that.

There is no instrumentation for internal pressure, but CHT moves in lockstep with it, which is why many engine manuals use CHT as a limit (it's not the heat itself, though, but the pressure that's the main danger).

Once you're above 3,000 ft density altitude, a normally-aspirated piston engine can't produce a lot more than 75% power anyway, and peak CHT/pressure is a little lean of that point, which is why they recommend leaning for max RPM at that DA or higher.

D

hbeni commented 2 months ago

So, David said he will get on touch and comment here.

But he also already did elaborate extensively in the forums, so the tldr is:

davidmegginson commented 2 months ago

First, apologies that this is a complex topic. Leaning mattered a lot to me during my 19 years of flying, because it affected my fuel reserves for long IFR flights, but I know it's not something non-pilots pay much attention to.

The JSBSim code is designed right now so that (by default) full rich mixture (1.0 input from FlightGear) is clamped to approximately peak power (see diagram below). That would make sense to a physicist — why would anyone want to use more fuel to produce (relatively) less power? — but it's completely wrong from an engineering PoV.

The problem is that petroleum (avgas) piston engines risk predetonation if internal cylinder pressure gets too high — that means that the fuel/air mixture ignites before the spark fires, throwing off the timing. In the best case scenario, that means tens of thousands of dollars in premature engine overhauls; in the worst case, catastrophic in-flight engine damage.

As a result, all petroleum/avgas piston engines (at least since the 1930s) are designed so that they fly somewhat inefficiently when the mixture is advanced to full rich. That means that when someone is using full takeoff power (typically above 75%), the internal cylinder pressure won't get as high (the diagram shows CHT, which is a proxy for internal pressure, since its curve is identical). Once in cruise, at 75% power or below, the pilot can lean to a more-efficient fuel/air mixture.

For higher-elevation takeoffs, where a normally-aspirated engine can't make above 75% power anyway, the recommendation is to lean for takeoff precisely so that the pilot will have the maximum power available.

So in real life, you will always see an increase in power (using RPM as a proxy with a fixed-pitch prop) as you start to lean, before the power drops again. With JSBSim aircraft, you do not see that unless the aircraft designer has deliberately kludged around this bug.

I strongly recommend that we fix this in the FDM itself, rather than relying on each aircraft designer to kludge around it, because we'll end up with more-consistent realism for our piston aircraft. I also suggest making this change only in the FlightGear 4.0 candidate codestream (leave the current 3.0 production release alone), so that aircraft designers who have used kludges can undo them and rely on the correct behaviour from the FDM itself.

Here is the chart. I lived and breathed (and staked my life on it) for many years of flying my former PA-28-161. I'll be happy to answer any questions.

And please note that I'm not asserting that my patch is the best possible one; only that it produces the expected behaviour in FlightGear, while the current JSBSim FDM does not. Please feel free to substitute a better patch that produces the same behaviour. If the behaviour is correct, then putting the parking brake on (e.g.) the Cessna 172p, advancing the throttle to full, then gradually leaning should show a noticeably increase in RPM as you lean, before the RPM starts falling again.

David out.

Leaning diagram

dany93 commented 2 months ago

Many thanks, @davidmegginson for this superb explanation. I was totally unaware of this point prior to this discussion.

Now, I agree with you and @hbeni, the best solution is to fix it in the JSBSim code. Unfortunately, it will be an issue for (I hope a weak number of) current aircraft, but it is the best for the long term.

I have a question: David wrote

(...) the mixture is advanced to full rich. That means that when someone is using full takeoff power (typically above 75%), the internal cylinder pressure won't get as high

I believed that the self-detonation (which causes predetonation) was firstly due to the pressure increase by the piston (which gives a temperature increase for the gas mixture). Like in Diesel engines, where there are no sparking plugs. For me, the pressure increase always happen. Whatever the mixture is rich or lean.

Does your explanation means that the self-detonation (predetonation) will not happen when the mixture is inefficient, too rich here? Or is it due to the (relatively) lower cylinder temperature, resulting from the previous detonations with this mixture value?

dershow commented 2 months ago

I agree that leaning behavior is important in any piston and therefore to appropriately capture in JSBSim. (Ever have one start running rough until you burn off some carbon on the plugs by running lean? That would be harder to simulate).
I think that the outstanding question is whether it is something that should be captured at the airplane model level (xml) or the overall JSBSim level (c-code). You think that one seems like a kludge, while the other could break many existing models.

--Adam

On Feb 16, 2024, at 11:44 AM, David Megginson @.***> wrote:

First, apologies that this is a complex topic. Leaning mattered a lot to me during my 19 years of flying, because it affected my fuel reserves for long IFR flights, but I know it's not something non-pilots pay much attention to.

The JSBSim code is designed right now so that (by default) full rich mixture (1.0 input from FlightGear) is clamped to approximately peak power (see diagram below). That would make sense to a physicist — why would anyone want to use more fuel to produce (relatively) less power? — but it's completely wrong from an engineering PoV.

The problem is that petroleum (avgas) piston engines risk predetonation if internal cylinder pressure gets too high — that means that the fuel/air mixture ignites before the spark fires, throwing off the timing. In the best case scenario, that means tens of thousands of dollars in premature engine overhauls; in the worst case, catastrophic in-flight engine damage.

As a result, all petroleum/avgas piston engines (at least since the 1930s) are designed so that they fly somewhat inefficiently when the mixture is advanced to full rich. That means that when someone is using full takeoff power (typically above 75%), the internal cylinder pressure won't get as high (the diagram shows CHT, which is a proxy for internal pressure, since its curve is identical). Once in cruise, at 75% power or below, the pilot can lean to a more-efficient fuel/air mixture.

For higher-elevation takeoffs, where a normally-aspirated engine can't make above 75% power anyway, the recommendation is to lean for takeoff precisely so that the pilot will have the maximum power available.

So in real life, you will always see an increase in power (using RPM as a proxy with a fixed-pitch prop) as you start to lean, before the power drops again. With JSBSim aircraft, you do not see that unless the aircraft designer has deliberately kludged around this bug.

I strongly recommend that we fix this in the FDM itself, rather than relying on each aircraft designer to kludge around it, because we'll end up with more-consistent realism for our piston aircraft. I also suggest making this change only in the FlightGear 4.0 candidate codestream (leave the current 3.0 production release alone), so that aircraft designers who have used kludges can undo them and rely on the correct behaviour from the FDM itself.

Here is the chart. I lived and breathed (and staked my life on it) for many years of flying my former PA-28-161. I'll be happy to ask any questions.

And please note that I'm not asserting that my patch is the best possibly one; only that it produces the expected behaviour in FlightGear, while the current JSBSim FDM does not. Please feel free to substitute a better patch that produces the same behaviour. If the behaviour is correct, then putting the parking brake on (e.g.) the Cessna 172p, advancing the throttle to full, then gradually leaning should show a noticeably increase in RPM as you lean, before the RPM starts falling again.

David out.

Leaning.diagram.png (view on web) https://github.com/JSBSim-Team/jsbsim/assets/2810060/77c5d714-32b4-4e43-a7a5-9ea51cfd9f73 — Reply to this email directly, view it on GitHub https://github.com/JSBSim-Team/jsbsim/issues/1037#issuecomment-1948858148, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABQ44XHSTGIES3EINBKNMBTYT6EFFAVCNFSM6AAAAABDDRWNE6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNBYHA2TQMJUHA. You are receiving this because you are subscribed to this thread.

hbeni commented 2 months ago

I think that the outstanding question is whether it is something that should be captured at the airplane model level (xml) or the overall JSBSim level (c-code).

I think generic fundamentals like leaning behaviour should be simulated at the generic piston simulation level (jsbsim). That simulation also should provide great flexibility so advanced stuff like spark plug fouling (or using other plugs, or other oil types, etc) can be implemented at the aircraft level.

wlbragg commented 2 months ago

This is similar to the effect of running out of gas, you know, when the engine starts to perform even better and revs up right before it dies. Is this simulated in the JSB code? Also choking the engine can buy you fractions of a second more as it drains the system. Interesting subject!

hbeni commented 2 months ago

Also fuel vapor forming could be simulated...

Alot of things spring to mind.

davidmegginson commented 2 months ago

I understand this from an operational PoV rather than a theoretical one, so I'll share what I know.

Generally speaking, we use EGT as the reference, because exhaust-gas-temperature gauges were formerly cheaper and easier to install (CHT requires probes right in the cylinders), though they're also less accurate. Peak EGT is where the EGT curve is highest on the chart I shared earlier; use the temperature scale on the top right to figure out how many degF lean of peak (LOP) or rich of peak (ROP) EGT you are elsewhere on the curves.

As you'll see in the chart, the CHT (and internal pressure) curve peaks at about 50 ROP, while the power curve peaks at about 100 ROP. Those two are very close in terms of movement in the mixture knob or lever, and the odds are that not all cylinders are in exactly the same place on the curve, so the general cruise-leaning recommendation for pilots not using a modern, all-cylinder engine monitor is either to lean to peak power and then enrich slightly (which is easier with a fixed-pitch prop), so that you land around 150 ROP or richer, or to lean right past peak EGT to the LOP side; the challenge with the latter is that the power curve falls off much more steeply on the lean side than it does on the rich side, so if the fuel/air distribution to the cylinders is too uneven, the engine will run very rough LOP (I was able to do it in my former Piper PA-28-161 with a four-cylinder Lycoming O-320, but most pilots with carbureted six-cylinder engines are not).

So back to the question of why you don't get predetonation with a full-rich mixture and full throttle at sea level. The pressure still increases, of course, as the cylinder compresses the mixture, but — and I'm on shaky ground here in the theoretical stuff — with more fuel and less air, the mixture doesn't ignite as easily, perhaps because the fuel is less compressible, or perhaps because of the lower temperature, as someone else mentioned (??? help me here — I'm flailing when I get into the theory). So you don't get the pre-ignition before the plug fires. When the engine is producing less power (below 75%), the compression cycle is happening more slowly, and perhaps that makes the difference. But again, I'm a pilot, not a theoretician.

If you want to dive deeply into this stuff, Mike Busch has written dozens of videos and articles and several books around the topic, so you can do a quick search for his name together with "leaning". I used to own his hefty, 508-page book on piston engines, but I donated it to my local flying club when Transport Canada took away my medical and I had to sell my plane.

gallonmate commented 2 months ago

There is an open Issue from 2018 that is related, if not the same. It's regarding the peak power, mixture, and exhaust temp. https://github.com/JSBSim-Team/jsbsim/issues/96 (also posted to https://github.com/c172p-team/c172p/issues/1128). In this other comment, Sean graphed the power output https://github.com/JSBSim-Team/jsbsim/issues/116#issuecomment-421064741:

I decided to compare the engine power output versus AFR.

image

I used the C172p aircraft model at sea-level with the engine running and brakes on and fcs\throttle-cmd-norm = 1 and then ramped the fcs/mixture-cmd-norm from 1.3 down to 0.7.

To compare against the Wikipedia graph:

image

Just wondering whether the power peak appearing so far left around an AFR of 10 is related to this issue - https://github.com/JSBSim-Team/jsbsim/issues/96 ?

/

So you can see there clearly a prior issue with the engine. Lets figure out how to solve it.

If I look at the Mixture_Efficiency_Correlation table here and then map it to a graph, it's appears to be fairly decent curve that's similar enough to what's expected of a real world engine. https://github.com/JSBSim-Team/jsbsim/blob/32d2c3b8831d932a4b2e83e6ae4d4082c3ffe1b8/src/models/propulsion/FGPiston.cpp#L333-L350 image

So the mixture table doesn't appear to be the culprit. Although it was added in 2008 with this commit: Added Ron Jensen's piston engine model changes. While at the same time a Power_Mixture_Correlation table was removed and some code altered in the DoFuelFlow.

David's fix seems to be in the correct general area of code. But as asked twice before, where did the original 1.3 * mixture math come from and where does the 1.65 * sqrt(mixture)solution come from?

If no one knows exactly, then it could take some time for members to investigate and re-solve where that math comes from in DoFuelFlow.

Edit: Also the horsepower is able to reach over 250 in flight when the engine file states maxhp of 160? Seems a little excessive which further suggests there is some miscalculation happening.

dany93 commented 2 months ago

Thank you @davidmegginson for your response. It is sufficient for me. Just curiosity, in case you would have known... :-)

davidmegginson commented 2 months ago

The 1.3 is the original factor for multiplying the mixture-control input (0.0 - 1.0) to map into the mixture table. I gave it 1.65 instead, because that seems to give about the right RPM drop rich of best power in the Cessna 172, but it's based on trial-and-error. So far, aircraft designers and pilots in the forum have reported that the behaviour seems reasonable.

The sqrt was just a response to pilots who find the mixture movement a bit unrealistic (cutoff too early). Since it's a feel thing, I have no objection to removing it, and letting it be handled in the joystick configuration. Obviously, sqrt(0.0) is still 0.0, and sqrt(1.0) is still 1.0, so you get the full range from lean-cutoff to full rich, but sqrt(0.5) for example is about 0.7, so it ensures that the you can make finer adjustments to the mixture in the functional range before you hit cutoff.

seanmcleod commented 2 months ago

Thanks @gallonmate for reminding me I'd generated a graph showing the power output versus AFR a couple of years ago.

So in that vein, here is a repeat of the Lycoming example that @davidmegginson posted earlier, repeated in this reply to make it easier to compare to the updated graphs I generated for the C172p in the JSBSim repo.

Leaning diagram

JSBSim C172p

image

Unfortunately not as well laid out as the Lycoming graphs, but it should give you a good idea of the default setup in JSBSim at the moment and how it compares to the Lycoming 0-360.

In terms of the TSFC don't read too much into the particular values, unit wise I've divided gallons per hour by thrust-lbs, but rather the general shape compared to the Lycoming.

hbeni commented 2 months ago

Is it just me, or doesn‘t the power courve looks „reversed“?

And should the „wontfix“ label be removed?

hbeni commented 2 months ago

So, how do we progress with this bugfix from david?

seanmcleod commented 2 months ago

Is it just me, or doesn‘t the power curve looks "reversed“?

I don't think so. Maybe what's confusing is the massive drop-off in power after a MixturePos > 1.3?

And looking at the graphs I generated would you not get the effect David is looking for by mapping MixtureCmd [0, 1] to MixturePos [0.7, 1.3]?

hbeni commented 2 months ago

I don't think so. Maybe what's confusing is the massive drop-off in power after a MixturePos > 1.3?

Ok, i see.

And looking at the graphs I generated would you not get the effect David is looking for by mapping…

I would, but the proposal to change the default range from David is based on the valid assumption that all piston engines currently have that wrong. Hence it should be fixed here in jsbsim and not in each individual Aircraft. This if course is backwards incompatible, but David proposed a way to deal with that; and its a bug, not a feature request. All Aircraft using this fdm have it wrong without manual intervention!

So in my opinion it would be good to merge this, and I make an heads up at the fgfs mailing list and newsletter. If you do decide that this wont happen, I can understand, but then please close this issue here, so we at least have an official closed case and I can start to file issues with the aircraft i know of. And also, please amnend the documentation with the knowledge here, so users reading the manual are knowing they need to something with their mixture coding to get a realistic mixture control.

seanmcleod commented 2 months ago

In my mind it's too early to jump in and change it. I'm trying to get a better understanding of how JSBSim's engine model works the range of mixture inputs and how that compares to the Lycoming example for example.

So currently what happens with a MixtureCmd in the range of say [0.1, 0,.7] and how realistic is that compared to the Lycoming example?

Plus, what do the graphs look like that I produced above when David's suggested formula of double thi_sea_level = 1.65 * sqrt(in.MixturePos[EngineNumber]); is used?

bcoconni commented 2 months ago

the proposal to change the default range from David is based on the valid assumption that all piston engines currently have that wrong.

I haven't seen any evidence of this statement. All we got is one single piston engine mapping with a vague mixture scale i.e. Too lean, Lean, Rich, Full rich. How could one tell 1.65 is the correct scale from that ? All we got was:

it's based on trial-and-error. So far, aircraft designers and pilots in the forum have reported that the behaviour seems reasonable.

hence pretending this trial-and-error applies to all piston engines is a bit of a stretch.

The sqrt was just a response to pilots who find the mixture movement a bit unrealistic (cutoff too early). Since it's a feel thing, I have no objection to removing it, and letting it be handled in the joystick configuration.

The latter statement seems more reasonable. One has to keep in mind that the whole point of using XML definition files is to let aircraft developers do the fine tuning and this is including that kind of things.

This of course is backwards incompatible, but David proposed a way to deal with that

Added to the fact that you can't properly substantiate the value you are submitting, backward incompatibility is a definitive showstopper. No matter what David suggested, JSBSim usage isn't limited to FlightGear and I don't want to deal with tickets here on JSBSim about "it used to work but it's no longer working" for the sole reason that you requested to change a number by another number that you couldn't properly substantiate.

All in all, this ticket is a perfect example of why magic numbers are evil. The initial author thought their number looked right, then someone comes to tell that a new number feels right, then someone else thinks that another number sounds right, etc. What will be the next ticket ? Replace 1.65 by 1.78 ? Then 1.78 by 1.57 ? And break backward compatibility several times in the process ? If we have no criteria other than the new number seems more right than the previous one, how could we make the decision ?

Magic numbers are evil and replacing a magic number by another magic number is even more evil.

please amnend the documentation with the knowledge here, so users reading the manual are knowing they need to something with their mixture coding to get a realistic mixture control.

The docs are indeed misleading in that they are telling that mixture should be in the [0.0 - 1.0] range and this needs to be clarified.

So granted that the current situation with the misleading statement that mixture should be in the [0.0 - 1.0] range does not give the global behavior that one should be expecting from real piston engines. However we'll have to live with this situation with an amended documentation because:

  1. The change would break backward compatibility.
  2. No one can give a new value built on solid foundations.

In addition, the proper way to address this issue is more likely to modify the scale of the table Mixture_Efficiency_Correlation so that the piston engine behaves correctly over the full mixture range [0.0 - 1.0]. I understand that is what @seanmcleod is currently investigating.

Hence it should be fixed here in jsbsim and not in each individual Aircraft.

In order to close this ticket with a suggestion that I hope will fit all parties, I'd suggest modifying the JSBSim glue code in FlightGear C++ code. More specifically the 2 following lines of code in src/FDM/JSBSim/JSBSim.cxx:

line 417

        globals->get_controls()->set_mixture(i, FCS->GetMixtureCmd(i));

line 639

      FCS->SetMixtureCmd(i, globals->get_controls()->get_mixture(i));

There you can convert the mixture value from JSBSim to any value that fits FlightGear. With that suggestion, you could change the conversion factor as often as you need and deal with backward incompatibility at your level.

then please close this issue here

Granted.

hbeni commented 2 months ago

Thank you for responding so elaborately :) I think that are good toughts and so we can move on.

davidmegginson commented 2 months ago

Yes, we can definitely work around the issue at the FlightGear level by applying a multiplier to the normalised mixture input, as long as it's limited to JSBsim and doesn't break other FDMs.

I noticed there was a pull request for the Cessna 172. I think that would be the wrong approach: we want to fix a bug only once, not repeat the fix dozens of times for each individual aircraft.

Thank you all for the discussion.

p.s. For future JSBsim development, I don't think the table is far off, so it might not be worth spending a lot of time investigating. It's not about the combustion physics; just that the mixture level/knob can't "travel" far enough into the table based on the multiplier currently applied to the normalized mixture-input value, as if it were hitting a stop before full rich.

bcoconni commented 2 months ago

please amnend the documentation with the knowledge here, so users reading the manual are knowing they need to something with their mixture coding to get a realistic mixture control.

The docs are indeed misleading in that they are telling that mixture should be in the [0.0 - 1.0] range and this needs to be clarified.

This has been fixed by the PR #1048 which has just been merged in the master branch.

davidmegginson commented 1 month ago

I think the docs were correct, since all of the other control inputs (AFAIK) are normalised to between 0.0 and 1.0 (or -1.0 and 1.0), but we do need to allow a exception to work around this specific issue.

gallonmate commented 1 month ago

@davidmegginson Flightgear and the c172 nasal engine file are clamping the mixture to 0.0 to 1.0. There is no JSBSim code that is clamping or normalizing the mixture controls to 1.0.

https://github.com/FlightGear/flightgear/blob/cf4801e11c5b69b107f87191584eefda3c5a9b26/src/Aircraft/controls.cxx#L822-L832 I realize this link is a three year old copy of flightgear and I have no idea if this code is current or relevant but I'm using it as an example, since sourceforge files are not able to be searched without downloading the files.

And here's one part of the c172 nasal script that is also clamping the mixture input. (I saw three locations total in the script) https://github.com/c172p-team/c172p/blob/45c7c29273ea072e53352d8b11b53514cd2a8d73/Nasal/engine.nas#L484-L487

The mixture being clamped/normalized is by Flightgear & c172p model. You can start up a standalone JSBSim and easily set the mixture to any amount you wish. That being said, as I mentioned in another comment - there is already an open Issue with JSBSim from 2018 regarding the engine model which shows the same issue you've mentioned. The engine issue is also noted and open in the c172p repository. I recently spent some time checking the engine model code and checking line by line the expected results and verifying each formula. I opened a copy of "The Internal-Combustion Engine in Theory and Practice. Vol. I_ Thermodynamics, Fluid Flow, Performance" by Charles Fayette Taylor. I didn't see any glaring issues but I'm still investigating, which is why I have not posted any comments or results yet. To be honest there are some questionable parts of the engine model code, such the indicated horsepower is equal to the fuel flow rate * mixture efficiency, but fuel flow is determined from air flow, which is determined from RPM, and RPM is determined from horsepower... Seems a bit of feedback loop between the horsepower and fuelflow, and might be the cause of the slightly overrich conditions as noted in the 2018 issue. And it's why you notice the engine already being too lean at full rich, because the curve is being cutoff from being overly rich to begin with. So until someone can prove a solution which fixes the AFR and thereby giving a normalized access to the full mixture curve, then any suggestions to fix only the mixture input value is just another workaround, which no one wants. Sean mentioned he would investigate the engine model and I'm still investigating it. I'm not sure what else you want. No one has disagreed that there is a engine model/mixture issue.

hbeni commented 1 month ago

@davidmegginson Flightgear and the c172 nasal engine file are clamping the mixture to 0.0 to 1.0. There is no JSBSim code that is clamping or normalizing the mixture controls to 1.0.

Its not flightgear itself clamping the value. Its basicly the input mappings. I made a PR which modifies the mapping courve for the mixture to mimic Davids proposed change, and for me it worked: https://github.com/c172p-team/c172p/pull/1492

If done in the plane, you can easily exceed 1.0 with the desired effects. The thing is, every basic single piston plane needs to do a similar change. David wanted to avoid that, the "default" should be a correct simulation for a 1.0 setting.

gallonmate commented 1 month ago

The thing is, every basic single piston plane needs to do a similar change. David wanted to avoid that

Everyone wants to avoid that. There's an Open engine Issue I linked 16 comments prior. I'm going to focus on solving that problem, which includes the mixture issue.

seanmcleod commented 1 month ago

So until someone can prove a solution which fixes the AFR and thereby giving a normalized access to the full mixture curve, then any suggestions to fix only the mixture input value is just another workaround, which no one wants.

I agree, let's figure out the root cause(s) and work on those as opposed to adding band-aids.

davidmegginson commented 1 month ago

There's really no need to look further for a root cause:

  1. By convention, all control axes (joysticks, sliders, etc) in FlightGear are mapped in a normalised range from 0.0 to 1.0 (usually minimum to maximum) or -1.0 to 1.0 (e.g. left-right, down-up). The documentation is correct in that regard.

  2. Like other FDMs, JSBSim seems mostly set up to work with those normalised ranges as inputs and then adjust them internally as needed. However, in the case of the mixture control for piston engines, someone long ago made a small-but-understandable mistake, applying a multiplier so that "maximum" mixture (1.0) would land in the fuel:air table at peak combustion efficiency, instead of a bit rich of that (as happens in a real plane).

  3. The best fix would be correcting the error by changing the multiplier in JSBSim so that normalised input ranges for mixture control still work the same way they do for other controls like throttle, ailerons, etc.

  4. The second-best fix would be applying a kludge in the FlightGear:JSBSim interface, effectively denormalising the value by extending it past 1.0 to offset the JSBSim bug.

  5. The worst fix would be requiring every single piston-aircraft designer to apply that same kludge separately in their config files.

There's lots else we could improve in the piston engine model, but this isn't a difficult bug, and I've been a bit puzzled that it became controversial; it took me only a few minutes to diagnose,even though I'm not very familiar with the JSBSim code. (thanks to a helpful comment in the source where the original coder documented their assumption — +1 for good commenting practice).

seanmcleod commented 1 month ago

Just a couple of initial/immediate issues/questions.

If you take a look at the graph I generated you see engine power hitting ~200hp for a mixture-pos of 1.0, and peaking around ~215hp for a mixture-pos of ~1.1, whereas the engine file as mentioned by @gallonmate mentions a max of 160hp.

How accurate and representative is the mixture efficiency correlation table?

The earlier issue and the graph, repeated above, with regards to the plot of power versus AFR and it's relative shift compared to the wikipedia example?

gallonmate commented 1 month ago

David,

Changing the multiplier to 1.65 * sqrt decreases power output at full rich mixture and then increases power output as you lean. And as you lean it actually gives a higher power output than the current multiplier allows. It has the effect of the engine developing an extra 15hp compared to the current max HP available. You can see this in the power/mixture curve chart Sean posted. And you're absolutely right it does affect every piston aircraft. Every aircraft maintainer will need to verify the leaning behavior and power outputs are correct for their aircraft. Do all piston aircraft use manual mixture controls where the full rich position at sea level is not actually max power? And if the new behavior and power outputs are not correct, then they will need to correct their aircraft. And then when/if the the other engine issues are fixed, do you want every aircraft maintainer to verify everything is correct, making adjustments a second time? I thought you would have understood this all by now.

Here's a summary of changing the multiplier- Pros: +corrects the leaning behavior seen with most manual mixture controlled aircraft

Potential Issues per aircraft: -changes the power curve which may or maynot be correct for each piston aircraft -decreases the initial full rich horsepower for piston aircraft -increases the total available horsepower, (previously not available using the standard 0.0-1.0 mixture range) -could require adjustments made to each piston aircraft to account for these changes. -when/if AFR and EGT engine issues are solved, it could require each aircraft adjustment to be adjusted again, as a result of changing the multiplier.

You have two people that volunteered to investigate the issues. But instead you just say: "There's really no need to look further for a root cause". That's great that you've solved the leaning behavior for manual mixture piston aircraft and that you're happy with the results. It's not great that you're continuing to ignore everything else about the effects of the multiplier solution and the related 2018 engine issues. I could be spending time working on the engine model, but instead I'm wasting my time typing here to explain results and data that was shared 20 comments back. I thought you would have seen the data and reached the same conclusions that the multiplier solution is indeed another workaround.

davidmegginson commented 1 month ago

Ah, that's interesting -- thanks. Based on the original coder's comment, I'd thought that it was just a multiplier for a reference into the lookup table.

And yes, I've never heard of an aircraft piston engine that clamped full-rich mixture to peak power, because such an engine would wear out quickly (or even eventually tear itself apart) at full/takeoff throttle power (peak power is just barely rich of peak CHT) — when you open up the throttle all the way near sea level it needs to well rich of peak power to inhibit predetonation. Rich of peak power is the common fail-safe for the "full rich" mixture setting, which is why at higher-elevations airports you need to carefully lean to peak power for takeoff (as you'll see in numerous articles from AOPA and other aviation sources) — if you tried to take off from Leadville CO without leaning with a normally-aspirated piston engine to peak power, for example, you'd probably never escape ground effect.

There could be some very early (1920s or 1910s) aircraft engines that set up the mixture different, and FlightGear aircraft designers could treat them as special cases, but overwhelmingly JSBSim is displaying the wrong default behaviour for nearly every piston engine, and we need to shift it.

hbeni commented 1 month ago

Isn‘t a good way forward to note Davids experience and conclusion into the other 2018 engine overhaul issue, and then fix them together in one merge, so aircraft devs can adapt to a consistent single change to the engine model?

seanmcleod commented 1 month ago

That's been our point all along, i.e. let's look at all these issues together to see how they interact and then decide on the best way forward as opposed to being pressurized into a quick fix of "just take David's fix and incorporate it.".

dany93 commented 1 month ago

@seanmcleod wrote

If you take a look at the graph I generated you see engine power hitting ~200hp for a mixture-pos of 1.0, and peaking around ~215hp for a mixture-pos of ~1.1, whereas the engine file as mentioned by @gallonmate mentions a max of 160hp.

I understand that this is the graph in this message.

I don't know how you got this 200 hp value for power, but I checked several times in the simulator and found that thepower-hp in fdm/jsbsim/propulsion/engine[0] or [1]was in accordance with in the xml engine file configuration (5 to 7 hp lower for 160 or 180 hp nominal). Close to the <maxhp> at <maxrpm> values in the engine file configuration. From this point of view, JSBSim works well. Watched at full throttle, mixture = 1, close to sea level, by climbing such as the engine was at <maxrpm> = 2700 RPM. Pressure inhg : default value (close to 29 for 29.97 at sea level)

seanmcleod commented 1 month ago

@dany93 here is my procedure and script that I used to generate the graph.

So in broad strokes I used a script which placed the C172p on the ground, trimmed for ground, set the brakes on, started the engine with throttle-cmd-norm = 1.0 and set mixture-cmd-norm to the value I wanted to test for.

Then output a number of engine parameters after 10s and after 30s, in case there was some lag, and used the 30s figures as input for the graph.

Here is a copy of my script:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="http://jsbsim.sourceforge.net/JSBSimScript.xsl"?>
<runscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://jsbsim.sf.net/JSBSimScript.xsd"
    name="C172p mixture test">
    <description>This run is for testing the C172 mixture</description>
    <use aircraft="c172p" initialize="groundreset"/>

    <run start="0.0" end="500" dt="0.0083333">

        <event name="Start engine">
            <condition>
                simulation/sim-time-sec  ge  0.01
            </condition>
            <set name="fcs/throttle-cmd-norm" value="1.0" />
            <set name="fcs/mixture-cmd-norm" value="1.0" />
            <set name="propulsion/magneto_cmd" value="3"/>
            <set name="propulsion/starter_cmd" value="1"/>
            <set name="fcs/left-brake-cmd-norm" value="1.0" />
            <set name="fcs/right-brake-cmd-norm" value="1.0" />

            <notify/>
        </event>

        <event name="Trim">
            <condition>
                simulation/sim-time-sec  gt  1.0
            </condition>
            <set name="simulation/do_simple_trim" value="2"/>
            <notify>
                <property> velocities/u-aero-fps </property>
                <property> velocities/v-aero-fps </property>
                <property> velocities/w-aero-fps </property>
                <property> position/h-agl-ft </property>
                <property> propulsion/engine/thrust-lbs </property>
                <property> propulsion/engine/propeller-rpm </property>
                <property> fcs/throttle-pos-norm[0] </property>
                <property> fcs/throttle-cmd-norm[0] </property>

                <property> velocities/vc-kts </property>

                <property> fcs/mixture-pos-norm[0] </property>
                <property> propulsion/engine/power-hp </property>
                <property> propulsion/engine/AFR </property>
                <property> propulsion/engine/egt-degF </property>
                <property> propulsion/engine/engine-rpm </property>
                <property> propulsion/engine/fuel-flow-rate-gph </property>
            </notify>
        </event>

        <event name="Update">
            <condition>
                simulation/sim-time-sec  gt  10.0
            </condition>
            <notify>
                <property> velocities/u-aero-fps </property>
                <property> velocities/v-aero-fps </property>
                <property> velocities/w-aero-fps </property>
                <property> position/h-agl-ft </property>
                <property> propulsion/engine/thrust-lbs </property>
                <property> propulsion/engine/propeller-rpm </property>
                <property> fcs/throttle-pos-norm[0] </property>
                <property> fcs/throttle-cmd-norm[0] </property>

                <property> velocities/vc-kts </property>

                <property> fcs/mixture-pos-norm[0] </property>
                <property> propulsion/engine/power-hp </property>
                <property> propulsion/engine/AFR </property>
                <property> propulsion/engine/egt-degF </property>
                <property> propulsion/engine/engine-rpm </property>
                <property> propulsion/engine/fuel-flow-rate-gph </property>
            </notify>
        </event>

        <event name="Update">
            <condition>
                simulation/sim-time-sec  gt  30.0
            </condition>
            <notify>
                <property> velocities/u-aero-fps </property>
                <property> velocities/v-aero-fps </property>
                <property> velocities/w-aero-fps </property>
                <property> position/h-agl-ft </property>
                <property> propulsion/engine/thrust-lbs </property>
                <property> propulsion/engine/propeller-rpm </property>
                <property> fcs/throttle-pos-norm[0] </property>
                <property> fcs/throttle-cmd-norm[0] </property>

                <property> velocities/vc-kts </property>

                <property> fcs/mixture-pos-norm[0] </property>
                <property> propulsion/engine/power-hp </property>
                <property> propulsion/engine/AFR </property>
                <property> propulsion/engine/egt-degF </property>
                <property> propulsion/engine/engine-rpm </property>
                <property> propulsion/engine/fuel-flow-rate-gph </property>

            </notify>
        </event>

    </run>
</runscript>

And here is the output for mixture-cmd-norm = 1.0 case:

Update (Event 3) executed at time: 30.008213
    velocities/u-aero-fps = 0.000002
    velocities/v-aero-fps = -0.000010
    velocities/w-aero-fps = -0.000002
    position/h-agl-ft = 4.336028
    propulsion/engine/thrust-lbs = 473.735175
    propulsion/engine/propeller-rpm = 2538.157833
    fcs/throttle-pos-norm[0] = 1.000000
    fcs/throttle-cmd-norm[0] = 1.000000
    velocities/vc-kts = 0.000000
    fcs/mixture-pos-norm[0] = 1.000000
    propulsion/engine/power-hp = 205.892919
    propulsion/engine/AFR = 11.305920
    propulsion/engine/egt-degF = 1427.738788
    propulsion/engine/engine-rpm = 2538.157833
    propulsion/engine/fuel-flow-rate-gph = 11.666088

End: Friday March 08 2024 11:08:25 (HH:MM:SS)
(base) C:\source\jsbsim>

So for this case I see propulsion/engine/power-hp = 205.892919.

I've just double-checked the engine being used:

    <propulsion>
        <engine file="eng_io320">

And from eng_io320.xml:

<piston_engine name="IO320">
  <minmp unit="INHG">        10.0  </minmp>
  <maxmp unit="INHG">        28.5  </maxmp> 
  <displacement unit="IN3"> 320.0  </displacement>
  <maxhp>                   160.0  </maxhp> 
  <bsfc>                      0.32 </bsfc>
  <cycles>                    4.0  </cycles>
  <idlerpm>                 550.0  </idlerpm>
  <maxrpm>                 2700.0  </maxrpm>
  <maxthrottle>               1.0  </maxthrottle>
  <minthrottle>               0.1  </minthrottle>
  <sparkfaildrop>             0.1 </sparkfaildrop>
</piston_engine>

Just to be clear, I'm using just the JSBSim repo, not any C172p version that may be bundled with FlightGear. I haven't checked to see if there are any differences between the C172p in the JSBSim repo compared to FlightGear.

dany93 commented 1 month ago

With the c172p from the github rep, I find for the 160 hp engine (on the ground with brakes on; throttle = 1, mixture = 1) Internal properties displayed in the simulator (no script). power-hp = 136 engine-rpm 2315

seanmcleod commented 1 month ago

Hmm, that's a massive difference. And if you run the script above what result do you get?

dany93 commented 1 month ago

I don't think it comes from your script. From the c172p version that you use? Did you try with our github repo version? My test is very basic, I don't see why it would give an error. If you merely watch the Internal properties, I guess they are in accordance with your values from the script?

Out of my memory, I checked the power hp values vs the config file ones on several JSBSim aircraft.

Anyway, testing on the ground is not the correct way to check the max power hp at max rpm. But this is another subject.

seanmcleod commented 1 month ago

I don't have FlightGear installed. It would be useful to know what output you get from the script on your system if you use the JSBSim version bundled with your FlightGear installation, and how those compare to the properties reported via FlightGear compared to the script outputs.