Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing the mixture for JSBSim piston aircraft #1037

Closed
hbeni opened this issue Feb 11, 2024 · 59 comments
Closed

Fixing the mixture for JSBSim piston aircraft #1037

hbeni opened this issue Feb 11, 2024 · 59 comments
Labels

Comments

@hbeni
Copy link

hbeni commented Feb 11, 2024

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
Copy link
Member

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
Copy link
Member

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
Copy link
Member

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
Copy link
Member

@dany93 could you comment on this topic ?

@bcoconni
Copy link
Member

bcoconni commented Feb 11, 2024

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:

<channel name="Automatic Mixture Control">
<fcs_function name="systems/mixture-cmd-norm">
<function>
<quotient>
<table>
<independentVar lookup="row">atmosphere/P-psf</independentVar>
<tableData>
50 0.1
990 0.5
1300 0.675
2117 1.0
</tableData>
</table>
<value>1.0</value>
</quotient>
</function>
</fcs_function>
<pure_gain name="systems/mixture-pos-norm[0]">
<input> fcs/mixture-cmd-norm[0]</input>
<gain> systems/mixture-cmd-norm</gain>
<output>fcs/mixture-pos-norm[0]</output>
</pure_gain>
<pure_gain name="systems/mixture-pos-norm[1]">
<input> fcs/mixture-cmd-norm[1]</input>
<gain> systems/mixture-cmd-norm</gain>
<output>fcs/mixture-pos-norm[1]</output>
</pure_gain>
</channel>

<channel name="Clerget9B">
<fcs_function name="systems/mixture-cmd">
<function>
<product>
<table>
<independentVar lookup="row">atmosphere/P-psf</independentVar>
<!-- This keeps the engine at 1250 rpm 2013/04/15 -->
<tableData>
<!--
50 0.075
990 0.52
1078 0.62325
1215.7 0.72225
1364.6 0.824
1679 0.714
2048 0.846
2110 0.86525
2500 1.0
-->
<!--
50 0.075
990 0.52
1208 0.5655
1470 0.6425
1671 0.725
2043 0.846
2111 0.86525
-->
50 0.075
990 0.52
1207 0.61775 <!-- wont reach 1250, about 1210 -->
1469 0.843
1684 0.78275
2048 0.9065
2113 0.934
2500 1.0
</tableData>
</table>
</product>
</function>
</fcs_function>
<switch name="systems/mixture-pos-norm">
<default value="fcs/mixture-cmd-norm"/>
<test value="systems/mixture-cmd">fcs/automixture-enable == 1</test>
</switch>
<pure_gain name="systems/mixture-pos-gain-norm">
<input>systems/mixture-pos-norm</input>
<gain>fcs/mixture-gain</gain>
<output>fcs/mixture-pos-norm</output>
</pure_gain>
</channel>
</system>

<channel name="Mixture Control">
<switch name="mixture/position">
<default value= "atmosphere/delta" />
<test value="atmosphere/delta"> <!-- emergency full rich -->
fcs/mixture-cmd-norm GE 0.90
</test>
<test logic="AND" value="999999999999"> <!-- cut off -->
fcs/throttle-cmd-norm LE 0.1
fcs/mixture-cmd-norm LE 0.1
</test>
<test value="1.053"> <!-- lean -->
fcs/mixture-cmd-norm LE 0.1
</test>
<test value="1.0"> <!-- normal run -->
fcs/mixture-cmd-norm LT 0.90
fcs/mixture-cmd-norm GT 0.1
</test>
</switch>
<fcs_function name="mixture">
<function>
<product>
<quotient> <!-- invert mixture/position -->
<value> 1.0 </value>
<property> mixture/position </property>
</quotient>
<property> atmosphere/delta </property>
</product>
</function>
<output>fcs/mixture-pos-norm</output>
</fcs_function>
</channel>

<fcs_function name="fcs/mixture/pos-norm[0]">
<function>
<product>
<le>
<property>fcs/outer-engines-cutoff-cmd-norm</property>
<value>0.0</value>
</le>
<property>fcs/mixture/altitude-standard-pos</property>
<sum>
<value>1.0</value>
<product>
<property>fcs/mixture/manual-control-factor</property>
<difference>
<property>fcs/mixture-cmd-norm[0]</property>
<value>0.5</value>
</difference>
</product>
</sum>
</product>
</function>
<clipto>
<min>0.0</min>
<max>1.0</max>
</clipto>
<output>fcs/mixture-pos-norm[0]</output>
</fcs_function>

<channel name="Mixture control">
<switch name="fcs/etc/mixture-pos-norm[0]">
<default value="fcs/mixture-cmd-norm[0]"/>
<output>fcs/mixture-pos-norm[0]</output>
</switch>
<switch name="fcs/etc/mixture-pos-norm[1]">
<default value="fcs/mixture-cmd-norm[1]"/>
<output>fcs/mixture-pos-norm[1]</output>
</switch>
<switch name="fcs/etc/mixture-pos-norm[2]">
<default value="fcs/mixture-cmd-norm[2]"/>
<output>fcs/mixture-pos-norm[2]</output>
</switch>
</channel>

<system name="Mixture control">
<!--
E = 1.3 * Mixture * P_std / P_amb
Mixture = P_amb / P_std
-->
<channel name="Automatic Mixture Control">
<fcs_function name="systems/mixture-pos-norm">
<function>
<table>
<independentVar lookup="row">atmosphere/P-psf</independentVar>
<tableData>
0 0.0
2117 1.0
</tableData>
</table>
</function>
<output>fcs/mixture-cmd-norm</output>
</fcs_function>
</channel>
</system>

@hbeni
Copy link
Author

hbeni commented Feb 11, 2024

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
Copy link

dany93 commented Feb 11, 2024

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
Copy link
Author

hbeni commented Feb 11, 2024

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
Copy link
Author

hbeni commented Feb 11, 2024

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
Copy link
Member

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
Copy link
Author

hbeni commented Feb 12, 2024

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
Copy link
Author

hbeni commented Feb 12, 2024

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
Copy link
Author

hbeni commented Feb 12, 2024

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:

  • its a bug, affecting all piston aicraft
  • thus it should be fixed in jsbsim rather each individual Aircraft

@hbeni hbeni reopened this Feb 12, 2024
@davidmegginson
Copy link

davidmegginson commented Feb 16, 2024

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
Copy link

dany93 commented Feb 16, 2024

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
Copy link

dershow commented Feb 16, 2024 via email

@hbeni
Copy link
Author

hbeni commented Feb 16, 2024

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
Copy link

wlbragg commented Feb 16, 2024

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
Copy link
Author

hbeni commented Feb 16, 2024

Also fuel vapor forming could be simulated...

Alot of things spring to mind.

@davidmegginson
Copy link

davidmegginson commented Feb 16, 2024

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
Copy link
Contributor

gallonmate commented Feb 17, 2024

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

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 - #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.

// First column is Fuel/Air Ratio, second is neta (mixture efficiency)
if( Mixture_Efficiency_Correlation == 0) {
Mixture_Efficiency_Correlation = new FGTable(15);
*Mixture_Efficiency_Correlation << 0.05000 << 0.00000;
*Mixture_Efficiency_Correlation << 0.05137 << 0.00862;
*Mixture_Efficiency_Correlation << 0.05179 << 0.21552;
*Mixture_Efficiency_Correlation << 0.05430 << 0.48276;
*Mixture_Efficiency_Correlation << 0.05842 << 0.70690;
*Mixture_Efficiency_Correlation << 0.06312 << 0.83621;
*Mixture_Efficiency_Correlation << 0.06942 << 0.93103;
*Mixture_Efficiency_Correlation << 0.07786 << 1.00000;
*Mixture_Efficiency_Correlation << 0.08845 << 1.00000;
*Mixture_Efficiency_Correlation << 0.09270 << 0.98276;
*Mixture_Efficiency_Correlation << 0.10120 << 0.93103;
*Mixture_Efficiency_Correlation << 0.11455 << 0.72414;
*Mixture_Efficiency_Correlation << 0.12158 << 0.45690;
*Mixture_Efficiency_Correlation << 0.12435 << 0.23276;
*Mixture_Efficiency_Correlation << 0.12500 << 0.00000;
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
Copy link

dany93 commented Feb 17, 2024

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

@davidmegginson
Copy link

davidmegginson commented Feb 17, 2024

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.

@bcoconni
Copy link
Member

bcoconni commented Mar 3, 2024

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
Copy link

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
Copy link
Contributor

gallonmate commented Mar 6, 2024

@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
Copy link
Author

hbeni commented Mar 6, 2024

@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: c172p-team/c172p#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
Copy link
Contributor

gallonmate commented Mar 6, 2024

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
Copy link
Member

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
Copy link

davidmegginson commented Mar 7, 2024

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
Copy link
Member

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
Copy link
Contributor

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
Copy link

davidmegginson commented Mar 8, 2024

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
Copy link
Author

hbeni commented Mar 8, 2024

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
Copy link
Member

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
Copy link

dany93 commented Mar 8, 2024

@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 the power-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
Copy link
Member

seanmcleod commented Mar 8, 2024

@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
Copy link

dany93 commented Mar 8, 2024

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
Copy link
Member

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

@dany93
Copy link

dany93 commented Mar 8, 2024

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
Copy link
Member

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.

@dany93
Copy link

dany93 commented Mar 8, 2024

Sorry, I don't have the time to try this.

@seanmcleod
Copy link
Member

Came across the following diagram online, not sure of the provenance of it, but basically looks like the Lycoming O-360, but with actual FAR values for the x-axis. I've annotated it in red to add AFR values.

image

Matches up fairly well with the Wikipedia diagram of power versus AFR. Max CHT occurring around ~14.7 AFR.

image

Lastly another potentially useful graph for the IO-360.

image

@seanmcleod
Copy link
Member

@bcoconni found one difference between the JSBSim repo and the repo used for the C172p for FlightGear.

https://github.com/c172p-team/c172p/blob/177b702391daa80249a463dbdf27c970ce858d74/Engines/eng_io320.xml#L20

If bsfc is not supplied in the engine file then a value for IFSC is calculated in the engine code, which makes use of MaxHp:

if (ISFC < 0) {
double pmep = 29.92 - MaxManifoldPressure_inHg;
pmep *= inhgtopa * volumetric_efficiency;
double fmep = (FMEPDynamic * RatedMeanPistonSpeed_fps * fttom + FMEPStatic);
double hp_loss = ((pmep + fmep) * displacement_SI * MaxRPM)/(Cycles*22371);
ISFC = ( 1.1*Displacement * MaxRPM * volumetric_efficiency *(MaxManifoldPressure_inHg / 29.92) ) / (9411 * (MaxHP+hp_loss-StaticFriction_HP));
// cout <<"FMEP: "<< fmep <<" PMEP: "<< pmep << " hp_loss: " <<hp_loss <<endl;
}

So commenting out bsfc in my engine file I now get the following:

Update (Event 3) executed at time: 30.008213
    velocities/u-aero-fps = 0.000002
    velocities/v-aero-fps = -0.000007
    velocities/w-aero-fps = -0.000001
    position/h-agl-ft = 4.342310
    propulsion/engine/thrust-lbs = 354.437263
    propulsion/engine/propeller-rpm = 2195.434759
    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 = 133.243817
    propulsion/engine/AFR = 11.305918
    propulsion/engine/egt-degF = 1427.738632
    propulsion/engine/engine-rpm = 2195.434759
    propulsion/engine/fuel-flow-rate-gph = 10.158791
    propulsion/engine/map-inhg = 28.751743

End: Saturday March 09 2024 14:59:47 (HH:MM:SS)

So a massive drop from 205.9hp to 133.2hp, a lot closer but not exactly the same figure that @dany93 reported.

133.2hp vs 136hp
2195rpm vs 2315rpm

@bomber64
Copy link

bomber64 commented Apr 4, 2024

Hi, I find this a very interesting conversation.

I worked at Rolls Royce Derby for a while as a Test Bed designer and I can assure everyone that a 'static engine test' is performed on EVERY engine. And is the only way to get accurate engine performance data.

I use a static engine test bed in FG and test all my piston engines this way.
And I assume we're all using a 'standard day' on which to test on.

So I take my engine to 7000ft and Full Throttle.

AFR = 9,9
Power-hp = 1108
mixture-cmd-norm = 0.9023

So I increase my AFR

AFR = 10
Power-hp = 1103
mixture-cmd-norm = 0.8931

So I decrease my AFR

AFR = 9.8
Power-hp = 1101
mixture-cmd-norm = 0.8115

So Max Power AFR is 9,9...

So at 9.9 AFR

fuel-flow-rate-gph =140.7
And goes down as AFR goes up, never does Fuel Flow got up like in the graph
And goes up as AFR goes down,

I'd simply like to be able to use an AFR of 9.9 at sea level.
It can't be too much to ask to be able to get the max power out of my engine.

Simon

@hbeni
Copy link
Author

hbeni commented Sep 9, 2024

Hi, jut wanted to know - what is the state of the dicussion?
As far as I could see, the initial problem is still not solved?

@gallonmate
Copy link
Contributor

The problem lies at the heart of the piston engine calculations, which currently no one has solved. #96. The discussion continues there. I studied the engine calculations for a few days and was verifying the individual formulas used in the code, some of which came from the book "The Internal-Combustion Engine in Theory and Practice. Vol. I_ Thermodynamics, Fluid Flow, Performance". But I was not able to find a clear fault. The piston engine model appears to be custom made and a work-in-progress that was still evolving when the engine author stopped updating it.

@davidmegginson
Copy link

davidmegginson commented Sep 9, 2024 via email

@seanmcleod
Copy link
Member

@davidmegginson it appears that Github doesn't handle embedded images in email comments so your illustration doesn't show up on Github.

However, I'm pretty sure it's covered by the graph I included in an earlier comment above, see - #1037 (comment)

@davidmegginson
Copy link

davidmegginson commented Sep 9, 2024

Yes, that's the same power curve for a different engine -- thanks! I posted early in the morning, and mistakenly thought I was in a flightgear-devel mailing list thread rather than GitHub. :)

tl;dr If the JSBSim piston-engine performance doesn't at least roughly follow the shape of those curves, then it's wrong, period. We're not talking about fine-tuning here — it's OK if it's off by a few degrees of CHT or EGT, or even a few horsepower, because no sim is perfect, and engines vary in real-life anyway because of wear-and-tear, carbon buildups, etc — but not following the general curves is like not having wings stall at some critical AoA. It's just blatantly unrealistic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants