def test_pitot_angle(self): script_name = 'ball_chute.xml' script_path = self.sandbox.path_to_jsbsim_file('scripts', script_name) # Add a Pitot angle to the Cessna 172 tree, aircraft_name, path_to_jsbsim_aircrafts = CopyAircraftDef( script_path, self.sandbox) root = tree.getroot() pitot_angle_deg = 5.0 self.addPitotTube(root, 5.0) contact_tag = root.find('./ground_reactions/contact') contact_tag.attrib['type'] = 'STRUCTURE' tree.write( self.sandbox('aircraft', aircraft_name, aircraft_name + '.xml')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_model('ball') pitot_angle = pitot_angle_deg * math.pi / 180. weight = fdm['inertia/weight-lbs'] spring_tag = contact_tag.find('./spring_coeff') spring_coeff = float(spring_tag.text) print("Weight=%d Spring=%d" % (weight, spring_coeff)) fdm['ic/h-sl-ft'] = weight / spring_coeff fdm['forces/hold-down'] = 1.0 fdm.run_ic() ExecuteUntil(fdm, 10.) for i in range(36): for j in range(-9, 10): angle = math.pi * i / 18.0 angle2 = math.pi * j / 18.0 ca2 = math.cos(angle2) fdm['atmosphere/wind-north-fps'] = 10. * math.cos(angle) * ca2 fdm['atmosphere/wind-east-fps'] = 10. * math.sin(angle) * ca2 fdm['atmosphere/wind-down-fps'] = 10. * math.sin(angle2) fdm.run() vg = fdm['velocities/vg-fps'] self.assertAlmostEqual(vg, 0.0, delta=1E-7) vt = fdm['velocities/vt-fps'] self.assertAlmostEqual(vt, 10., delta=1E-7) mach = vt / fdm['atmosphere/a-fps'] P = fdm['atmosphere/P-psf'] pt = P * math.pow(1 + 0.2 * mach * mach, 3.5) psl = fdm['atmosphere/P-sl-psf'] rhosl = fdm['atmosphere/rho-sl-slugs_ft3'] A = math.pow((pt - P) / psl + 1.0, 1.0 / 3.5) alpha = fdm['aero/alpha-rad'] beta = fdm['aero/beta-rad'] vc = math.sqrt( 7.0 * psl / rhosl * (A - 1.0)) * math.cos(alpha + pitot_angle) * math.cos(beta) self.assertAlmostEqual(fdm['velocities/vc-kts'], max(0.0, vc) / 1.68781, delta=1E-7)
def test_hold_down_with_gnd_reactions(self): fdm = CreateFDM(self.sandbox) fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'c1721.xml')) fdm.run_ic() ExecuteUntil(fdm, 0.25) fdm['forces/hold-down'] = 1.0 h0 = fdm['position/h-sl-ft'] pitch = fdm['attitude/pitch-rad'] roll = fdm['attitude/roll-rad'] heading = fdm['attitude/heading-true-rad'] while fdm['simulation/sim-time-sec'] < 2.0: fdm.run() self.assertAlmostEqual(fdm['accelerations/pdot-rad_sec2'], 0.0) self.assertAlmostEqual(fdm['accelerations/qdot-rad_sec2'], 0.0) self.assertAlmostEqual(fdm['accelerations/rdot-rad_sec2'], 0.0) self.assertAlmostEqual(fdm['accelerations/udot-ft_sec2'], 0.0) self.assertAlmostEqual(fdm['accelerations/vdot-ft_sec2'], 0.0) self.assertAlmostEqual(fdm['accelerations/wdot-ft_sec2'], 0.0) self.assertAlmostEqual(fdm['position/h-sl-ft'], h0, delta=1E-6) self.assertAlmostEqual(fdm['attitude/pitch-rad'], pitch) self.assertAlmostEqual(fdm['attitude/roll-rad'], roll) self.assertAlmostEqual(fdm['attitude/heading-true-rad'], heading)
def test_direct_steer(self): fdm = CreateFDM(self.sandbox) fdm.load_model('c172r') aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft') fdm.load_ic(os.path.join(aircraft_path, 'c172r', 'reset00'), False) fdm.run_ic() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 0.0) self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], 0.0) # Should be part of a unit test in C++ ? fpectl.turnon_sigfpe() grndreact = fdm.get_ground_reactions() for i in xrange(grndreact.get_num_gear_units()): gear = grndreact.get_gear_unit(i) self.assertEqual(gear.get_steer_norm(), 0.0) fpectl.turnoff_sigfpe() fdm['fcs/steer-pos-deg'] = 5.0 self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], 5.0) fdm.run() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 0.0) fdm['fcs/steer-cmd-norm'] = 1.0 self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 1.0) fdm.run() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 1.0) self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], 10.0)
def test_hold_down_with_gnd_reactions(self): fdm = CreateFDM(self.sandbox) fdm.load_script( self.sandbox.path_to_jsbsim_file('scripts', 'c1721.xml')) fdm.run_ic() ExecuteUntil(fdm, 0.25) fdm['forces/hold-down'] = 1.0 h0 = fdm['position/h-sl-ft'] pitch = fdm['attitude/pitch-rad'] roll = fdm['attitude/roll-rad'] heading = fdm['attitude/heading-true-rad'] while fdm['simulation/sim-time-sec'] < 2.0: fdm.run() self.assertAlmostEqual(fdm['accelerations/pdot-rad_sec2'], 0.0) self.assertAlmostEqual(fdm['accelerations/qdot-rad_sec2'], 0.0) self.assertAlmostEqual(fdm['accelerations/rdot-rad_sec2'], 0.0) self.assertAlmostEqual(fdm['accelerations/udot-ft_sec2'], 0.0) self.assertAlmostEqual(fdm['accelerations/vdot-ft_sec2'], 0.0) self.assertAlmostEqual(fdm['accelerations/wdot-ft_sec2'], 0.0) self.assertAlmostEqual(fdm['position/h-sl-ft'], h0, delta=1E-6) self.assertAlmostEqual(fdm['attitude/pitch-rad'], pitch) self.assertAlmostEqual(fdm['attitude/roll-rad'], roll) self.assertAlmostEqual(fdm['attitude/heading-true-rad'], heading)
def test_pitot_angle(self): script_name = 'ball_chute.xml' script_path = self.sandbox.path_to_jsbsim_file('scripts', script_name) # Add a Pitot angle to the Cessna 172 tree, aircraft_name, path_to_jsbsim_aircrafts = CopyAircraftDef(script_path, self.sandbox) root = tree.getroot() pitot_angle_deg = 5.0 self.addPitotTube(root, 5.0) contact_tag = root.find('./ground_reactions/contact') contact_tag.attrib['type'] = 'STRUCTURE' tree.write(self.sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_model('ball') pitot_angle = pitot_angle_deg * math.pi / 180. weight = fdm['inertia/weight-lbs'] spring_tag = contact_tag.find('./spring_coeff') spring_coeff = float(spring_tag.text) print "Weight=%d Spring=%d" % (weight, spring_coeff) fdm['ic/h-sl-ft'] = weight / spring_coeff fdm['forces/hold-down'] = 1.0 fdm.run_ic() ExecuteUntil(fdm, 10.) for i in xrange(36): for j in xrange(-9, 10): angle = math.pi * i / 18.0 angle2 = math.pi * j / 18.0 ca2 = math.cos(angle2) fdm['atmosphere/wind-north-fps'] = 10. * math.cos(angle) * ca2 fdm['atmosphere/wind-east-fps'] = 10. * math.sin(angle) * ca2 fdm['atmosphere/wind-down-fps'] = 10. * math.sin(angle2) fdm.run() vg = fdm['velocities/vg-fps'] self.assertAlmostEqual(vg, 0.0, delta=1E-7) vt = fdm['velocities/vt-fps'] self.assertAlmostEqual(vt, 10., delta=1E-7) mach = vt / fdm['atmosphere/a-fps'] P = fdm['atmosphere/P-psf'] pt = P * math.pow(1+0.2*mach*mach, 3.5) psl = fdm['atmosphere/P-sl-psf'] rhosl = fdm['atmosphere/rho-sl-slugs_ft3'] A = math.pow((pt-P)/psl+1.0, 1.0/3.5) alpha = fdm['aero/alpha-rad'] beta = fdm['aero/beta-rad'] vc = math.sqrt(7.0*psl/rhosl*(A-1.0))*math.cos(alpha+pitot_angle)*math.cos(beta) self.assertAlmostEqual(fdm['velocities/vc-kts'], max(0.0, vc) / 1.68781, delta=1E-7)
def testEnginePowerVC(self): # Check that the same results are obtained whether the engine power # velocity correction is given in a <table> or <function> fdm = CreateFDM(self.sandbox) fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'L4102.xml')) fdm.run_ic() while fdm.run(): pass del fdm ref = pd.read_csv('L410.csv', index_col=0) tree = et.parse(self.sandbox.path_to_jsbsim_file('engine', 'engtm601.xml')) # Modify the engine definition to use a <function> rather than a # <table> component. root = tree.getroot() engPowVC_tag = root.find("table/[@name='EnginePowerVC']") root.remove(engPowVC_tag) del engPowVC_tag.attrib['name'] func_engPowVC = et.SubElement(root, 'function') func_engPowVC.attrib['name'] = 'EnginePowerVC' func_engPowVC.append(engPowVC_tag) tree.write('engtm601.xml') # Copy the propeller file. shutil.copy(self.sandbox.path_to_jsbsim_file('engine', 'vrtule2.xml'), '.') self.sandbox.delete_csv_files() fdm = CreateFDM(self.sandbox) fdm.set_engine_path('.') fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'L4102.xml')) fdm.run_ic() while fdm.run(): pass current = pd.read_csv('L410.csv', index_col=0) # Check the data are matching i.e. the time steps are the same between # the two data sets and that the output data are also the same. self.assertTrue(isDataMatching(ref, current)) # Find all the data that are differing by more than 1E-5 between the # two data sets. diff = FindDifferences(ref, current, 0.0) self.longMessage = True self.assertEqual(len(diff), 0, msg='\n'+diff.to_string())
def testKinematicSetInitialValue(self): fdm = CreateFDM(self.sandbox) fdm.load_model('p51d') fdm.load_ic('reset01', True) fdm.run_ic() fdm['gear/gear-cmd-norm'] = 0.5 fdm['gear/gear-pos-norm'] = 0.5 while fdm['simulation/sim-time-sec'] < 1.0: fdm.run() self.assertAlmostEqual(fdm['gear/gear-cmd-norm'], 0.5) self.assertAlmostEqual(fdm['gear/gear-pos-norm'], 0.5)
def test_static_hold_down(self): fdm = CreateFDM(self.sandbox) fdm.load_model('J246') aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft') fdm.load_ic(os.path.join(aircraft_path, 'J246', 'LC39'), False) fdm['forces/hold-down'] = 1.0 fdm.run_ic() h0 = fdm['position/h-sl-ft'] t = 0.0 while t < 420.0: fdm.run() t = fdm['simulation/sim-time-sec'] self.assertAlmostEqual(fdm['position/h-sl-ft'], h0, delta=1E-5)
def testKinematicSetInitialValue(self): fdm = CreateFDM(self.sandbox) fdm.load_model('p51d') fdm.load_ic(self.sandbox.path_to_jsbsim_file('aircraft', 'p51d', 'reset01'), False) fdm.run_ic() fdm['gear/gear-cmd-norm'] = 0.5 fdm['gear/gear-pos-norm'] = 0.5 while fdm['simulation/sim-time-sec'] < 1.0: fdm.run() self.assertAlmostEqual(fdm['gear/gear-cmd-norm'], 0.5) self.assertAlmostEqual(fdm['gear/gear-pos-norm'], 0.5)
def test_steer_with_fcs(self): fdm = CreateFDM(self.sandbox) fdm.load_model('L410') aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft') fdm.load_ic(os.path.join(aircraft_path, 'L410', 'reset00'), False) fdm.run_ic() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 0.0) self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], 0.0) fdm['fcs/steer-cmd-norm'] = 1.0 self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 1.0) fdm.run() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 1.0) self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], 5.0) fdm['/controls/switches/full-steering-sw'] = 1.0 fdm.run() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 1.0) self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], 0.0) fdm['/controls/switches/full-steering-sw'] = 2.0 fdm.run() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 1.0) self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], 45.0) fdm['fcs/steer-cmd-norm'] = -0.5 fdm.run() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], -0.5) self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], -22.5)
def testKinematicSetInitialValue(self): fdm = CreateFDM(self.sandbox) fdm.load_model('p51d') fdm.load_ic( self.sandbox.path_to_jsbsim_file('aircraft', 'p51d', 'reset01'), False) fdm.run_ic() fdm['gear/gear-cmd-norm'] = 0.5 fdm['gear/gear-pos-norm'] = 0.5 while fdm['simulation/sim-time-sec'] < 1.0: fdm.run() self.assertAlmostEqual(fdm['gear/gear-cmd-norm'], 0.5) self.assertAlmostEqual(fdm['gear/gear-pos-norm'], 0.5)
def test_static_hold_down(self): fdm = CreateFDM(self.sandbox) fdm.load_model('J246') fdm.load_ic('LC39', True) fdm['forces/hold-down'] = 1.0 fdm.run_ic() h0 = fdm['position/vrp-radius-ft'] t = 0.0 while t < 420.0: fdm.run() t = fdm['simulation/sim-time-sec'] self.assertAlmostEqual(fdm['position/vrp-radius-ft'] / h0, 1.0, delta=1E-9)
def testSteadyFlight(self): script_name = 'c1722.xml' script_path = self.sandbox.path_to_jsbsim_file('scripts', script_name) self.AddAccelerometersToAircraft(script_path) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_path) # Switch the accel on fdm.set_property_value('fcs/accelerometer/on', 1.0) # Use the standard gravity (i.e. GM/r^2) fdm.set_property_value('simulation/gravity-model', 0) # Simplifies the transformation to compare the accelerometer with the # gravity fdm.set_property_value('ic/psi-true-rad', 0.0) fdm.run_ic() while fdm.get_property_value('simulation/sim-time-sec') <= 0.5: fdm.run() fdm.set_property_value('simulation/do_simple_trim', 1) ax = fdm.get_property_value('accelerations/udot-ft_sec2') ay = fdm.get_property_value('accelerations/vdot-ft_sec2') az = fdm.get_property_value('accelerations/wdot-ft_sec2') g = fdm.get_property_value('accelerations/gravity-ft_sec2') theta = fdm.get_property_value('attitude/theta-rad') # There is a lag of one time step between the computations of the # accelerations and the update of the accelerometer fdm.run() fax = fdm.get_property_value('fcs/accelerometer/X') fay = fdm.get_property_value('fcs/accelerometer/Y') faz = fdm.get_property_value('fcs/accelerometer/Z') fax -= ax fay -= ay faz -= az # Deltas are relaxed because the tolerances of the trimming algorithm # are quite relaxed themselves. self.assertAlmostEqual(faz / (g * math.cos(theta)), -1.0, delta=1E-5) self.assertAlmostEqual(fax / (g * math.sin(theta)), 1.0, delta=1E-5) self.assertAlmostEqual(math.sqrt(fax * fax + fay * fay + faz * faz) / g, 1.0, delta=1E-6) del fdm
def testOrbit(self): script_name = 'ball_orbit.xml' script_path = self.sandbox.path_to_jsbsim_file('scripts', script_name) self.AddAccelerometersToAircraft(script_path) # The time step is too small in ball_orbit so let's increase it to 0.1s # for a quicker run tree = et.parse(script_path) run_tag = tree.getroot().find('./run') run_tag.attrib['dt'] = '0.1' tree.write(script_name) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_name) # Switch the accel on fdm['fcs/accelerometer/on'] = 1.0 fdm.run_ic() while fdm.run(): self.assertAlmostEqual(fdm['fcs/accelerometer/X'], 0.0, delta=1E-8) self.assertAlmostEqual(fdm['fcs/accelerometer/Y'], 0.0, delta=1E-8) self.assertAlmostEqual(fdm['fcs/accelerometer/Z'], 0.0, delta=1E-8) self.assertAlmostEqual(fdm['accelerations/a-pilot-x-ft_sec2'], 0.0, delta=1E-8) self.assertAlmostEqual(fdm['accelerations/a-pilot-y-ft_sec2'], 0.0, delta=1E-8) self.assertAlmostEqual(fdm['accelerations/a-pilot-z-ft_sec2'], 0.0, delta=1E-8) del fdm
def test_wind_frame(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'ball_chute.xml') fdm = CreateFDM(self.sandbox) fdm.load_script(script_path) fdm.run_ic() self.assertAlmostEqual(fdm['external_reactions/parachute/location-x-in'], 12.0) self.assertAlmostEqual(fdm['external_reactions/parachute/location-y-in'], 0.0) self.assertAlmostEqual(fdm['external_reactions/parachute/location-z-in'], 0.0) self.assertAlmostEqual(fdm['external_reactions/parachute/x'], -1.0) self.assertAlmostEqual(fdm['external_reactions/parachute/y'], 0.0) self.assertAlmostEqual(fdm['external_reactions/parachute/z'], 0.0) while fdm.run(): Tw2b = fdm.get_auxiliary().get_Tw2b() mag = fdm['aero/qbar-psf'] * fdm['fcs/parachute_reef_pos_norm']*20.0 f = Tw2b * np.mat([-1.0, 0.0, 0.0]).T * mag self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], f[0, 0]) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], f[1, 0]) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], f[2, 0]) m = np.cross(self.getLeverArm(fdm,'parachute'), np.array([f[0,0], f[1,0], f[2, 0]])) self.assertAlmostEqual(fdm['moments/l-external-lbsft'], m[0]) self.assertAlmostEqual(fdm['moments/m-external-lbsft'], m[1]) self.assertAlmostEqual(fdm['moments/n-external-lbsft'], m[2])
def testFunctionWithIndexedProps(self): tree = et.parse(self.sandbox.path_to_jsbsim_file('engine', 'eng_PegasusXc.xml')) # Define the function starter-max-power-W as a 'post' function root = tree.getroot() startPowFunc_tag = root.find("function/[@name='propulsion/engine[#]/starter-max-power-W']") startPowFunc_tag.attrib['type']='post' tree.write('eng_PegasusXc.xml') # Copy the propeller file. shutil.copy(self.sandbox.path_to_jsbsim_file('engine', 'prop_deHavilland5000.xml'), '.') fdm = CreateFDM(self.sandbox) fdm.set_engine_path('.') fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'Short_S23_1.xml')) fdm.run_ic() pm = fdm.get_property_manager() self.assertTrue(pm.hasNode('propulsion/engine[0]/starter-max-power-W')) self.assertTrue(pm.hasNode('propulsion/engine[1]/starter-max-power-W')) self.assertTrue(pm.hasNode('propulsion/engine[2]/starter-max-power-W')) self.assertTrue(pm.hasNode('propulsion/engine[3]/starter-max-power-W')) while fdm.run(): rpm = [fdm['propulsion/engine[0]/engine-rpm'], fdm['propulsion/engine[1]/engine-rpm'], fdm['propulsion/engine[2]/engine-rpm'], fdm['propulsion/engine[3]/engine-rpm']] for i in range(4): maxPower = max(0.0, 1.0-rpm[i]/400)*498.941*0.10471976*rpm[i] self.assertAlmostEqual(fdm['propulsion/engine[%d]/starter-max-power-W' % (i,)], maxPower)
def test_wind_frame(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'ball_chute.xml') fdm = CreateFDM(self.sandbox) fdm.load_script(script_path) fdm.run_ic() self.assertAlmostEqual( fdm['external_reactions/parachute/location-x-in'], 12.0) self.assertAlmostEqual( fdm['external_reactions/parachute/location-y-in'], 0.0) self.assertAlmostEqual( fdm['external_reactions/parachute/location-z-in'], 0.0) self.assertAlmostEqual(fdm['external_reactions/parachute/x'], -1.0) self.assertAlmostEqual(fdm['external_reactions/parachute/y'], 0.0) self.assertAlmostEqual(fdm['external_reactions/parachute/z'], 0.0) while fdm.run(): Tw2b = fdm.get_auxiliary().get_Tw2b() mag = fdm['aero/qbar-psf'] * fdm[ 'fcs/parachute_reef_pos_norm'] * 20.0 f = Tw2b * np.mat([-1.0, 0.0, 0.0]).T * mag self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], f[0, 0]) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], f[1, 0]) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], f[2, 0]) m = np.cross(self.getLeverArm(fdm, 'parachute'), np.array([f[0, 0], f[1, 0], f[2, 0]])) self.assertAlmostEqual(fdm['moments/l-external-lbsft'], m[0]) self.assertAlmostEqual(fdm['moments/m-external-lbsft'], m[1]) self.assertAlmostEqual(fdm['moments/n-external-lbsft'], m[2])
def SubProcessScriptExecution(sandbox, script_path): fdm = CreateFDM(sandbox) fdm.load_script(script_path) fdm.run_ic() while fdm.run(): pass
def test_direct_steer(self): fdm = CreateFDM(self.sandbox) fdm.load_model('c172r') aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft') fdm.load_ic(os.path.join(aircraft_path, 'c172r', 'reset00'), False) fdm.run_ic() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 0.0) self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], 0.0) fdm['fcs/steer-pos-deg'] = 5.0 self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], 5.0) fdm.run() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 0.0) fdm['fcs/steer-cmd-norm'] = 1.0 self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 1.0) fdm.run() self.assertAlmostEqual(fdm['fcs/steer-cmd-norm'], 1.0) self.assertAlmostEqual(fdm['fcs/steer-pos-deg'], 10.0)
def test_moments_update(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'weather-balloon.xml') fdm = CreateFDM(self.sandbox) fdm.load_script(script_path) fdm.run_ic() # Moves the radio sonde to modify the CG location fdm.set_property_value('inertia/pointmass-location-X-inches', 5.0) # Check that the moment is immediately updated accordingly fdm.run() Fbz = fdm.get_property_value('forces/fbz-buoyancy-lbs') CGx = fdm.get_property_value('inertia/cg-x-in') / 12.0 # Converts from in to ft Mby = fdm.get_property_value('moments/m-buoyancy-lbsft') self.assertTrue(abs(Fbz * CGx + Mby) < 1E-7, msg="Fbz*CGx = %f and Mby = %f do not match" % (-Fbz*CGx, Mby))
def testMagnitude(self): fdm = CreateFDM(self.sandbox) fdm.load_script( self.sandbox.path_to_jsbsim_file('scripts', 'c172_cruise_8K.xml')) fdm.run_ic() t = 0.0 startup_duration = 5.0 steady_duration = 1.0 end_duration = 5.0 start_time = 10.0 magnitude = 30.0 end_time = start_time + startup_duration + steady_duration + end_duration while fdm['simulation/run_id'] == 0: fdm.run() wn = fdm['atmosphere/total-wind-north-fps'] we = fdm['atmosphere/total-wind-east-fps'] wd = fdm['atmosphere/total-wind-down-fps'] if t >= start_time and t <= end_time: wmag = math.sqrt(wn * wn + we * we + wd * wd) t -= start_time if t <= startup_duration: self.assertAlmostEqual( 0.5 * magnitude * (1.0 - math.cos(math.pi * t / startup_duration)), wmag, delta=1E-3) else: t -= startup_duration if t <= steady_duration: self.assertAlmostEqual(magnitude, wmag, delta=1E-8) else: t -= steady_duration if t <= end_duration: self.assertAlmostEqual( 0.5 * magnitude * (1.0 + math.cos(math.pi * t / end_duration)), wmag, delta=1E-3) t = fdm['simulation/sim-time-sec']
def testSteadyFlight(self): script_name = 'c1722.xml' script_path = self.sandbox.path_to_jsbsim_file('scripts', script_name) self.AddAccelerometersToAircraft(script_path) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_path) # Switch the accel on fdm.set_property_value('fcs/accelerometer/on', 1.0) # Use the standard gravity (i.e. GM/r^2) fdm.set_property_value('simulation/gravity-model', 0) # Simplifies the transformation to compare the accelerometer with the # gravity fdm.set_property_value('ic/psi-true-rad', 0.0) fdm.run_ic() while fdm.get_property_value('simulation/sim-time-sec') <= 0.5: fdm.run() fdm.set_property_value('simulation/do_simple_trim', 1) ax = fdm.get_property_value('accelerations/udot-ft_sec2') ay = fdm.get_property_value('accelerations/vdot-ft_sec2') az = fdm.get_property_value('accelerations/wdot-ft_sec2') g = fdm.get_property_value('accelerations/gravity-ft_sec2') theta = fdm.get_property_value('attitude/theta-rad') # There is a lag of one time step between the computations of the # accelerations and the update of the accelerometer fdm.run() fax = fdm.get_property_value('fcs/accelerometer/X') fay = fdm.get_property_value('fcs/accelerometer/Y') faz = fdm.get_property_value('fcs/accelerometer/Z') fax -= ax fay -= ay faz -= az # Deltas are relaxed because the tolerances of the trimming algorithm # are quite relaxed themselves. self.assertAlmostEqual(faz / (g * math.cos(theta)), -1.0, delta=1E-5) self.assertAlmostEqual(fax / (g * math.sin(theta)), 1.0, delta=1E-5) self.assertAlmostEqual(math.sqrt(fax*fax+fay*fay+faz*faz)/g, 1.0, delta=1E-6) del fdm
def test_dynamic_hold_down(self): fdm = CreateFDM(self.sandbox) fdm.load_model('J246') fdm.load_ic('LC39', True) fdm['forces/hold-down'] = 1.0 fdm.run_ic() h0 = fdm['position/vrp-radius-ft'] # Start solid rocket boosters fdm['fcs/throttle-cmd-norm[0]'] = 1.0 fdm['fcs/throttle-cmd-norm[1]'] = 1.0 t = 0.0 while t < 420.0: fdm.run() t = fdm['simulation/sim-time-sec'] self.assertAlmostEqual(fdm['position/vrp-radius-ft'] / h0, 1.0, delta=1E-9)
def test_dynamic_hold_down(self): fdm = CreateFDM(self.sandbox) fdm.load_model('J246') aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft') fdm.load_ic(os.path.join(aircraft_path, 'J246', 'LC39'), False) fdm['forces/hold-down'] = 1.0 fdm.run_ic() h0 = fdm['position/vrp-radius-ft'] # Start solid rocket boosters fdm['fcs/throttle-cmd-norm[0]'] = 1.0 fdm['fcs/throttle-cmd-norm[1]'] = 1.0 t = 0.0 while t < 420.0: fdm.run() t = fdm['simulation/sim-time-sec'] self.assertAlmostEqual(fdm['position/vrp-radius-ft'] / h0, 1.0, delta=1E-9)
def testOnGround(self): script_name = 'c1721.xml' script_path = self.sandbox.path_to_jsbsim_file('scripts', script_name) self.AddAccelerometersToAircraft(script_path) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_path) # Switch the accel on fdm.set_property_value('fcs/accelerometer/on', 1.0) # Use the standard gravity (i.e. GM/r^2) fdm.set_property_value('simulation/gravity-model', 0) # Simplifies the transformation to compare the accelerometer with the # gravity fdm.set_property_value('ic/psi-true-rad', 0.0) fdm.run_ic() for i in xrange(500): fdm.run() ax = fdm.get_property_value('accelerations/udot-ft_sec2') ay = fdm.get_property_value('accelerations/vdot-ft_sec2') az = fdm.get_property_value('accelerations/wdot-ft_sec2') g = fdm.get_property_value('accelerations/gravity-ft_sec2') theta = fdm.get_property_value('attitude/theta-rad') # There is a lag of one time step between the computations of the # accelerations and the update of the accelerometer fdm.run() fax = fdm.get_property_value('fcs/accelerometer/X') fay = fdm.get_property_value('fcs/accelerometer/Y') faz = fdm.get_property_value('fcs/accelerometer/Z') fax -= ax faz -= az self.assertAlmostEqual(fay, 0.0, delta=1E-6) self.assertAlmostEqual(fax / (g * math.sin(theta)), 1.0, delta=1E-5) self.assertAlmostEqual(faz / (g * math.cos(theta)), -1.0, delta=1E-7) del fdm
def test_trim_on_ground(self): # Check that the trim is made with up to date initial conditions fdm = CreateFDM(self.sandbox) fdm.load_model('c172x') fdm['ic/theta-deg'] = 90.0 fdm.run_ic() fdm['ic/theta-deg'] = 0.0 # If the trim fails, it will raise an exception fdm['simulation/do_simple_trim'] = 2 # Ground trim # Check that the aircraft has been trimmed successfully (velocities # should be zero i.e. the aircraft must be motionless once trimmed). while fdm['simulation/sim-time-sec'] <= 1.0: fdm.run() self.assertAlmostEqual(fdm['velocities/p-rad_sec'], 0., delta=1E-4) self.assertAlmostEqual(fdm['velocities/q-rad_sec'], 0., delta=1E-4) self.assertAlmostEqual(fdm['velocities/r-rad_sec'], 0., delta=1E-4) self.assertAlmostEqual(fdm['velocities/u-fps'], 0.0, delta=1E-4) self.assertAlmostEqual(fdm['velocities/v-fps'], 0.0, delta=1E-4) self.assertAlmostEqual(fdm['velocities/w-fps'], 0.0, delta=1E-4)
def testEnginePowerVC(self): fdm = CreateFDM(self.sandbox) fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'L4102.xml')) fdm.run_ic() pm = fdm.get_property_manager() self.assertTrue(pm.hasNode('propulsion/engine[0]/EnginePowerVC')) self.assertTrue(pm.hasNode('propulsion/engine[1]/EnginePowerVC')) while fdm.run(): self.assertAlmostEqual(fdm['propulsion/engine[0]/EnginePowerVC'], fdm['propulsion/engine[1]/EnginePowerVC'])
def testEnginePowerVC(self): fdm = CreateFDM(self.sandbox) fdm.load_script( self.sandbox.path_to_jsbsim_file('scripts', 'L4102.xml')) fdm.run_ic() pm = fdm.get_property_manager() self.assertTrue(pm.hasNode('propulsion/engine[0]/EnginePowerVC')) self.assertTrue(pm.hasNode('propulsion/engine[1]/EnginePowerVC')) while fdm.run(): self.assertAlmostEqual(fdm['propulsion/engine[0]/EnginePowerVC'], fdm['propulsion/engine[1]/EnginePowerVC'])
def testKinematicAndTrim(self): fdm = CreateFDM(self.sandbox) fdm.load_model('p51d') fdm.load_ic('reset01', True) self.assertEqual(fdm['gear/gear-cmd-norm'], 1.0) # Set the landing gears up. Since the command is equal to 1.0, the # <kinematic> system will trigger the gear down sequence. fdm['gear/gear-pos-norm'] = 0.0 fdm.run_ic() # The test succeeds if the trim does not raise an exception i.e. if the # <kinematic> system does not interfer with the trim on ground # algorithm. fdm['simulation/do_simple_trim'] = 2 # Ground trim # Check that the gear is down after the trim as requested by # gear/gear-cmd-norm self.assertAlmostEqual(fdm['gear/gear-pos-norm'], 1.0) # Check that the gear is not moving to another position after trim. fdm.run() self.assertAlmostEqual(fdm['gear/gear-pos-norm'], 1.0)
def testMagnitude(self): fdm = CreateFDM(self.sandbox) fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'c172_cruise_8K.xml')) fdm.run_ic() t = 0.0 startup_duration = 5.0 steady_duration = 1.0 end_duration = 5.0 start_time = 10.0 magnitude = 30.0 end_time = start_time + startup_duration + steady_duration + end_duration while fdm.get_property_value('simulation/run_id') == 0: fdm.run() wn = fdm.get_property_value('atmosphere/total-wind-north-fps') we = fdm.get_property_value('atmosphere/total-wind-east-fps') wd = fdm.get_property_value('atmosphere/total-wind-down-fps') if t >= start_time and t <= end_time: wmag = math.sqrt(wn*wn + we*we + wd*wd) t -= start_time if t <= startup_duration: self.assertAlmostEqual(0.5 * magnitude * (1.0 - math.cos(math.pi*t/startup_duration)), wmag, delta=1E-3) else: t -= startup_duration if t <= steady_duration: self.assertAlmostEqual(magnitude, wmag, delta=1E-8) else: t -= steady_duration if t <= end_duration: self.assertAlmostEqual(0.5 * magnitude * (1.0 + math.cos(math.pi*t/end_duration)), wmag, delta=1E-3) t = fdm.get_property_value('simulation/sim-time-sec')
def testTableWithIndexedVars(self): tree = et.parse( self.sandbox.path_to_jsbsim_file('engine', 'eng_PegasusXc.xml')) # Define the function starter-max-power-W as a 'post' function root = tree.getroot() startPowFunc_tag = root.find( "function/[@name='propulsion/engine[#]/starter-max-power-W']") startPowFunc_tag.attrib['type'] = 'post' max_tag = startPowFunc_tag.find('product/max') diff_tag = max_tag.find('difference') max_tag.remove(diff_tag) table_tag = et.SubElement(max_tag, 'table') table_tag.attrib['name'] = 'propulsion/engine[#]/starter-tabular-data' indepVar_tag = et.SubElement(table_tag, 'independentVar') indepVar_tag.attrib['lookup'] = 'row' indepVar_tag.text = 'propulsion/engine[#]/engine-rpm' tData_tag = et.SubElement(table_tag, 'tableData') tData_tag.text = '0.0 1.0\n400.0 0.0' tree.write('eng_PegasusXc.xml') # Copy the propeller file. shutil.copy( self.sandbox.path_to_jsbsim_file('engine', 'prop_deHavilland5000.xml'), '.') fdm = CreateFDM(self.sandbox) fdm.set_engine_path('.') fdm.load_script( self.sandbox.path_to_jsbsim_file('scripts', 'Short_S23_1.xml')) fdm.run_ic() pm = fdm.get_property_manager() self.assertTrue(pm.hasNode('propulsion/engine[0]/starter-max-power-W')) self.assertTrue(pm.hasNode('propulsion/engine[1]/starter-max-power-W')) self.assertTrue(pm.hasNode('propulsion/engine[2]/starter-max-power-W')) self.assertTrue(pm.hasNode('propulsion/engine[3]/starter-max-power-W')) while fdm.run(): rpm = [ fdm['propulsion/engine[0]/engine-rpm'], fdm['propulsion/engine[1]/engine-rpm'], fdm['propulsion/engine[2]/engine-rpm'], fdm['propulsion/engine[3]/engine-rpm'] ] for i in xrange(4): tabularData = max(0.0, 1.0 - rpm[i] / 400) maxPower = tabularData * 498.941 * 0.10471976 * rpm[i] self.assertAlmostEqual( fdm['propulsion/engine[%d]/starter-max-power-W' % (i, )], maxPower) self.assertAlmostEqual( fdm['propulsion/engine[%d]/starter-tabular-data' % (i, )], tabularData)
def test_moment(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'ball_chute.xml') tree, aircraft_name, aircraft_path = CopyAircraftDef( script_path, self.sandbox) extReact_element = tree.getroot().find('external_reactions') moment_element = et.SubElement(extReact_element, 'moment') moment_element.attrib['name'] = 'parachute' moment_element.attrib['frame'] = 'WIND' direction_element = et.SubElement(moment_element, 'direction') x_element = et.SubElement(direction_element, 'x') x_element.text = '0.2' y_element = et.SubElement(direction_element, 'y') y_element.text = '0.0' z_element = et.SubElement(direction_element, 'z') z_element.text = '-1.5' tree.write( self.sandbox('aircraft', aircraft_name, aircraft_name + '.xml')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_path) fdm.run_ic() mDir = np.array([0.2, 0.0, -1.5]) mDir /= np.linalg.norm(mDir) self.assertAlmostEqual(fdm['external_reactions/parachute/l'], mDir[0]) self.assertAlmostEqual(fdm['external_reactions/parachute/m'], mDir[1]) self.assertAlmostEqual(fdm['external_reactions/parachute/n'], mDir[2]) fdm['external_reactions/parachute/magnitude-lbsft'] = -3.5 while fdm.run(): Tw2b = fdm.get_auxiliary().get_Tw2b() mag = fdm['aero/qbar-psf'] * fdm[ 'fcs/parachute_reef_pos_norm'] * 20.0 f = Tw2b * np.mat([-1.0, 0.0, 0.0]).T * mag self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], f[0, 0]) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], f[1, 0]) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], f[2, 0]) m = -3.5 * Tw2b * np.mat(mDir).T fm = np.cross(self.getLeverArm(fdm, 'parachute'), np.array([f[0, 0], f[1, 0], f[2, 0]])) self.assertAlmostEqual(fdm['moments/l-external-lbsft'], m[0, 0] + fm[0]) self.assertAlmostEqual(fdm['moments/m-external-lbsft'], m[1, 0] + fm[1]) self.assertAlmostEqual(fdm['moments/n-external-lbsft'], m[2, 0] + fm[2])
def testKinematicAndTrim(self): fdm = CreateFDM(self.sandbox) fdm.load_model('p51d') fdm.load_ic(self.sandbox.path_to_jsbsim_file('aircraft', 'p51d', 'reset01'), False) self.assertEqual(fdm['gear/gear-cmd-norm'], 1.0) # Set the landing gears up. Since the command is equal to 1.0, the # <kinematic> system will trigger the gear down sequence. fdm['gear/gear-pos-norm'] = 0.0 fdm.run_ic() # The test succeeds if the trim does not raise an exception i.e. if the # <kinematic> system does not interfer with the trim on ground # algorithm. fdm['simulation/do_simple_trim'] = 2 # Ground trim # Check that the gear is down after the trim as requested by # gear/gear-cmd-norm self.assertAlmostEqual(fdm['gear/gear-pos-norm'], 1.0) # Check that the gear is not moving to another position after trim. fdm.run() self.assertAlmostEqual(fdm['gear/gear-pos-norm'], 1.0)
def testOnGround(self): script_name = 'c1721.xml' script_path = self.sandbox.path_to_jsbsim_file('scripts', script_name) self.AddAccelerometersToAircraft(script_path) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_path) # Switch the accel on fdm['fcs/accelerometer/on'] = 1.0 # Use the standard gravity (i.e. GM/r^2) fdm['simulation/gravity-model'] = 0 # Simplifies the transformation to compare the accelerometer with the # gravity fdm['ic/psi-true-rad'] = 0.0 fdm.run_ic() for i in xrange(1000): fdm.run() r = fdm['position/radius-to-vehicle-ft'] g = fdm['accelerations/gravity-ft_sec2'] latitude = fdm['position/lat-gc-rad'] pitch = fdm['attitude/theta-rad'] omega = 0.00007292115 # Earth rotation rate in rad/sec fc = r * math.cos(latitude) * omega * omega fax = fc * math.sin(latitude - pitch) + g * math.sin(pitch) faz = fc * math.cos(latitude - pitch) - g * math.cos(pitch) self.assertAlmostEqual(fdm['fcs/accelerometer/X'], fax, delta=1E-7) self.assertAlmostEqual(fdm['fcs/accelerometer/Y'], 0.0, delta=1E-7) self.assertAlmostEqual(fdm['fcs/accelerometer/Z'], faz, delta=1E-6) del fdm
def testOrbitCheckCase(self): os.environ['JSBSIM_DEBUG'] = str(0) fdm = CreateFDM(self.sandbox) fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'ball_orbit.xml')) fdm.run_ic() while fdm.run(): pass ref, current = Table(), Table() ref.ReadCSV(self.sandbox.elude(self.sandbox.path_to_jsbsim_file('logged_data', 'BallOut.csv'))) current.ReadCSV(self.sandbox('BallOut.csv')) diff = ref.compare(current) self.longMessage = True self.assertTrue(diff.empty(), msg='\n'+repr(diff))
def testChannelRate(self): os.environ['JSBSIM_DEBUG'] = str(0) fdm = CreateFDM(self.sandbox) fdm.load_script( self.sandbox.path_to_jsbsim_file('scripts', 'systems-rate-test-0.xml')) fdm.run_ic() while fdm['simulation/sim-time-sec'] < 30: fdm.run() self.assertEqual(fdm['simulation/frame'], fdm['tests/rate-1']) self.assertEqual(int(fdm['simulation/frame'] / 4), fdm['tests/rate-4']) self.assertEqual(fdm['simulation/sim-time-sec'], fdm['tests/rate-1-dt-sum']) self.assertAlmostEqual( fdm['simulation/dt'] * fdm['tests/rate-4'] * 4, fdm['tests/rate-4-dt-sum']) self.assertEqual(fdm['simulation/dt'], fdm['tests/rate-1-dt']) self.assertEqual(fdm['simulation/dt'] * 4, fdm['tests/rate-4-dt']) try: fdm['simulation/do_simple_trim'] = 1 except RuntimeError as e: # The trim cannot succeed. Just make sure that the raised exception # is due to the trim failure otherwise rethrow. if e.args[0] != 'Trim Failed': raise while fdm['simulation/sim-time-sec'] < 40: self.assertEqual(fdm['simulation/frame'], fdm['tests/rate-1']) self.assertEqual(int(fdm['simulation/frame'] / 4), fdm['tests/rate-4']) self.assertEqual(fdm['simulation/sim-time-sec'], fdm['tests/rate-1-dt-sum']) self.assertAlmostEqual( fdm['simulation/dt'] * fdm['tests/rate-4'] * 4, fdm['tests/rate-4-dt-sum']) fdm.run() fdm.reset_to_initial_conditions(1) tf = fdm['tests/rate-1-dt-sum'] xtraFrames = fdm['simulation/frame'] % 4 while fdm['simulation/sim-time-sec'] < 30: fdm.run() self.assertEqual(fdm['simulation/frame'], fdm['tests/rate-1']) self.assertEqual(int((fdm['simulation/frame'] - xtraFrames) / 4), fdm['tests/rate-4']) self.assertAlmostEqual(fdm['simulation/sim-time-sec'], fdm['tests/rate-1-dt-sum'] - tf) self.assertAlmostEqual( fdm['simulation/dt'] * fdm['tests/rate-4'] * 4, fdm['tests/rate-4-dt-sum'])
def test_moment(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'ball_chute.xml') tree, aircraft_name, aircraft_path = CopyAircraftDef(script_path, self.sandbox) extReact_element = tree.getroot().find('external_reactions') moment_element = et.SubElement(extReact_element, 'moment') moment_element.attrib['name'] = 'parachute' moment_element.attrib['frame'] = 'WIND' direction_element = et.SubElement(moment_element, 'direction') x_element = et.SubElement(direction_element, 'x') x_element.text = '0.2' y_element = et.SubElement(direction_element, 'y') y_element.text = '0.0' z_element = et.SubElement(direction_element, 'z') z_element.text = '-1.5' tree.write(self.sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_path) fdm.run_ic() mDir = np.array([0.2, 0.0, -1.5]) mDir /= np.linalg.norm(mDir) self.assertAlmostEqual(fdm['external_reactions/parachute/l'], mDir[0]) self.assertAlmostEqual(fdm['external_reactions/parachute/m'], mDir[1]) self.assertAlmostEqual(fdm['external_reactions/parachute/n'], mDir[2]) fdm['external_reactions/parachute/magnitude-lbsft'] = -3.5 while fdm.run(): Tw2b = fdm.get_auxiliary().get_Tw2b() mag = fdm['aero/qbar-psf'] * fdm['fcs/parachute_reef_pos_norm']*20.0 f = Tw2b * np.mat([-1.0, 0.0, 0.0]).T * mag self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], f[0, 0]) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], f[1, 0]) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], f[2, 0]) m = -3.5 * Tw2b * np.mat(mDir).T fm = np.cross(self.getLeverArm(fdm,'parachute'), np.array([f[0,0], f[1,0], f[2, 0]])) self.assertAlmostEqual(fdm['moments/l-external-lbsft'], m[0, 0] + fm[0]) self.assertAlmostEqual(fdm['moments/m-external-lbsft'], m[1, 0] + fm[1]) self.assertAlmostEqual(fdm['moments/n-external-lbsft'], m[2, 0] + fm[2])
def testDragFunctions(self): fdm = CreateFDM(self.sandbox) self.script_path = self.sandbox.path_to_jsbsim_file('scripts', 'x153.xml') fdm.load_script(self.script_path) fdm.set_output_directive(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.run_ic() while fdm.run(): pass results = pd.read_csv('output.csv', index_col=0) Fdrag = results['F_{Drag} (lbs)'] CDmin = results['aero/coefficient/CDmin'] CDi = results['aero/coefficient/CDi'] self.assertAlmostEqual(abs(Fdrag/(CDmin+CDi)).max(), 1.0, delta=1E-5)
def testTableWithIndexedVars(self): tree = et.parse(self.sandbox.path_to_jsbsim_file('engine', 'eng_PegasusXc.xml')) # Define the function starter-max-power-W as a 'post' function root = tree.getroot() startPowFunc_tag = root.find("function/[@name='propulsion/engine[#]/starter-max-power-W']") startPowFunc_tag.attrib['type']='post' max_tag = startPowFunc_tag.find('product/max') diff_tag = max_tag.find('difference') max_tag.remove(diff_tag) table_tag = et.SubElement(max_tag,'table') table_tag.attrib['name']='propulsion/engine[#]/starter-tabular-data' indepVar_tag = et.SubElement(table_tag, 'independentVar') indepVar_tag.attrib['lookup']='row' indepVar_tag.text = 'propulsion/engine[#]/engine-rpm' tData_tag = et.SubElement(table_tag, 'tableData') tData_tag.text ='0.0 1.0\n400.0 0.0' tree.write('eng_PegasusXc.xml') # Copy the propeller file. shutil.copy(self.sandbox.path_to_jsbsim_file('engine', 'prop_deHavilland5000.xml'), '.') fdm = CreateFDM(self.sandbox) fdm.set_engine_path('.') fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'Short_S23_1.xml')) fdm.run_ic() pm = fdm.get_property_manager() self.assertTrue(pm.hasNode('propulsion/engine[0]/starter-max-power-W')) self.assertTrue(pm.hasNode('propulsion/engine[1]/starter-max-power-W')) self.assertTrue(pm.hasNode('propulsion/engine[2]/starter-max-power-W')) self.assertTrue(pm.hasNode('propulsion/engine[3]/starter-max-power-W')) while fdm.run(): rpm = [fdm['propulsion/engine[0]/engine-rpm'], fdm['propulsion/engine[1]/engine-rpm'], fdm['propulsion/engine[2]/engine-rpm'], fdm['propulsion/engine[3]/engine-rpm']] for i in range(4): tabularData = max(0.0, 1.0-rpm[i]/400) maxPower = tabularData*498.941*0.10471976*rpm[i] self.assertAlmostEqual(fdm['propulsion/engine[%d]/starter-max-power-W' % (i,)], maxPower) self.assertAlmostEqual(fdm['propulsion/engine[%d]/starter-tabular-data' % (i,)], tabularData)
def testChannelRate(self): os.environ['JSBSIM_DEBUG'] = str(0) fdm = CreateFDM(self.sandbox) fdm.load_script( self.sandbox.path_to_jsbsim_file('scripts', 'systems-rate-test-0.xml')) fdm.run_ic() while fdm['simulation/sim-time-sec'] < 30: fdm.run() self.assertEqual(fdm['simulation/frame'], fdm['tests/rate-1']) self.assertEqual(int(fdm['simulation/frame'] / 4), fdm['tests/rate-4']) self.assertEqual(fdm['simulation/sim-time-sec'], fdm['tests/rate-1-dt-sum']) self.assertAlmostEqual( fdm['simulation/dt'] * fdm['tests/rate-4'] * 4, fdm['tests/rate-4-dt-sum']) self.assertEqual(fdm['simulation/dt'], fdm['tests/rate-1-dt']) self.assertEqual(fdm['simulation/dt'] * 4, fdm['tests/rate-4-dt']) # Trigger the trimming and check that it fails (i.e. it raises an # exception TrimFailureError) with self.assertRaises(TrimFailureError): fdm['simulation/do_simple_trim'] = 1 while fdm['simulation/sim-time-sec'] < 40: self.assertEqual(fdm['simulation/frame'], fdm['tests/rate-1']) self.assertEqual(int(fdm['simulation/frame'] / 4), fdm['tests/rate-4']) self.assertEqual(fdm['simulation/sim-time-sec'], fdm['tests/rate-1-dt-sum']) self.assertAlmostEqual( fdm['simulation/dt'] * fdm['tests/rate-4'] * 4, fdm['tests/rate-4-dt-sum']) fdm.run() fdm.reset_to_initial_conditions(1) tf = fdm['tests/rate-1-dt-sum'] xtraFrames = fdm['simulation/frame'] % 4 while fdm['simulation/sim-time-sec'] < 30: fdm.run() self.assertEqual(fdm['simulation/frame'], fdm['tests/rate-1']) self.assertEqual(int((fdm['simulation/frame'] - xtraFrames) / 4), fdm['tests/rate-4']) self.assertAlmostEqual(fdm['simulation/sim-time-sec'], fdm['tests/rate-1-dt-sum'] - tf) self.assertAlmostEqual( fdm['simulation/dt'] * fdm['tests/rate-4'] * 4, fdm['tests/rate-4-dt-sum'])
def testChannelRate(self): os.environ['JSBSIM_DEBUG'] = str(0) fdm = CreateFDM(self.sandbox) fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'systems-rate-test-0.xml')) fdm.run_ic() while fdm['simulation/sim-time-sec'] < 30: fdm.run() self.assertEqual(fdm['simulation/frame'], fdm['tests/rate-1']) self.assertEqual(int(fdm['simulation/frame']/4), fdm['tests/rate-4']) self.assertEqual(fdm['simulation/sim-time-sec'], fdm['tests/rate-1-dt-sum']) self.assertAlmostEqual(fdm['simulation/dt']*fdm['tests/rate-4']*4, fdm['tests/rate-4-dt-sum']) self.assertEqual(fdm['simulation/dt'], fdm['tests/rate-1-dt']) self.assertEqual(fdm['simulation/dt']*4, fdm['tests/rate-4-dt']) try: fdm['simulation/do_simple_trim'] = 1 except RuntimeError as e: # The trim cannot succeed. Just make sure that the raised exception # is due to the trim failure otherwise rethrow. if e.args[0] != 'Trim Failed': raise while fdm['simulation/sim-time-sec'] < 40: self.assertEqual(fdm['simulation/frame'], fdm['tests/rate-1']) self.assertEqual(int(fdm['simulation/frame']/4), fdm['tests/rate-4']) self.assertEqual(fdm['simulation/sim-time-sec'], fdm['tests/rate-1-dt-sum']) self.assertAlmostEqual(fdm['simulation/dt']*fdm['tests/rate-4']*4, fdm['tests/rate-4-dt-sum']) fdm.run() fdm.reset_to_initial_conditions(1) tf = fdm['tests/rate-1-dt-sum'] xtraFrames = fdm['simulation/frame'] % 4 while fdm['simulation/sim-time-sec'] < 30: fdm.run() self.assertEqual(fdm['simulation/frame'], fdm['tests/rate-1']) self.assertEqual(int((fdm['simulation/frame']-xtraFrames)/4), fdm['tests/rate-4']) self.assertAlmostEqual(fdm['simulation/sim-time-sec'], fdm['tests/rate-1-dt-sum']-tf) self.assertAlmostEqual(fdm['simulation/dt']*fdm['tests/rate-4']*4, fdm['tests/rate-4-dt-sum'])
def testOrbitCheckCase(self): os.environ['JSBSIM_DEBUG'] = str(0) fdm = CreateFDM(self.sandbox) fdm.load_script( self.sandbox.path_to_jsbsim_file('scripts', 'ball_orbit.xml')) fdm.run_ic() while fdm.run(): pass ref, current = Table(), Table() ref.ReadCSV( self.sandbox.elude( self.sandbox.path_to_jsbsim_file('logged_data', 'BallOut.csv'))) current.ReadCSV(self.sandbox('BallOut.csv')) diff = ref.compare(current) self.longMessage = True self.assertTrue(diff.empty(), msg='\n' + repr(diff))
def test_failhardover_without_clipto(self): tree, flight_control_element, actuator_element = self.prepare_actuator( ) rate_limit = float(actuator_element.find('rate_limit').text) fail_hardover = actuator_element.attrib[ 'name'] + "/malfunction/fail_hardover" clipto = actuator_element.find('clipto') clipmax = float(clipto.find('max').text) actuator_element.remove(clipto) output_file = os.path.join('aircraft', self.aircraft_name, self.aircraft_name + '.xml') tree.write(output_file) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(self.script_path) fdm.run_ic() # Displace the actuator in the maximum position. fdm[self.input_prop] = clipmax t = fdm['simulation/sim-time-sec'] dt = clipmax / rate_limit while fdm['simulation/sim-time-sec'] <= t + dt: fdm.run() # Check the maximum position has been reached. self.assertAlmostEqual(fdm[self.output_prop], clipmax) # Trigger "fail hardover" fdm[fail_hardover] = 1.0 t = fdm['simulation/sim-time-sec'] dt = clipmax / rate_limit while fdm['simulation/sim-time-sec'] <= t + dt: fdm.run() # Check the actuator is failed in neutral position self.assertAlmostEqual(fdm[self.output_prop], 0.0) # Check that setting an input different from the neutral position does # not result in a modification of the actuator position. fdm[self.input_prop] = clipmax t = fdm['simulation/sim-time-sec'] dt = clipmax / rate_limit while fdm['simulation/sim-time-sec'] <= t + dt: fdm.run() self.assertAlmostEqual(fdm[self.output_prop], 0.0)
def testOrbitCheckCase(self): os.environ['JSBSIM_DEBUG'] = str(0) fdm = CreateFDM(self.sandbox) fdm.load_script( self.sandbox.path_to_jsbsim_file('scripts', 'ball_orbit.xml')) fdm.run_ic() while fdm.run(): pass ref = pd.read_csv(self.sandbox.path_to_jsbsim_file( 'logged_data', 'BallOut.csv'), index_col=0) current = pd.read_csv('BallOut.csv', index_col=0) # Check the data are matching i.e. the time steps are the same between # the two data sets and that the output data are also the same. self.assertTrue(isDataMatching(ref, current)) # Find all the data that are differing by more than 1E-5 between the # two data sets. diff = FindDifferences(ref, current, 1E-5) self.longMessage = True self.assertEqual(len(diff), 0, msg='\n' + diff.to_string())
def testOrbitCheckCase(self): os.environ['JSBSIM_DEBUG'] = str(0) fdm = CreateFDM(self.sandbox) fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'ball_orbit.xml')) fdm.run_ic() while fdm.run(): pass ref = pd.read_csv(self.sandbox.path_to_jsbsim_file('logged_data', 'BallOut.csv'), index_col=0) current = pd.read_csv('BallOut.csv', index_col=0) # Check the data are matching i.e. the time steps are the same between # the two data sets and that the output data are also the same. self.assertTrue(isDataMatching(ref, current)) # Find all the data that are differing by more than 1E-5 between the # two data sets. diff = FindDifferences(ref, current, 1E-5) self.longMessage = True self.assertEqual(len(diff), 0, msg='\n'+diff.to_string())
class TestAeroFuncFrame(JSBSimTestCase): def setUp(self): JSBSimTestCase.setUp(self) self.fdm = CreateFDM(self.sandbox) self.script_path = self.sandbox.path_to_jsbsim_file('scripts', 'x153.xml') self.tree, self.aircraft_name, b = CopyAircraftDef(self.script_path, self.sandbox) self.aero2wind = np.mat(np.identity(3)); self.aero2wind[0,0] *= -1.0 self.aero2wind[2,2] *= -1.0 self.auxilliary = self.fdm.get_auxiliary() def tearDown(self): del self.fdm JSBSimTestCase.tearDown(self) def getTs2b(self): alpha = self.fdm['aero/alpha-rad'] ca = math.cos(alpha) sa = math.sin(alpha) Ts2b = np.mat([[ca, 0., -sa], [0., 1., 0.], [sa, 0., ca]]) return Ts2b def checkForcesAndMoments(self, getForces, getMoment, aeroFunc): self.fdm.load_script(self.script_path) self.fdm.run_ic() rp = np.mat([self.fdm['metrics/aero-rp-x-in'], -self.fdm['metrics/aero-rp-y-in'], self.fdm['metrics/aero-rp-z-in']]) result = {} while self.fdm.run(): for axis in aeroFunc.keys(): result[axis] = 0.0 for func in aeroFunc[axis]: result[axis] += self.fdm[func] Fa, Fb = getForces(result) Tb2s = self.getTs2b().T Fs = self.aero2wind * (Tb2s * Fb) Mb_MRC = getMoment(result) cg = np.mat([self.fdm['inertia/cg-x-in'], -self.fdm['inertia/cg-y-in'], self.fdm['inertia/cg-z-in']]) arm_ft = (cg - rp)/12.0 # Convert from inches to ft Mb = Mb_MRC + np.cross(arm_ft, Fb.T) Tb2w = self.auxilliary.get_Tb2w() Mw = Tb2w * Mb.T Ms = Tb2s * Mb.T self.assertAlmostEqual(Fa[0,0], self.fdm['forces/fwx-aero-lbs']) self.assertAlmostEqual(Fa[1,0], self.fdm['forces/fwy-aero-lbs']) self.assertAlmostEqual(Fa[2,0], self.fdm['forces/fwz-aero-lbs']) self.assertAlmostEqual(Fb[0,0], self.fdm['forces/fbx-aero-lbs']) self.assertAlmostEqual(Fb[1,0], self.fdm['forces/fby-aero-lbs']) self.assertAlmostEqual(Fb[2,0], self.fdm['forces/fbz-aero-lbs']) self.assertAlmostEqual(Fs[0,0], self.fdm['forces/fsx-aero-lbs']) self.assertAlmostEqual(Fs[1,0], self.fdm['forces/fsy-aero-lbs']) self.assertAlmostEqual(Fs[2,0], self.fdm['forces/fsz-aero-lbs']) self.assertAlmostEqual(Mb[0,0], self.fdm['moments/l-aero-lbsft']) self.assertAlmostEqual(Mb[0,1], self.fdm['moments/m-aero-lbsft']) self.assertAlmostEqual(Mb[0,2], self.fdm['moments/n-aero-lbsft']) self.assertAlmostEqual(Ms[0,0], self.fdm['moments/roll-stab-aero-lbsft']) self.assertAlmostEqual(Ms[1,0], self.fdm['moments/pitch-stab-aero-lbsft']) self.assertAlmostEqual(Ms[2,0], self.fdm['moments/yaw-stab-aero-lbsft']) self.assertAlmostEqual(Mw[0,0], self.fdm['moments/roll-wind-aero-lbsft']) self.assertAlmostEqual(Mw[1,0], self.fdm['moments/pitch-wind-aero-lbsft']) self.assertAlmostEqual(Mw[2,0], self.fdm['moments/yaw-wind-aero-lbsft']) def checkAerodynamicsFrame(self, newAxisName, getForces, getMoment, frame): aeroFunc = {} for axis in self.tree.findall('aerodynamics/axis'): axisName = newAxisName[axis.attrib['name']] axis.attrib['name'] = axisName if frame: axis.attrib['frame'] = frame aeroFunc[axisName] = [] for func in axis.findall('function'): aeroFunc[axisName].append(func.attrib['name']) if (frame == 'BODY' or len(frame) == 0) and (axisName == 'X' or axisName == 'Z'): # Convert the signs of X and Z forces so that the force # along X is directed backward and the force along Z is # directed upward. product_tag = func.find('product') value_tag = et.SubElement(product_tag, 'value') value_tag.text = '-1.0' self.tree.write(self.sandbox('aircraft', self.aircraft_name, self.aircraft_name+'.xml')) self.fdm.set_aircraft_path('aircraft') self.checkForcesAndMoments(getForces, getMoment, aeroFunc) def checkBodyFrame(self, frame): newAxisName = {'DRAG': 'X', 'SIDE': 'Y', 'LIFT': 'Z', 'ROLL': 'ROLL', 'PITCH': 'PITCH', 'YAW': 'YAW'} def getForces(result): Tb2w = self.auxilliary.get_Tb2w() Fb = np.mat([result['X'], result['Y'], result['Z']]).T Fw = Tb2w * Fb Fa = self.aero2wind * Fw return Fa, Fb def getMoment(result): return np.mat([result['ROLL'], result['PITCH'], result['YAW']]) self.checkAerodynamicsFrame(newAxisName, getForces, getMoment, '') def testBodyFrame(self): self.checkBodyFrame('') def testBodyFrameAltSyntax(self): self.checkBodyFrame('BODY') def testAxialFrame(self): newAxisName = {'DRAG': 'AXIAL', 'SIDE': 'SIDE', 'LIFT': 'NORMAL', 'ROLL': 'ROLL', 'PITCH': 'PITCH', 'YAW': 'YAW'} def getForces(result): Tb2w = self.auxilliary.get_Tb2w() Fnative = np.mat([result['AXIAL'], result['SIDE'], result['NORMAL']]).T Fb = self.aero2wind * Fnative Fw = Tb2w * Fb Fa = self.aero2wind * Fw return Fa, Fb def getMoment(result): return np.mat([result['ROLL'], result['PITCH'], result['YAW']]) self.checkAerodynamicsFrame(newAxisName, getForces, getMoment, '') def testWindFrame(self): newAxisName = {'DRAG': 'X', 'SIDE': 'Y', 'LIFT': 'Z', 'ROLL': 'ROLL', 'PITCH': 'PITCH', 'YAW': 'YAW'} def getForces(result): Tw2b = self.auxilliary.get_Tw2b() Fa = np.mat([result['X'], result['Y'], result['Z']]).T Fw = self.aero2wind * Fa Fb = Tw2b * Fw return Fa, Fb def getMoment(result): Tw2b = self.auxilliary.get_Tw2b() Mw = np.mat([result['ROLL'], result['PITCH'], result['YAW']]).T Mb = Tw2b*Mw return Mb.T self.checkAerodynamicsFrame(newAxisName, getForces, getMoment, 'WIND') def testAeroFrame(self): aeroFunc = {} for axis in self.tree.findall('aerodynamics/axis'): axisName = axis.attrib['name'] aeroFunc[axisName] = [] for func in axis.findall('function'): aeroFunc[axisName].append(func.attrib['name']) def getForces(result): Tw2b = self.auxilliary.get_Tw2b() Fa = np.mat([result['DRAG'], result['SIDE'], result['LIFT']]).T Fw = self.aero2wind * Fa Fb = Tw2b * Fw return Fa, Fb def getMoment(result): return np.mat([result['ROLL'], result['PITCH'], result['YAW']]) self.checkForcesAndMoments(getForces, getMoment, aeroFunc) def testStabilityFrame(self): newAxisName = {'DRAG': 'X', 'SIDE': 'Y', 'LIFT': 'Z', 'ROLL': 'ROLL', 'PITCH': 'PITCH', 'YAW': 'YAW'} def getForces(result): Tb2w = self.auxilliary.get_Tb2w() Ts2b = self.getTs2b() Fs = np.mat([result['X'], result['Y'], result['Z']]).T Fb = Ts2b * (self.aero2wind * Fs) Fw = Tb2w * Fb Fa = self.aero2wind * Fw return Fa, Fb def getMoment(result): Ts2b = self.getTs2b() Ms = np.mat([result['ROLL'], result['PITCH'], result['YAW']]).T Mb = Ts2b*Ms return Mb.T self.checkAerodynamicsFrame(newAxisName, getForces, getMoment, 'STABILITY')
class CheckOutputRate(JSBSimTestCase): def setUp(self): JSBSimTestCase.setUp(self) self.fdm = CreateFDM(self.sandbox) self.script_path = self.sandbox.path_to_jsbsim_file('scripts', 'c1722.xml') # Read the time step 'dt' from the script file self.tree = et.parse(self.script_path) root = self.tree.getroot() use_tag = root.find('use') aircraft_name = use_tag.attrib['aircraft'] self.run_tag = root.find('run') self.dt = float(self.run_tag.attrib['dt']) # Read the date at which the trim will be run for event in root.findall('run/event'): if event.attrib['name'] == 'Trim': cond_tag = event.find('condition') self.trim_date = float(cond_tag.text.split()[-1]) break # Read the output rate and the output file from the aircraft file aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft', aircraft_name, append_xml(aircraft_name)) tree = et.parse(aircraft_path) output_tag = tree.getroot().find('output') self.output_file = output_tag.attrib['name'] self.rateHz = float(output_tag.attrib['rate']) self.rate = int(1.0 / (self.rateHz * self.dt)) def tearDown(self): del self.fdm JSBSimTestCase.tearDown(self) def testOutputRate(self): self.fdm.load_script(self.script_path) # Check that the output is enabled by default self.assertEqual(self.fdm["simulation/output/enabled"], 1.0) # Check that the rate is consistent with the values extracted from the # script and the aircraft definition self.assertAlmostEqual(self.fdm["simulation/output/log_rate_hz"], self.rateHz, delta=1E-5) self.fdm.run_ic() for i in range(self.rate): self.fdm.run() output = pd.read_csv(self.output_file) # According to the settings, the output file must contain 2 lines in # addition to the headers : # 1. The initial conditions # 2. The output after 'rate' iterations self.assertEqual(output['Time'].iloc[0], 0.0) self.assertEqual(output['Time'].iloc[1], self.rate * self.dt) self.assertEqual(output['Time'].iloc[1], self.fdm["simulation/sim-time-sec"]) def testDisablingOutput(self): self.fdm.load_script(self.script_path) # Disables the output during the initialization self.fdm["simulation/output/enabled"] = 0.0 self.fdm.run_ic() self.fdm["simulation/output/enabled"] = 1.0 for i in range(self.rate): self.fdm.run() output = pd.read_csv(self.output_file) # According to the settings, the output file must contain 1 line in # addition to the headers : # 1. The output after 'rate' iterations self.assertEqual(output['Time'].iloc[0], self.fdm["simulation/sim-time-sec"]) def testTrimRestoresOutputSettings(self): self.fdm.load_script(self.script_path) # Disables the output during the initialization self.fdm["simulation/output/enabled"] = 0.0 self.fdm.run_ic() # Check that the output remains disabled even after the trim is # executed while self.fdm["simulation/sim-time-sec"] < self.trim_date + 2.0*self.dt: self.fdm.run() self.assertEqual(self.fdm["simulation/output/enabled"], 0.0) # Re-enable the output and check that the output rate is unaffected by # the previous operations self.fdm["simulation/output/enabled"] = 1.0 frame = int(self.fdm["simulation/frame"]) for i in range(self.rate): self.fdm.run() output = pd.read_csv(self.output_file) # The frame at which the data is logged must be the next multiple of # the output rate self.assertEqual(int(output['Time'].iloc[0] / self.dt), (1 + frame // self.rate)*self.rate) def testDisablingOutputInScript(self): property = et.SubElement(self.run_tag, 'property') property.text = 'simulation/output/enabled' property.attrib['value'] = "0.0" self.tree.write('c1722_0.xml') self.fdm.load_script('c1722_0.xml') # Check that the output is disabled self.assertEqual(self.fdm["simulation/output/enabled"], 0.0) self.fdm.run_ic() self.fdm["simulation/output/enabled"] = 1.0 for i in range(self.rate): self.fdm.run() output = pd.read_csv(self.output_file) # According to the settings, the output file must contain 1 line in # addition to the headers : # 1. The output after 'rate' iterations self.assertEqual(output['Time'].iloc[0], self.fdm["simulation/sim-time-sec"])
class CheckMomentsUpdate(unittest.TestCase): def setUp(self): self.sandbox = SandBox() def tearDown(self): self.sandbox.erase() def CheckCGPosition(self): weight = self.fdm.get_property_value('inertia/weight-lbs') empty_weight = self.fdm.get_property_value('inertia/empty-weight-lbs') contents = self.fdm.get_property_value('buoyant_forces/gas-cell/contents-mol') radiosonde_weight = weight - empty_weight - contents * mol2lbs CGx = self.fdm.get_property_value('inertia/cg-x-in') CGy = self.fdm.get_property_value('inertia/cg-y-in') CGz = self.fdm.get_property_value('inertia/cg-z-in') X = self.fdm.get_property_value('inertia/pointmass-location-X-inches') Y = self.fdm.get_property_value('inertia/pointmass-location-Y-inches') Z = self.fdm.get_property_value('inertia/pointmass-location-Z-inches') self.assertAlmostEqual(CGx, X * radiosonde_weight / weight, delta = 1E-7) self.assertAlmostEqual(CGy, Y * radiosonde_weight / weight, delta = 1E-7) self.assertAlmostEqual(CGz, Z * radiosonde_weight / weight, delta = 1E-7) def test_moments_update(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'weather-balloon.xml') self.fdm = CreateFDM(self.sandbox) self.fdm.load_script(script_path) self.fdm.set_output_directive(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) self.fdm.run_ic() self.CheckCGPosition() dt = self.fdm.get_property_value('simulation/dt') ExecuteUntil(self.fdm, 1.0-2.0*dt) self.CheckCGPosition() # Moves the radio sonde to modify the CG location self.fdm.set_property_value('inertia/pointmass-location-X-inches', 5.0) # Check that the moment is immediately updated accordingly self.fdm.run() self.CheckCGPosition() Fbx = self.fdm.get_property_value('forces/fbx-buoyancy-lbs') Fbz = self.fdm.get_property_value('forces/fbz-buoyancy-lbs') CGx = self.fdm.get_property_value('inertia/cg-x-in') / 12.0 # Converts from in to ft CGz = self.fdm.get_property_value('inertia/cg-z-in') / 12.0 Mby = self.fdm.get_property_value('moments/m-buoyancy-lbsft') self.assertAlmostEqual(Fbx * CGz - Fbz * CGx, Mby, delta=1E-7, msg="Fbx*CGz-Fbz*CGx = %f and Mby = %f do not match" % (Fbx*CGz-Fbz*CGx, Mby)) # One further step to log the same results in the output file self.fdm.run() self.CheckCGPosition() csv = Table() csv.ReadCSV(self.sandbox('output.csv')) Mby = csv.get_column('M_{Buoyant} (ft-lbs)')[-1] Fbx = csv.get_column('F_{Buoyant x} (lbs)')[-1] Fbz = csv.get_column('F_{Buoyant z} (lbs)')[-1] self.assertAlmostEqual(Fbx * CGz - Fbz * CGx, Mby, delta=1E-7, msg="Fbx*CGz-Fbz*CGx = %f and Mby = %f do not match" % (Fbx*CGz-Fbz*CGx, Mby))
def test_body_frame(self): fdm = CreateFDM(self.sandbox) aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft') fdm.load_model('f16') aircraft_path = os.path.join(aircraft_path, 'f16') fdm.load_ic(os.path.join(aircraft_path, 'reset00.xml'), False) fdm.run_ic() self.assertAlmostEqual(fdm['external_reactions/pushback/location-x-in'], -2.98081) self.assertAlmostEqual(fdm['external_reactions/pushback/location-y-in'], 0.0) self.assertAlmostEqual(fdm['external_reactions/pushback/location-z-in'], -1.9683) self.assertAlmostEqual(fdm['external_reactions/pushback/x'], 1.0) self.assertAlmostEqual(fdm['external_reactions/pushback/y'], 0.0) self.assertAlmostEqual(fdm['external_reactions/pushback/z'], 0.0) self.assertAlmostEqual(fdm['external_reactions/pushback/magnitude'], 0.0) self.assertAlmostEqual(fdm['external_reactions/hook/location-x-in'], 100.669) self.assertAlmostEqual(fdm['external_reactions/hook/location-y-in'], 0.0) self.assertAlmostEqual(fdm['external_reactions/hook/location-z-in'], -28.818) dx = -0.9995 dz = 0.01 fhook = np.array([dx, 0.0, dz]) fhook /= np.linalg.norm(fhook) self.assertAlmostEqual(fdm['external_reactions/hook/x'], fhook[0]) self.assertAlmostEqual(fdm['external_reactions/hook/y'], fhook[1]) self.assertAlmostEqual(fdm['external_reactions/hook/z'], fhook[2]) self.assertAlmostEqual(fdm['external_reactions/hook/magnitude'], 0.0) self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], 0.0) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], 0.0) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], 0.0) self.assertAlmostEqual(fdm['moments/l-external-lbsft'], 0.0) self.assertAlmostEqual(fdm['moments/m-external-lbsft'], 0.0) self.assertAlmostEqual(fdm['moments/n-external-lbsft'], 0.0) # Check the 'pushback' external force alone fdm['/sim/model/pushback/position-norm'] = 1.0 fdm['/sim/model/pushback/target-speed-fps'] = 1.0 fdm['/sim/model/pushback/kp'] = 0.05 fdm.run() fpb = np.array([1.0, 0.0, 0.0]) * 0.05 self.assertAlmostEqual(fdm['external_reactions/pushback/magnitude'], 0.05) self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], fpb[0]) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], fpb[1]) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], fpb[2]) m = np.cross(self.getLeverArm(fdm, 'pushback'), fpb) self.assertAlmostEqual(fdm['moments/l-external-lbsft'], m[0]) self.assertAlmostEqual(fdm['moments/m-external-lbsft'], m[1]) self.assertAlmostEqual(fdm['moments/n-external-lbsft'], m[2]) # Reset the 'pushback' external force to zero fdm['/sim/model/pushback/position-norm'] = 0.0 fdm.run() self.assertAlmostEqual(fdm['external_reactions/pushback/magnitude'], 0.0) self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], 0.0) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], 0.0) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], 0.0) self.assertAlmostEqual(fdm['moments/l-external-lbsft'], 0.0) self.assertAlmostEqual(fdm['moments/m-external-lbsft'], 0.0) self.assertAlmostEqual(fdm['moments/n-external-lbsft'], 0.0) # Check the 'hook' external force alone fdm['external_reactions/hook/magnitude'] = 10.0 fhook *= 10.0 fdm.run() self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], fhook[0]) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], fhook[1]) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], fhook[2]) m = np.cross(self.getLeverArm(fdm, 'hook'), fhook) self.assertAlmostEqual(fdm['moments/l-external-lbsft'], m[0]) self.assertAlmostEqual(fdm['moments/m-external-lbsft'], m[1]) self.assertAlmostEqual(fdm['moments/n-external-lbsft'], m[2]) # Add the 'pushback' force to the hook force and check that the global # external forces is the sum of the push back force and the hook force. fdm['/sim/model/pushback/position-norm'] = 1.0 fdm.run() fp = fdm['systems/pushback/force'] fpb = np.array([1.0, 0.0, 0.0]) * fp f = fhook + fpb self.assertAlmostEqual(fdm['external_reactions/pushback/magnitude'], fp) self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], f[0]) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], f[1]) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], f[2]) # Modify the push back force direction and check that the global external # force is modified accordingly. fdm['external_reactions/pushback/x'] = 1.5 fdm['external_reactions/pushback/y'] = 0.1 fdm.run() fp = fdm['systems/pushback/force'] fpb = np.array([1.5, 0.1, 0.0]) * fp f = fhook + fpb self.assertAlmostEqual(fdm['external_reactions/pushback/magnitude'], fp) self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], f[0]) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], f[1]) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], f[2]) m = (np.cross(self.getLeverArm(fdm, 'pushback'), fpb) + np.cross(self.getLeverArm(fdm, 'hook'), fhook)) self.assertAlmostEqual(fdm['moments/l-external-lbsft'], m[0]) self.assertAlmostEqual(fdm['moments/m-external-lbsft'], m[1]) self.assertAlmostEqual(fdm['moments/n-external-lbsft'], m[2]) fdm['external_reactions/hook/location-y-in'] = 50.0 fdm.run() fp = fdm['systems/pushback/force'] fpb = np.array([1.5, 0.1, 0.0]) * fp f = fhook + fpb self.assertAlmostEqual(fdm['external_reactions/pushback/magnitude'], fp) self.assertAlmostEqual(fdm['forces/fbx-external-lbs'], f[0]) self.assertAlmostEqual(fdm['forces/fby-external-lbs'], f[1]) self.assertAlmostEqual(fdm['forces/fbz-external-lbs'], f[2]) m = (np.cross(self.getLeverArm(fdm, 'pushback'), fpb) + np.cross(self.getLeverArm(fdm, 'hook'), fhook)) self.assertAlmostEqual(fdm['moments/l-external-lbsft'], m[0]) self.assertAlmostEqual(fdm['moments/m-external-lbsft'], m[1]) self.assertAlmostEqual(fdm['moments/n-external-lbsft'], m[2])
class CheckMomentsUpdate(JSBSimTestCase): def CheckCGPosition(self): weight = self.fdm['inertia/weight-lbs'] empty_weight = self.fdm['inertia/empty-weight-lbs'] contents = self.fdm['buoyant_forces/gas-cell/contents-mol'] radiosonde_weight = weight - empty_weight - contents * mol2lbs CGx = self.fdm['inertia/cg-x-in'] CGy = self.fdm['inertia/cg-y-in'] CGz = self.fdm['inertia/cg-z-in'] X = self.fdm['inertia/pointmass-location-X-inches'] Y = self.fdm['inertia/pointmass-location-Y-inches'] Z = self.fdm['inertia/pointmass-location-Z-inches'] self.assertAlmostEqual(CGx, X * radiosonde_weight / weight, delta=1E-7) self.assertAlmostEqual(CGy, Y * radiosonde_weight / weight, delta=1E-7) self.assertAlmostEqual(CGz, Z * radiosonde_weight / weight, delta=1E-7) def test_moments_update(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'weather-balloon.xml') self.fdm = CreateFDM(self.sandbox) self.fdm.load_script(script_path) self.fdm.set_output_directive(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) self.fdm.run_ic() self.CheckCGPosition() dt = self.fdm['simulation/dt'] ExecuteUntil(self.fdm, 1.0-2.0*dt) self.CheckCGPosition() # Moves the radio sonde to modify the CG location self.fdm['inertia/pointmass-location-X-inches'] = 5.0 # Check that the moment is immediately updated accordingly self.fdm.run() self.CheckCGPosition() Fbx = self.fdm['forces/fbx-buoyancy-lbs'] Fbz = self.fdm['forces/fbz-buoyancy-lbs'] CGx = self.fdm['inertia/cg-x-in'] / 12.0 # Converts from in to ft CGz = self.fdm['inertia/cg-z-in'] / 12.0 Mby = self.fdm['moments/m-buoyancy-lbsft'] self.assertAlmostEqual(Fbx * CGz - Fbz * CGx, Mby, delta=1E-7, msg="Fbx*CGz-Fbz*CGx = %f and Mby = %f do not match" % (Fbx*CGz-Fbz*CGx, Mby)) # One further step to log the same results in the output file self.fdm.run() self.CheckCGPosition() csv = pd.read_csv('output.csv') Mby = csv['M_{Buoyant} (ft-lbs)'].iget(-1) Fbx = csv['F_{Buoyant x} (lbs)'].iget(-1) Fbz = csv['F_{Buoyant z} (lbs)'].iget(-1) self.assertAlmostEqual(Fbx * CGz - Fbz * CGx, Mby, delta=1E-7, msg="Fbx*CGz-Fbz*CGx = %f and Mby = %f do not match" % (Fbx*CGz-Fbz*CGx, Mby))