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(self.sandbox.elude(script_path)) run_tag = tree.getroot().find('./run') run_tag.attrib['dt'] = '0.1' tree.write(self.sandbox(script_name)) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_name) # Switch the accel on fdm.set_property_value('fcs/accelerometer/on', 1.0) fdm.run_ic() while fdm.run(): self.assertAlmostEqual(fdm.get_property_value('fcs/accelerometer/X'), 0.0, delta=1E-8) self.assertAlmostEqual(fdm.get_property_value('fcs/accelerometer/Y'), 0.0, delta=1E-8) self.assertAlmostEqual(fdm.get_property_value('fcs/accelerometer/Z'), 0.0, delta=1E-8) self.assertAlmostEqual(fdm.get_property_value('accelerations/a-pilot-x-ft_sec2'), 0.0, delta=1E-8) self.assertAlmostEqual(fdm.get_property_value('accelerations/a-pilot-y-ft_sec2'), 0.0, delta=1E-8) self.assertAlmostEqual(fdm.get_property_value('accelerations/a-pilot-z-ft_sec2'), 0.0, delta=1E-8) del fdm
def Compare(self, section): # Rerun the script with the modified aircraft definition self.sandbox.delete_csv_files() fdm = CreateFDM(self.sandbox) # We need to tell JSBSim that the aircraft definition is located in the # directory build/.../aircraft fdm.set_aircraft_path('aircraft') fdm.set_output_directive( self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.load_script(self.script) fdm.set_property_value('simulation/randomseed', 0.0) fdm.run_ic() ExecuteUntil(fdm, 50.0) mod = Table() mod.ReadCSV(self.sandbox('output.csv')) # Whether the data is read from the aircraft definition file or from an # external file, the results shall be exactly identical. Hence the # precision set to 0.0. diff = self.ref.compare(mod, 0.0) self.assertTrue(diff.empty(), msg='\nTesting section "' + section + '"\n' + repr(diff))
def test_fuel_tanks_inertia(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'c1722.xml') # The aircraft c172x does not contain an <inertia_factor> tag so we need # to add one. tree, aircraft_name, b = CopyAircraftDef(script_path, self.sandbox) tank_tag = tree.getroot().find('./propulsion/tank') inertia_factor = et.SubElement(tank_tag, 'inertia_factor') inertia_factor.text = '1.0' 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() contents0 = fdm.get_property_value('propulsion/tank/contents-lbs') ixx0 = fdm.get_property_value('propulsion/tank/local-ixx-slug_ft2') iyy0 = fdm.get_property_value('propulsion/tank/local-iyy-slug_ft2') izz0 = fdm.get_property_value('propulsion/tank/local-izz-slug_ft2') # Remove half of the tank contents and check that the inertias are # updated accordingly fdm.set_property_value('propulsion/tank/contents-lbs', 0.5*contents0) contents = fdm.get_property_value('propulsion/tank/contents-lbs') ixx = fdm.get_property_value('propulsion/tank/local-ixx-slug_ft2') iyy = fdm.get_property_value('propulsion/tank/local-iyy-slug_ft2') izz = fdm.get_property_value('propulsion/tank/local-izz-slug_ft2') self.assertTrue(abs(contents-0.5*contents0) < 1E-7, msg="The tank content (%f lbs) should be %f lbs" % (contents, 0.5*contents0)) self.assertTrue(abs(ixx-0.5*ixx0) < 1E-7, msg="The tank inertia Ixx (%f slug*ft^2) should be %f slug*ft^2" % (ixx, 0.5*ixx0)) self.assertTrue(abs(iyy-0.5*iyy0) < 1E-7, msg="The tank inertia Iyy (%f slug*ft^2) should be %f slug*ft^2" % (iyy, 0.5*iyy0)) self.assertTrue(abs(izz-0.5*izz0) < 1E-7, msg="The tank inertia Izz (%f slug*ft^2) should be %f slug*ft^2" % (izz, 0.5*izz0)) # Execute the script and check that the fuel inertias have been updated # along with the consumption. ExecuteUntil(fdm, 200.0) contents = fdm.get_property_value('propulsion/tank/contents-lbs') ixx = fdm.get_property_value('propulsion/tank/local-ixx-slug_ft2') iyy = fdm.get_property_value('propulsion/tank/local-iyy-slug_ft2') izz = fdm.get_property_value('propulsion/tank/local-izz-slug_ft2') contents_ratio = contents / contents0 ixx_ratio = ixx / ixx0 iyy_ratio = iyy / iyy0 izz_ratio = izz / izz0 self.assertTrue(abs(contents_ratio - ixx_ratio) < 1E-7, msg="Ixx does not vary as the tank content does\nIxx ratio=%f\nContents ratio=%f" % (ixx_ratio, contents_ratio)) self.assertTrue(abs(contents_ratio - iyy_ratio) < 1E-7, msg="Iyy does not vary as the tank content does\nIyy ratio=%f\nContents ratio=%f" % (iyy_ratio, contents_ratio)) self.assertTrue(abs(contents_ratio - izz_ratio) < 1E-7, msg="Izz does not vary as the tank content does\nIzz ratio=%f\nContents ratio=%f" % (izz_ratio, contents_ratio))
def CheckRateLimit(script_path, input_prop, output_prop, incr_limit, decr_limit): fdm = CreateFDM(sandbox) fdm.set_aircraft_path('aircraft') ScriptExecution(fdm, script_path, 1.0) fdm.set_property_value(input_prop, 1.0) CheckRateValue(fdm, output_prop, incr_limit) fdm.set_property_value(input_prop, 0.0) CheckRateValue(fdm, output_prop, decr_limit)
def testSpinningBodyOnOrbit(self): script_name = 'ball_orbit.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_model('ball') # Offset the CG along Y (by 30") fdm.set_property_value('inertia/pointmass-weight-lbs[1]', 50.0) aircraft_path = self.sandbox.elude( self.sandbox.path_to_jsbsim_file('aircraft', 'ball')) fdm.load_ic(os.path.join(aircraft_path, 'reset00.xml'), False) # Switch the accel on fdm.set_property_value('fcs/accelerometer/on', 1.0) # Set the orientation such that the spinning axis is Z. fdm.set_property_value('ic/phi-rad', 0.5 * math.pi) # Set the angular velocities to 0.0 in the ECEF frame. The angular # velocity R_{inertial} will therefore be equal to the Earth rotation # rate 7.292115E-5 rad/sec fdm.set_property_value('ic/p-rad_sec', 0.0) fdm.set_property_value('ic/q-rad_sec', 0.0) fdm.set_property_value('ic/r-rad_sec', 0.0) fdm.run_ic() fax = fdm.get_property_value('fcs/accelerometer/X') fay = fdm.get_property_value('fcs/accelerometer/Y') faz = fdm.get_property_value('fcs/accelerometer/Z') cgy_ft = fdm.get_property_value('inertia/cg-y-in') / 12. omega = 0.00007292115 # Earth rotation rate in rad/sec self.assertAlmostEqual( fdm.get_property_value('accelerations/a-pilot-x-ft_sec2'), fax, delta=1E-8) self.assertAlmostEqual( fdm.get_property_value('accelerations/a-pilot-y-ft_sec2'), fay, delta=1E-8) self.assertAlmostEqual( fdm.get_property_value('accelerations/a-pilot-z-ft_sec2'), faz, delta=1E-8) # Acceleration along X should be zero self.assertAlmostEqual(fax, 0.0, delta=1E-8) # Acceleration along Y should be equal to r*omega^2 self.assertAlmostEqual(fay / (cgy_ft * omega * omega), 1.0, delta=1E-7) # Acceleration along Z should be zero self.assertAlmostEqual(faz, 0.0, delta=1E-8)
def test_static_hold_down(self): fdm = CreateFDM(self.sandbox) fdm.load_model('J246') aircraft_path = self.sandbox.elude(self.sandbox.path_to_jsbsim_file('aircraft')) fdm.load_ic(os.path.join(aircraft_path, 'J246', 'LC39'), False) fdm.set_property_value('forces/hold-down', 1.0) fdm.run_ic() h0 = fdm.get_property_value('position/h-sl-ft') t = 0.0 while t < 420.0: fdm.run() t = fdm.get_property_value('simulation/sim-time-sec') self.assertAlmostEqual(fdm.get_property_value('position/h-sl-ft'), h0, delta=1E-5)
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 CheckRateLimit(self, input_prop, output_prop, incr_limit, decr_limit): fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path("aircraft") self.ScriptExecution(fdm, 1.0) fdm.set_property_value(input_prop, 1.0) self.CheckRateValue(fdm, output_prop, incr_limit) fdm.set_property_value(input_prop, 0.0) self.CheckRateValue(fdm, output_prop, decr_limit) # Because JSBSim internals use static pointers, we cannot rely on Python # garbage collector to decide when the FDM is destroyed otherwise we can # get dangling pointers. del fdm
def CheckRateLimit(self, input_prop, output_prop, incr_limit, decr_limit): fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') self.ScriptExecution(fdm, 1.0) fdm.set_property_value(input_prop, 1.0) self.CheckRateValue(fdm, output_prop, incr_limit) fdm.set_property_value(input_prop, 0.0) self.CheckRateValue(fdm, output_prop, decr_limit) # Because JSBSim internals use static pointers, we cannot rely on Python # garbage collector to decide when the FDM is destroyed otherwise we can # get dangling pointers. del fdm
def test_static_hold_down(self): fdm = CreateFDM(self.sandbox) fdm.load_model('J246') aircraft_path = self.sandbox.elude( self.sandbox.path_to_jsbsim_file('aircraft')) fdm.load_ic(os.path.join(aircraft_path, 'J246', 'LC39'), False) fdm.set_property_value('forces/hold-down', 1.0) fdm.run_ic() h0 = fdm.get_property_value('position/h-sl-ft') t = 0.0 while t < 420.0: fdm.run() t = fdm.get_property_value('simulation/sim-time-sec') self.assertAlmostEqual(fdm.get_property_value('position/h-sl-ft'), h0, delta=1E-5)
def testSpinningBodyOnOrbit(self): script_name = 'ball_orbit.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_model('ball') # Offset the CG along Y (by 30") fdm.set_property_value('inertia/pointmass-weight-lbs[1]', 50.0) aircraft_path = self.sandbox.elude(self.sandbox.path_to_jsbsim_file('aircraft', 'ball')) fdm.load_ic(os.path.join(aircraft_path, 'reset00.xml'), False) # Switch the accel on fdm.set_property_value('fcs/accelerometer/on', 1.0) # Set the orientation such that the spinning axis is Z. fdm.set_property_value('ic/phi-rad', 0.5*math.pi) # Set the angular velocities to 0.0 in the ECEF frame. The angular # velocity R_{inertial} will therefore be equal to the Earth rotation # rate 7.292115E-5 rad/sec fdm.set_property_value('ic/p-rad_sec', 0.0) fdm.set_property_value('ic/q-rad_sec', 0.0) fdm.set_property_value('ic/r-rad_sec', 0.0) fdm.run_ic() fax = fdm.get_property_value('fcs/accelerometer/X') fay = fdm.get_property_value('fcs/accelerometer/Y') faz = fdm.get_property_value('fcs/accelerometer/Z') cgy_ft = fdm.get_property_value('inertia/cg-y-in') / 12. omega = 0.00007292115 # Earth rotation rate in rad/sec self.assertAlmostEqual(fdm.get_property_value('accelerations/a-pilot-x-ft_sec2'), fax, delta=1E-8) self.assertAlmostEqual(fdm.get_property_value('accelerations/a-pilot-y-ft_sec2'), fay, delta=1E-8) self.assertAlmostEqual(fdm.get_property_value('accelerations/a-pilot-z-ft_sec2'), faz, delta=1E-8) # Acceleration along X should be zero self.assertAlmostEqual(fax, 0.0, delta=1E-8) # Acceleration along Y should be equal to r*omega^2 self.assertAlmostEqual(fay / (cgy_ft * omega * omega), 1.0, delta=1E-7) # Acceleration along Z should be zero self.assertAlmostEqual(faz, 0.0, delta=1E-8)
def test_gust_reset(self): fdm = CreateFDM(self.sandbox) fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'c172_cruise_8K.xml')) fdm.set_property_value('simulation/randomseed', 0.0) fdm.set_output_directive(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.run_ic() ExecuteUntil(fdm, 15.5) ref, current = Table(), Table() ref.ReadCSV(self.sandbox('output.csv')) fdm.set_property_value('simulation/randomseed', 0.0) fdm.reset_to_initial_conditions(1) ExecuteUntil(fdm, 15.5) current.ReadCSV(self.sandbox('output_0.csv')) diff = ref.compare(current) self.longMessage = True self.assertTrue(diff.empty(), msg='\n'+repr(diff))
def test_no_script(self): fdm = CreateFDM(self.sandbox) aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft') fdm.set_aircraft_path(aircraft_path) fdm.load_model('c172x') aircraft_path = os.path.join(self.sandbox.elude(aircraft_path), 'c172x') fdm.load_ic(os.path.join(aircraft_path, 'reset01.xml'), False) fdm.run_ic() self.assertEqual(fdm.get_property_value('simulation/sim-time-sec'), 0.0) ExecuteUntil(fdm, 5.0) t = fdm.get_property_value('simulation/sim-time-sec') fdm.set_property_value('simulation/do_simple_trim', 1) self.assertEqual(fdm.get_property_value('simulation/sim-time-sec'), t) fdm.reset_to_initial_conditions(1) self.assertEqual(fdm.get_property_value('simulation/sim-time-sec'), 0.0) 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(self.sandbox.elude(script_path)) run_tag = tree.getroot().find('./run') run_tag.attrib['dt'] = '0.1' tree.write(self.sandbox(script_name)) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_name) # Switch the accel on fdm.set_property_value('fcs/accelerometer/on', 1.0) fdm.run_ic() while fdm.run(): self.assertAlmostEqual( fdm.get_property_value('fcs/accelerometer/X'), 0.0, delta=1E-8) self.assertAlmostEqual( fdm.get_property_value('fcs/accelerometer/Y'), 0.0, delta=1E-8) self.assertAlmostEqual( fdm.get_property_value('fcs/accelerometer/Z'), 0.0, delta=1E-8) self.assertAlmostEqual( fdm.get_property_value('accelerations/a-pilot-x-ft_sec2'), 0.0, delta=1E-8) self.assertAlmostEqual( fdm.get_property_value('accelerations/a-pilot-y-ft_sec2'), 0.0, delta=1E-8) self.assertAlmostEqual( fdm.get_property_value('accelerations/a-pilot-z-ft_sec2'), 0.0, delta=1E-8) del fdm
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 BuildReference(self, script_name): # Run the script self.script = self.sandbox.path_to_jsbsim_file(os.path.join('scripts', script_name)) self.sandbox.delete_csv_files() fdm = CreateFDM(self.sandbox) fdm.set_output_directive(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.load_script(self.script) fdm.set_property_value('simulation/randomseed', 0.0) fdm.run_ic() ExecuteUntil(fdm, 50.0) self.ref = Table() self.ref.ReadCSV(self.sandbox("output.csv")) # Since the script will work with modified versions of the aircraft XML # definition file, we need to make a copy of the directory that contains # all the input data of that aircraft tree, self.aircraft_name, self.path_to_jsbsim_aircrafts = CopyAircraftDef(self.script, self.sandbox) self.aircraft_path = self.sandbox('aircraft', self.aircraft_name)
def Compare(self, section): # Rerun the script with the modified aircraft definition self.sandbox.delete_csv_files() fdm = CreateFDM(self.sandbox) # We need to tell JSBSim that the aircraft definition is located in the # directory build/.../aircraft fdm.set_aircraft_path('aircraft') fdm.set_output_directive(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.load_script(self.script) fdm.set_property_value('simulation/randomseed', 0.0) fdm.run_ic() ExecuteUntil(fdm, 50.0) mod = Table() mod.ReadCSV(self.sandbox('output.csv')) # Whether the data is read from the aircraft definition file or from an # external file, the results shall be exactly identical. Hence the # precision set to 0.0. diff = self.ref.compare(mod, 0.0) self.assertTrue(diff.empty(), msg='\nTesting section "'+section+'"\n'+repr(diff))
def test_gust_reset(self): fdm = CreateFDM(self.sandbox) fdm.load_script( self.sandbox.path_to_jsbsim_file('scripts', 'c172_cruise_8K.xml')) fdm.set_property_value('simulation/randomseed', 0.0) fdm.set_output_directive( self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.run_ic() ExecuteUntil(fdm, 15.5) ref, current = Table(), Table() ref.ReadCSV(self.sandbox('output.csv')) fdm.set_property_value('simulation/randomseed', 0.0) fdm.reset_to_initial_conditions(1) ExecuteUntil(fdm, 15.5) current.ReadCSV(self.sandbox('output_0.csv')) diff = ref.compare(current) self.longMessage = True self.assertTrue(diff.empty(), msg='\n' + repr(diff))
def BuildReference(self, script_name): # Run the script self.script = self.sandbox.path_to_jsbsim_file( os.path.join('scripts', script_name)) self.sandbox.delete_csv_files() fdm = CreateFDM(self.sandbox) fdm.set_output_directive( self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.load_script(self.script) fdm.set_property_value('simulation/randomseed', 0.0) fdm.run_ic() ExecuteUntil(fdm, 50.0) self.ref = Table() self.ref.ReadCSV(self.sandbox("output.csv")) # Since the script will work with modified versions of the aircraft XML # definition file, we need to make a copy of the directory that contains # all the input data of that aircraft tree, self.aircraft_name, self.path_to_jsbsim_aircrafts = CopyAircraftDef( self.script, self.sandbox) self.aircraft_path = self.sandbox('aircraft', self.aircraft_name)
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 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
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_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() metrics_tag = root.find('./metrics') pitot_tag = et.SubElement(metrics_tag, 'pitot_angle') pitot_tag.attrib['unit'] = 'DEG' pitot_tag.text = '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 = float(pitot_tag.text) * math.pi / 180. weight = fdm.get_property_value('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.set_property_value('ic/h-sl-ft', weight / spring_coeff) fdm.set_property_value('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.set_property_value('atmosphere/wind-north-fps', 10. * math.cos(angle) * ca2) fdm.set_property_value('atmosphere/wind-east-fps', 10. * math.sin(angle) * ca2) fdm.set_property_value('atmosphere/wind-down-fps', 10. * math.sin(angle2)) fdm.run() vg = fdm.get_property_value('velocities/vg-fps') self.assertAlmostEqual(vg, 0.0, delta=1E-7) vt = fdm.get_property_value('velocities/vt-fps') self.assertAlmostEqual(vt, 10., delta=1E-7) mach = vt / fdm.get_property_value('atmosphere/a-fps') P = fdm.get_property_value('atmosphere/P-psf') pt = P * math.pow(1+0.2*mach*mach, 3.5) psl = fdm.get_property_value('atmosphere/P-sl-psf') rhosl = fdm.get_property_value('atmosphere/rho-sl-slugs_ft3') A = math.pow((pt-P)/psl+1.0, 1.0/3.5) alpha = fdm.get_property_value('aero/alpha-rad') beta = fdm.get_property_value('aero/beta-rad') vc = math.sqrt(7.0*psl/rhosl*(A-1.0))*math.cos(alpha+pitot_angle)*math.cos(beta) self.assertAlmostEqual(fdm.get_property_value('velocities/vc-kts'), max(0.0, vc) / 1.68781, delta=1E-7)
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() metrics_tag = root.find('./metrics') pitot_tag = et.SubElement(metrics_tag, 'pitot_angle') pitot_tag.attrib['unit'] = 'DEG' pitot_tag.text = '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 = float(pitot_tag.text) * math.pi / 180. weight = fdm.get_property_value('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.set_property_value('ic/h-sl-ft', weight / spring_coeff) fdm.set_property_value('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.set_property_value('atmosphere/wind-north-fps', 10. * math.cos(angle) * ca2) fdm.set_property_value('atmosphere/wind-east-fps', 10. * math.sin(angle) * ca2) fdm.set_property_value('atmosphere/wind-down-fps', 10. * math.sin(angle2)) fdm.run() vg = fdm.get_property_value('velocities/vg-fps') self.assertAlmostEqual(vg, 0.0, delta=1E-7) vt = fdm.get_property_value('velocities/vt-fps') self.assertAlmostEqual(vt, 10., delta=1E-7) mach = vt / fdm.get_property_value('atmosphere/a-fps') P = fdm.get_property_value('atmosphere/P-psf') pt = P * math.pow(1 + 0.2 * mach * mach, 3.5) psl = fdm.get_property_value('atmosphere/P-sl-psf') rhosl = fdm.get_property_value('atmosphere/rho-sl-slugs_ft3') A = math.pow((pt - P) / psl + 1.0, 1.0 / 3.5) alpha = fdm.get_property_value('aero/alpha-rad') beta = fdm.get_property_value('aero/beta-rad') vc = math.sqrt( 7.0 * psl / rhosl * (A - 1.0)) * math.cos(alpha + pitot_angle) * math.cos(beta) self.assertAlmostEqual( fdm.get_property_value('velocities/vc-kts'), max(0.0, vc) / 1.68781, delta=1E-7)
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))
class CheckOutputRate(unittest.TestCase): def setUp(self): self.sandbox = SandBox() 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.sandbox.elude(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 event_tags = root.findall('./run/event') for event in event_tags: if event.attrib['name'] == 'Trim': cond_tag = event.find('./condition') self.trim_date = float(string.split(cond_tag.text)[-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(self.sandbox.elude(aircraft_path)) output_tag = tree.getroot().find("./output") self.output_file = self.sandbox(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 self.sandbox.erase() def testOutputRate(self): self.fdm.load_script(self.script_path) # Check that the output is enabled by default self.assertEqual(self.fdm.get_property_value("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.get_property_value("simulation/output/log_rate_hz"), self.rateHz, delta=1E-5) self.fdm.run_ic() for i in xrange(self.rate): self.fdm.run() output = Table() output.ReadCSV(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.get_column(0)[1], 0.0) self.assertEqual(output.get_column(0)[2], self.rate * self.dt) self.assertEqual(output.get_column(0)[2], self.fdm.get_property_value("simulation/sim-time-sec")) def testDisablingOutput(self): self.fdm.load_script(self.script_path) # Disables the output during the initialization self.fdm.set_property_value("simulation/output/enabled", 0.0) self.fdm.run_ic() self.fdm.set_property_value("simulation/output/enabled", 1.0) for i in xrange(self.rate): self.fdm.run() output = Table() output.ReadCSV(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.get_column(0)[1], self.fdm.get_property_value("simulation/sim-time-sec")) def testTrimRestoresOutputSettings(self): self.fdm.load_script(self.script_path) # Disables the output during the initialization self.fdm.set_property_value("simulation/output/enabled", 0.0) self.fdm.run_ic() # Check that the output remains disabled even after the trim is # executed while self.fdm.get_property_value("simulation/sim-time-sec") < self.trim_date + 2.0*self.dt: self.fdm.run() self.assertEqual(self.fdm.get_property_value("simulation/output/enabled"), 0.0) # Re-enable the output and check that the output rate is unaffected by # the previous operations self.fdm.set_property_value("simulation/output/enabled", 1.0) frame = int(self.fdm.get_property_value("simulation/frame")) for i in xrange(self.rate): self.fdm.run() output = Table() output.ReadCSV(self.output_file) # The frame at which the data is logged must be the next multiple of # the output rate self.assertEqual(int(output.get_column(0)[1]/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(self.sandbox('c1722_0.xml')) self.fdm.load_script('c1722_0.xml') # Check that the output is disabled self.assertEqual(self.fdm.get_property_value("simulation/output/enabled"), 0.0) self.fdm.run_ic() self.fdm.set_property_value("simulation/output/enabled", 1.0) for i in xrange(self.rate): self.fdm.run() output = Table() output.ReadCSV(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.get_column(0)[1], self.fdm.get_property_value("simulation/sim-time-sec"))
class CheckOutputRate(unittest.TestCase): def setUp(self): self.sandbox = SandBox() 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.sandbox.elude(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 event_tags = root.findall("./run/event") for event in event_tags: if event.attrib["name"] == "Trim": cond_tag = event.find("./condition") self.trim_date = float(string.split(cond_tag.text)[-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(self.sandbox.elude(aircraft_path)) output_tag = tree.getroot().find("./output") self.output_file = self.sandbox(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 self.sandbox.erase() def testOutputRate(self): self.fdm.load_script(self.script_path) # Check that the output is enabled by default self.assertEqual(self.fdm.get_property_value("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.get_property_value("simulation/output/log_rate_hz"), self.rateHz, delta=1e-5) self.fdm.run_ic() for i in xrange(self.rate): self.fdm.run() output = Table() output.ReadCSV(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.get_column(0)[1], 0.0) self.assertEqual(output.get_column(0)[2], self.rate * self.dt) self.assertEqual(output.get_column(0)[2], self.fdm.get_property_value("simulation/sim-time-sec")) def testDisablingOutput(self): self.fdm.load_script(self.script_path) # Disables the output during the initialization self.fdm.set_property_value("simulation/output/enabled", 0.0) self.fdm.run_ic() self.fdm.set_property_value("simulation/output/enabled", 1.0) for i in xrange(self.rate): self.fdm.run() output = Table() output.ReadCSV(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.get_column(0)[1], self.fdm.get_property_value("simulation/sim-time-sec")) def testTrimRestoresOutputSettings(self): self.fdm.load_script(self.script_path) # Disables the output during the initialization self.fdm.set_property_value("simulation/output/enabled", 0.0) self.fdm.run_ic() # Check that the output remains disabled even after the trim is # executed while self.fdm.get_property_value("simulation/sim-time-sec") < self.trim_date + 2.0 * self.dt: self.fdm.run() self.assertEqual(self.fdm.get_property_value("simulation/output/enabled"), 0.0) # Re-enable the output and check that the output rate is unaffected by # the previous operations self.fdm.set_property_value("simulation/output/enabled", 1.0) frame = int(self.fdm.get_property_value("simulation/frame")) for i in xrange(self.rate): self.fdm.run() output = Table() output.ReadCSV(self.output_file) # The frame at which the data is logged must be the next multiple of # the output rate self.assertEqual(int(output.get_column(0)[1] / 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(self.sandbox("c1722_0.xml")) self.fdm.load_script("c1722_0.xml") # Check that the output is disabled self.assertEqual(self.fdm.get_property_value("simulation/output/enabled"), 0.0) self.fdm.run_ic() self.fdm.set_property_value("simulation/output/enabled", 1.0) for i in xrange(self.rate): self.fdm.run() output = Table() output.ReadCSV(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.get_column(0)[1], self.fdm.get_property_value("simulation/sim-time-sec"))