import%20marimo%0A%0A__generated_with%20%3D%20%220.16.0%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20rf%22%22%22%0A%20%20%20%20%23%20Inverted%20Trim%0A%0A%20%20%20%20%5BAgostino%20De%20Marco%5D(https%3A%2F%2Fwww.facebook.com%2Fagostino.demarco)%20posted%20a%20%5Bvideo%5D(https%3A%2F%2Fwww.facebook.com%2Fshare%2Fp%2F1Bfpg5QsSM%2F)%20of%20a%20Red%20Bull%20sponsored%20aerobatic%20aircraft%20flying%20inverted%20above%20a%20Red%20Bull%20Formula%201%20car%20and%20asked%20for%20a%20guess%20of%20the%20angle%20of%20attack%20(AoA)%20of%20the%20aircraft.%0A%0A%20%20%20%20%7Bmo.image(%22public%2FInvertedTrim%2FRedBullInverted.png%22%2C%20width%3D800)%7D%0A%0A%20%20%20%20In%20particular%20the%20sign%20convention%20for%20AoA%20when%20the%20aircraft%20is%20inverted.%0A%0A%20%20%20%20I%20decided%20to%20test%20out%20%5BJSBSim%5D(https%3A%2F%2Fgithub.com%2FJSBSim-Team%2Fjsbsim)%20to%20confirm%20that%20all%20the%20sign%20conventions%20worked%20out%20in%20terms%20of%20being%20able%20to%20fly%20the%20aircraft%20inverted%20in%20trim%20etc.%20and%20to%20also%20confirm%20whether%20the%20trim%20routines%20could%20calculate%20a%20trim%20solution%20for%20an%20inverted%20aircraft.%0A%0A%20%20%20%20Although%20the%20Red%20Bull%20sponsored%20aerobatic%20aircraft%20more%20than%20likely%20has%20a%20symmetrical%2C%20i.e.%20uncambered%20airfoil%20I%20decided%20to%20make%20things%20more%20interesting%20in%20terms%20of%20using%20a%20cambered%20airfoil.%20So%20I%20chose%20the%20A4%20Skyhawk%20model%20that%20is%20included%20with%20JSBSim.%0A%0A%20%20%20%20%7Bmo.image(%22public%2FInvertedTrim%2FA4.jpg%22%2C%20width%3D800)%7D%0A%0A%20%20%20%20Which%20has%20the%20following%20%24C_L%24%20vs%20AoA%20data.%0A%0A%20%20%20%20%7Bmo.image(%22public%2FInvertedTrim%2FA4ClvsAoA.png%22%2C%20width%3D800)%7D%0A%0A%20%20%20%20I%20then%20wrote%20the%20following%20Python%20code%20to%20calculate%20a%20trim%20solution%20for%20a%20range%20airspeeds%20for%20both%20the%20upright%20case%20and%20the%20inverted%20case.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(jsbsim%2C%20math%2C%20mo%2C%20plt)%3A%0A%20%20%20%20def%20trim_inverted_upright()%3A%0A%0A%20%20%20%20%20%20%20%20%23%20Avoid%20flooding%20the%20console%20with%20log%20messages%0A%20%20%20%20%20%20%20%20jsbsim.FGJSBBase().debug_lvl%20%3D%200%0A%0A%20%20%20%20%20%20%20%20fdm%20%3D%20jsbsim.FGFDMExec(None)%0A%0A%20%20%20%20%20%20%20%20%23%20Load%20the%20aircraft%20%0A%20%20%20%20%20%20%20%20fdm.load_model('A4')%20%0A%0A%20%20%20%20%20%20%20%20%23%20Set%20the%20engine%20running%0A%20%20%20%20%20%20%20%20fdm%5B'propulsion%2Fengine%5B0%5D%2Fset-running'%5D%20%3D%201%0A%0A%20%20%20%20%20%20%20%20%23%20Set%20alpha%20range%20for%20trim%20solutions%0A%20%20%20%20%20%20%20%20fdm%5B'aero%2Falpha-max-rad'%5D%20%3D%20math.radians(12)%0A%20%20%20%20%20%20%20%20fdm%5B'aero%2Falpha-min-rad'%5D%20%3D%20math.radians(-12.0)%0A%0A%20%20%20%20%20%20%20%20fig%20%3D%20plt.figure(layout%3D'constrained'%2C%20figsize%3D(10%2C%205))%0A%0A%20%20%20%20%20%20%20%20%23%20Roll%20angle%2C%20label%20and%20results%20array%20of%20tuples%20(speed%2C%20aoa)%0A%20%20%20%20%20%20%20%20configs%20%3D%20%5B%20(0%2C%20'Upright'%2C%20%5B%5D)%2C%20(180%2C%20'Inverted'%2C%20%5B%5D)%20%5D%0A%0A%20%20%20%20%20%20%20%20for%20config%20in%20configs%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20speed%20in%20range(160%2C%20460%2C%2010)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fdm%5B'ic%2Fh-sl-ft'%5D%20%3D%20250%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fdm%5B'ic%2Fvc-kts'%5D%20%3D%20speed%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fdm%5B'ic%2Fgamma-deg'%5D%20%3D%200%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fdm%5B'ic%2Fphi-deg'%5D%20%3D%20config%5B0%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Initialize%20the%20aircraft%20with%20initial%20conditions%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fdm.run_ic()%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fdm.run()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Trim%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fdm%5B'simulation%2Fdo_simple_trim'%5D%20%3D%201%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Store%20trim%20result%20(speed%2C%20aoa)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20config%5B2%5D.append((fdm%5B'velocities%2Fvc-kts'%5D%2C%20fdm%5B'aero%2Falpha-deg'%5D))%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20except%20RuntimeError%20as%20e%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20trim%20cannot%20succeed.%20Just%20make%20sure%20that%20the%20raised%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20exception%20is%20due%20to%20the%20trim%20failure%20otherwise%20rethrow.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20e.args%5B0%5D%20!%3D%20'Trim%20Failed'%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20raise%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20speed%2C%20alpha%20%3D%20zip(*config%5B2%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20plt.plot(speed%2C%20alpha%2C%20label%3Dconfig%5B1%5D)%0A%0A%20%20%20%20%20%20%20%20%23%20Calculate%20abs%20diff%20between%20inverted%20and%20upright%20aoa%20for%20trim%20speed%0A%20%20%20%20%20%20%20%20abs_diffs%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20for%20i%2C%20speed_alpha%20in%20enumerate(configs%5B0%5D%5B2%5D)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_%2C%20alpha%20%3D%20configs%5B1%5D%5B2%5D%5Bi%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20diff%20%3D%20abs(alpha)%20-%20speed_alpha%5B1%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20abs_diffs.append(diff)%0A%0A%20%20%20%20%20%20%20%20plt.plot(speed%2C%20abs_diffs%2C%20label%3D'Abs%20Diff')%0A%0A%20%20%20%20%20%20%20%20plt.xlabel('IAS%20(kt)')%0A%20%20%20%20%20%20%20%20plt.ylabel('AoA%20(deg)')%0A%20%20%20%20%20%20%20%20plt.title('AoA%20vs%20IAS')%0A%20%20%20%20%20%20%20%20plt.legend()%0A%0A%20%20%20%20%20%20%20%20return%20mo.md(f%22%7Bmo.as_html(fig)%7D%22)%0A%20%20%20%20return%20(trim_inverted_upright%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20trim_inverted_upright)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20rf%22%22%22%0A%20%20%20%20Sure%20enough%20JSBSim%20didn%E2%80%99t%20have%20any%20issues%20calculating%20trim%20solutions%20for%20the%20inverted%20case.%0A%0A%20%20%20%20%7Btrim_inverted_upright()%7D%0A%0A%20%20%20%20The%20difference%20of%20~2.7%C2%B0%2C%20i.e.%20the%20slightly%20larger%20AoA%20required%20for%20the%20inverted%20case%20pretty%20much%20matches%20the%20negative%20AoA%20required%20to%20achieve%20the%20same%20absolute%20%24C_L%24%20achieved%20at%200%C2%B0%20AoA%2C%20i.e.%20where%20the%20%24C_L%24%20vs%20AoA%20line%20crosses%20the%20y-axis.%0A%0A%20%20%20%20This%20difference%20will%20vary%20depending%20on%20the%20airfoil%20in%20terms%20of%20the%20slope%20of%20the%20%24C_L%24%20vs%20AoA%20line%20and%20where%20it%20crosses%20the%20y-axis.%20In%20the%20case%20of%20a%20symmetrical%2C%20i.e.%20non-cambered%20airfoil%20the%20%24C_L%24%20vs%20AoA%20will%20pass%20through%20the%20origin%20so%20there%20won%E2%80%99t%20be%20a%20difference%20in%20the%20absolute%20AoA%20required%20for%20upright%20versus%20inverted%20flight.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20import%20jsbsim%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20math%0A%20%20%20%20return%20jsbsim%2C%20math%2C%20mo%2C%20plt%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
0e8610ac929d37e444badd07e2cd941712d076ac00c313fc6f1cd1d5de488f0b