def test_fuel_tanks_content(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'J2460.xml') fdm = CreateFDM(self.sandbox) fdm.load_script(script_path) fdm.run_ic() tree = et.parse(script_path) use_tag = tree.getroot().find('use') aircraft_name = use_tag.attrib['aircraft'] aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft', aircraft_name) aircraft_tree = et.parse(os.path.join(aircraft_path, aircraft_name+'.xml')) total_fuel_quantity = 0.0 total_oxidizer_quantity = 0.0 for tank in aircraft_tree.findall('propulsion/tank'): contents = float(tank.find('contents').text) if tank.attrib['type'] == "FUEL": total_fuel_quantity += contents elif tank.attrib['type'] == 'OXIDIZER': total_oxidizer_quantity += contents self.assertAlmostEqual(fdm['propulsion/total-fuel-lbs'], total_fuel_quantity) self.assertAlmostEqual(fdm['propulsion/total-oxidizer-lbs'], total_oxidizer_quantity)
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 setUp(self): self.sandbox = SandBox() script_path = self.sandbox.path_to_jsbsim_file('scripts', 'c1722.xml') # The aircraft c172x does not contain an <input> tag so we need # to add one. tree, aircraft_name, b = CopyAircraftDef(script_path, self.sandbox) self.root = tree.getroot() input_tag = et.SubElement(self.root, 'input') input_tag.attrib['port']='1137' tree.write(self.sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) self.fdm = CreateFDM(self.sandbox) self.fdm.set_aircraft_path('aircraft') self.fdm.load_script(script_path) self.fdm.run_ic() self.fdm.hold() # Execute JSBSim in a separate thread self.cond = threading.Condition() self.thread = JSBSimThread(self.fdm, self.cond, 5., time.time()) self.thread.start() # Wait for the thread to be started before connecting a telnet session self.cond.acquire() self.cond.wait() self.tn = telnetlib.Telnet("localhost", 1137) self.cond.release()
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 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_actuator_rate_from_property(self): # Second part of the test. # ####################### # # The test is run again but this time, <rate_limit> will be read from a # property instead of being read from a value hard coded in the # aircraft definition file. It has been reported in the bug 1503 of # FlightGear that for such a configuration the <actuator> output is # constantly increasing even if the input is null. For this script the # <actuator> output is stored in the property # fcs/left-aileron-pos-rad. The function ScriptExecution will monitor # that property and if it changes then the test is failed. tree = et.parse(os.path.join(self.path_to_jsbsim_aircrafts, self.aircraft_name+'.xml')) actuator_element = tree.getroot().find('flight_control/channel/actuator//rate_limit/..') rate_element = actuator_element.find('rate_limit') flight_control_element = tree.getroot().find('flight_control') property = et.SubElement(flight_control_element, 'property') property.text = 'fcs/rate-limit-value' property.attrib['value'] = rate_element.text actuator_element = flight_control_element.find('channel/actuator//rate_limit/..') rate_element = actuator_element.find('rate_limit') rate_element.attrib['sense'] = 'decr' rate_element.text = property.text new_rate_element = et.SubElement(actuator_element, 'rate_limit') new_rate_element.attrib['sense'] = 'incr' new_rate_element.text = rate_element.text tree.write(self.sandbox('aircraft', self.aircraft_name, self.aircraft_name+'.xml')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') self.ScriptExecution(fdm) del fdm
def test_trim_on_ground(self): fdm = CreateFDM(self.sandbox) fdm.load_model('c172x') fdm['ic/theta-deg'] = 10.0 fdm.run_ic() fdm['ic/theta-deg'] = 0.0 fdm['simulation/do_simple_trim'] = 2
def test_gust_reset(self): fdm = CreateFDM(self.sandbox) fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'c172_cruise_8K.xml')) fdm['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 = pd.read_csv('output.csv', index_col=0) fdm['simulation/randomseed'] = 0.0 fdm.reset_to_initial_conditions(1) ExecuteUntil(fdm, 15.5) current = pd.read_csv('output_0.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-8 between the # two data sets. diff = FindDifferences(ref, current, 1E-8) self.longMessage = True self.assertEqual(len(diff), 0, msg='\n'+diff.to_string())
def testDebugLvl(self): fdm = CreateFDM(self.sandbox) fdm.load_script(self.sandbox.path_to_jsbsim_file('scripts', 'ball_orbit.xml')) fdm.run_ic() ExecuteUntil(fdm, 1000.) ref = pd.read_csv('BallOut.csv', index_col=0) del fdm 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() ExecuteUntil(fdm, 1000.) 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-8 between the # two data sets. diff = FindDifferences(ref, current, 1E-8) self.longMessage = True self.assertEqual(len(diff), 0, msg='\n'+diff.to_string())
def test_output(self): tree = et.parse(self.script_path) output_tag = et.SubElement(tree.getroot(), 'output') output_tag.attrib['name'] = 'test.csv' output_tag.attrib['type'] = 'CSV' output_tag.attrib['rate'] = '10' property_tag = et.SubElement(output_tag, 'property') property_tag.text = 'position/vrp-radius-ft' tree.write('c1722_0.xml') fdm = CreateFDM(self.sandbox) fdm.load_script('c1722_0.xml') fdm.run_ic() ExecuteUntil(fdm, 10.) self.assertTrue(self.sandbox.exists(output_tag.attrib['name']), msg="The file 'output.csv' has not been created") orig = pd.read_csv('JSBout172B.csv', index_col=0) test = pd.read_csv('test.csv', index_col=0) pname = '/fdm/jsbsim/' + property_tag.text ref = orig[pname] mod = test[pname] # Check the data are matching i.e. the time steps are the same between # the two data sets. self.assertTrue(isDataMatching(ref, mod)) # Find all the data that are differing by more than 1E-8 between the # two data sets. delta = pd.concat([np.abs(ref - mod), ref, mod], axis=1) delta.columns = ['delta', 'ref value', 'value'] diff = delta[delta['delta'] > 1E-8] self.longMessage = True self.assertEqual(len(diff), 0, msg='\n'+diff.to_string())
def SubProcessScriptExecution(self, sandbox, script_path, aircraft_path, time_limit): self.script_path = script_path self.sandbox = sandbox fdm = CreateFDM(sandbox) fdm.set_aircraft_path(aircraft_path) self.ScriptExecution(fdm, time_limit)
def testAircrafts(self): aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft') for d in os.listdir(aircraft_path): fullpath = os.path.join(aircraft_path, d) # Is d a directory ? if not os.path.isdir(fullpath): continue f = os.path.join(aircraft_path, d, d+'.xml') # Is f an aircraft definition file ? if not CheckXMLFile(f, 'fdm_config'): continue if d in ('blank'): continue fdm = CreateFDM(self.sandbox) self.assertTrue(fdm.load_model(d), msg='Failed to load aircraft %s' % (d,)) for f in os.listdir(fullpath): f = os.path.join(aircraft_path, d, f) if CheckXMLFile(f, 'initialize'): self.assertTrue(fdm.load_ic(f, False), msg='Failed to load IC %s for aircraft %s' % (f, d)) try: fdm.run_ic() except RuntimeError: self.fail('Failed to run IC %s for aircraft %s' % (f, d)) del fdm
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 LoadScript(self, tree, script_path, prop_output_to_CSV=[]): # Make a local copy of files referenced by the script. for element in list(tree.getroot()): if 'file' in element.keys(): name = append_xml(element.attrib['file']) name_with_path = os.path.join(os.path.dirname(script_path), name) if os.path.exists(name_with_path): shutil.copy(name_with_path, name) # Generate a CSV file to check that it is correctly initialized # with the initial values output_tag = et.SubElement(tree.getroot(), 'output') output_tag.attrib['name'] = 'check_csv_values.csv' output_tag.attrib['type'] = 'CSV' output_tag.attrib['rate'] = '10' position_tag = et.SubElement(output_tag, 'position') position_tag.text = 'ON' velocities_tag = et.SubElement(output_tag, 'velocities') velocities_tag.text = 'ON' for props in prop_output_to_CSV: property_tag = et.SubElement(output_tag, 'property') property_tag.text = props f = os.path.split(script_path)[-1] # Script name tree.write(f) # Initialize the script fdm = CreateFDM(self.sandbox) self.assertTrue(fdm.load_script(f), msg="Failed to load script %s" % (f,)) fdm.run_ic() return (f, fdm)
def test_actuator_rate_from_property(self): # Second part of the test. # ####################### # # The test is run again but this time, <rate_limit> will be read from a # property instead of being read from a value hard coded in the aircraft # definition file. It has been reported in the bug 1503 of FlightGear # that for such a configuration the <actuator> output is constantly # increasing even if the input is null. For this script the <actuator> # output is stored in the property fcs/left-aileron-pos-rad. The # function ScriptExecution will monitor that property and if it changes # then the test is failed. tree = et.parse(os.path.join(self.path_to_jsbsim_aircrafts, self.aircraft_name + ".xml")) actuator_element = tree.getroot().find("flight_control/channel/actuator//rate_limit/..") rate_element = actuator_element.find("rate_limit") flight_control_element = tree.getroot().find("flight_control") property = et.SubElement(flight_control_element, "property") property.text = "fcs/rate-limit-value" property.attrib["value"] = rate_element.text actuator_element = flight_control_element.find("channel/actuator//rate_limit/..") rate_element = actuator_element.find("rate_limit") rate_element.attrib["sense"] = "decr" rate_element.text = property.text new_rate_element = et.SubElement(actuator_element, "rate_limit") new_rate_element.attrib["sense"] = "incr" new_rate_element.text = rate_element.text tree.write(self.sandbox("aircraft", self.aircraft_name, self.aircraft_name + ".xml")) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path("aircraft") self.ScriptExecution(fdm) 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['simulation/randomseed'] = 0.0 fdm.run_ic() ExecuteUntil(fdm, 50.0) mod = pd.read_csv('output.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(self.ref, mod)) # 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 = FindDifferences(self.ref, mod, 0.0) self.assertEqual(len(diff), 0, msg='\nTesting section "' + section + '"\n' + diff.to_string())
def LoadScript(self, tree, script_path, prop_output_to_CSV=[]): # Make a local copy of files referenced by the script. for element in list(tree.getroot()): if 'file' in element.keys(): name = append_xml(element.attrib['file']) name_with_path = os.path.join(os.path.dirname(script_path), name) if os.path.exists(name_with_path): shutil.copy(name_with_path, name) # Generate a CSV file to check that it is correctly initialized # with the initial values output_tag = et.SubElement(tree.getroot(), 'output') output_tag.attrib['name'] = 'check_csv_values.csv' output_tag.attrib['type'] = 'CSV' output_tag.attrib['rate'] = '10' position_tag = et.SubElement(output_tag, 'position') position_tag.text = 'ON' velocities_tag = et.SubElement(output_tag, 'velocities') velocities_tag.text = 'ON' for props in prop_output_to_CSV: property_tag = et.SubElement(output_tag, 'property') property_tag.text = props f = os.path.split(script_path)[-1] # Script name tree.write(f) # Initialize the script fdm = CreateFDM(self.sandbox) self.assertTrue(fdm.load_script(f), msg="Failed to load script %s" % (f, )) fdm.run_ic() return (f, fdm)
def testScripts(self): for s in self.script_list(): fdm = CreateFDM(self.sandbox) self.assertTrue(fdm.load_script(s), msg="Failed to load script %s" % (s,)) fdm.run_ic() del fdm
def test_no_output(self): fdm = CreateFDM(self.sandbox) fdm.load_script(self.script_path) fdm.run_ic() ExecuteUntil(fdm, 10.) self.assertFalse(self.sandbox.exists('output.csv'), msg="Results have unexpectedly been written to 'output.csv'")
def test_std_atmosphere(self): fdm = CreateFDM(self.sandbox) fdm.load_model('ball') self.check_temperature(fdm, self.T0, 0.0) self.check_pressure(fdm, self.P0, self.T0, 0.0) del fdm
def test_asl_override_vs_geod_lat(self): fdm = CreateFDM(self.sandbox) fdm.load_model('c310') fdm.load_ic(self.sandbox.path_to_jsbsim_file('aircraft', 'c310', 'ellington.xml'), False) geod_lat = fdm['ic/lat-geod-deg'] fdm['ic/h-sl-ft'] = 35000. self.assertAlmostEqual(fdm['ic/lat-geod-deg'], geod_lat)
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['simulation/randomseed'] = 0.0 fdm.run_ic() ExecuteUntil(fdm, 50.0) mod = pd.read_csv('output.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(self.ref, mod)) # 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 = FindDifferences(self.ref, mod, 0.0) self.assertEqual(len(diff), 0, msg='\nTesting section "'+section+'"\n'+diff.to_string())
def test_CAS_ic(self): script_name = 'Short_S23_3.xml' script_path = self.sandbox.path_to_jsbsim_file('scripts', script_name) # Add a Pitot angle to the Short S23 tree, aircraft_name, path_to_jsbsim_aircrafts = CopyAircraftDef(script_path, self.sandbox) metrics_tag = tree.getroot().find('./metrics') pitot_tag = et.SubElement(metrics_tag, 'pitot_angle') pitot_tag.attrib['unit'] = 'DEG' pitot_tag.text = '5.0' tree.write(self.sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) # Read the CAS specified in the IC file tree = et.parse(self.sandbox.elude(script_path)) use_element = tree.getroot().find('use') IC_file = use_element.attrib['initialize'] tree = et.parse(os.path.join(path_to_jsbsim_aircrafts, append_xml(IC_file))) vc_tag = tree.getroot().find('./vc') VCAS = float(vc_tag.text) if 'unit' in vc_tag.attrib and vc_tag.attrib['unit'] == 'FT/SEC': VCAS /= 1.68781 # Run the IC and check that the model is initialized correctly fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_path) fdm.run_ic() self.assertAlmostEqual(fdm.get_property_value('ic/vc-kts'), VCAS, delta=1E-7) self.assertAlmostEqual(fdm.get_property_value('velocities/vc-kts'), VCAS, delta=1E-7)
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_sl_pressure_bias(self): fdm = CreateFDM(self.sandbox) fdm.load_model('ball') P_sl = 95000. fdm['atmosphere/P-sl-psf'] = P_sl * self.Pa_to_psf self.check_temperature(fdm, self.T0, 0.0) self.check_pressure(fdm, P_sl, self.T0, 0.0) del fdm
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_temperature_bias(self): fdm = CreateFDM(self.sandbox) fdm.load_model('ball') delta_T_K = 15.0 T_sl = self.T0 + delta_T_K fdm['atmosphere/delta-T'] = delta_T_K * self.K_to_R self.check_temperature(fdm, T_sl, 0.0) self.check_pressure(fdm, self.P0, T_sl, 0.0) del fdm
def test_output_from_file(self): tree = et.parse(self.sandbox.elude(self.script_path)) output_tag = et.SubElement(tree.getroot(), 'output') output_tag.attrib['file'] = self.sandbox.elude(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) tree.write(self.sandbox('c1722_0.xml')) fdm = CreateFDM(self.sandbox) fdm.load_script('c1722_0.xml') fdm.run_ic() ExecuteUntil(fdm, 10.) self.assertTrue(self.sandbox.exists('output.csv'), msg="The file 'output.csv' has not been created")
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 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 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_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 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 test_CAS_ic(self): script_name = 'Short_S23_3.xml' script_path = self.sandbox.path_to_jsbsim_file('scripts', script_name) # Add a Pitot angle to the Short S23 tree, aircraft_name, path_to_jsbsim_aircrafts = CopyAircraftDef(script_path, self.sandbox) self.addPitotTube(tree.getroot(), 5.0) tree.write(self.sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) # Read the CAS specified in the IC file tree = et.parse(script_path) use_element = tree.getroot().find('use') IC_file = use_element.attrib['initialize'] tree = et.parse(os.path.join(path_to_jsbsim_aircrafts, append_xml(IC_file))) vc_tag = tree.getroot().find('./vc') VCAS = float(vc_tag.text) if 'unit' in vc_tag.attrib and vc_tag.attrib['unit'] == 'FT/SEC': VCAS /= 1.68781 # Converts in kts # Run the IC and check that the model is initialized correctly fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_path) fdm.run_ic() self.assertAlmostEqual(fdm['ic/vc-kts'], VCAS, delta=1E-7) self.assertAlmostEqual(fdm['velocities/vc-kts'], VCAS, 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_waypoint(self): fdm = CreateFDM(self.sandbox) fdm.load_model('c310') aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft') fdm.load_ic(os.path.join(aircraft_path, 'c310', 'reset00'), False) slr = 20925646.32546 # Sea Level Radius TestCases = ( (0.25 * math.pi, 0.5 * math.pi, 0.0, 0.0), (0.0, 0.5 * math.pi, math.pi, slr * 0.25 * math.pi), # North pole (0.5 * math.pi, 0.5 * math.pi, 0.0, slr * 0.25 * math.pi), # South pole (-0.5 * math.pi, 0.5 * math.pi, math.pi, slr * 0.75 * math.pi), (0.0, 0.0, 1.5 * math.pi, slr * 0.5 * math.pi), (0.0, math.pi, 0.5 * math.pi, slr * 0.5 * math.pi)) fdm['ic/lat-gc-rad'] = TestCases[0][0] fdm['ic/long-gc-rad'] = TestCases[0][1] for case in TestCases: fdm['guidance/target_wp_latitude_rad'] = case[0] fdm['guidance/target_wp_longitude_rad'] = case[1] fdm.run_ic() self.assertAlmostEqual(fdm['guidance/wp-heading-rad'], case[2]) self.assertAlmostEqual(fdm['guidance/wp-distance'], case[3])
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['fcs/accelerometer/on'] = 1.0 # Use the standard gravity (i.e. GM/r^2) fdm['simulation/gravity-model'] = 0 # Select an orientation such that frame transformations simplify fdm['ic/psi-true-rad'] = 0.0 fdm.run_ic() ExecuteUntil(fdm, 0.1) fdm['simulation/do_simple_trim'] = 1 r = fdm['position/radius-to-vehicle-ft'] pitch = fdm['attitude/theta-rad'] roll = fdm['attitude/phi-rad'] latitude = fdm['position/lat-gc-rad'] g = fdm['accelerations/gravity-ft_sec2'] omega = 0.00007292115 # Earth rotation rate in rad/sec fc = r * math.cos(latitude) * omega * omega # Centrifugal force uvw = np.array(fdm.get_propagate().get_uvw().T)[0] Omega = omega * np.array([ math.cos(pitch - latitude), math.sin(pitch - latitude) * math.sin(roll), math.sin(pitch - latitude) * math.cos(roll) ]) # Compute the acceleration measured by the accelerometer as the sum of # the gravity and the centrifugal and Coriolis forces. fa_yz = (fc * math.cos(latitude - pitch) - g * math.cos(pitch)) fa = np.array([(fc * math.sin(latitude - pitch) + g * math.sin(pitch)), fa_yz * math.sin(roll), fa_yz * math.cos(roll) ]) + np.cross(2.0 * Omega, uvw) # After the trim we are close to the equilibrium but there remains a # small residual that we have to take the bias into account fax = fa[0] + fdm['accelerations/udot-ft_sec2'] fay = fa[1] + fdm['accelerations/vdot-ft_sec2'] faz = fa[2] + fdm['accelerations/wdot-ft_sec2'] # Deltas are relaxed because the tolerances of the trimming algorithm # are quite relaxed themselves. self.assertAlmostEqual(fdm['fcs/accelerometer/X'], fax, delta=1E-6) self.assertAlmostEqual(fdm['fcs/accelerometer/Y'], fay, delta=1E-4) self.assertAlmostEqual(fdm['fcs/accelerometer/Z'], faz, delta=1E-5) del fdm
def test_humidity_parameters(self): # Table: Dew point (deg C), Vapor pressure (Pa), RH humidity_table = [ (-40.0, 19.021201, 0.815452, 1.2040321), (-30.0, 51.168875, 2.193645, 1.2038877), (-20.0, 125.965126, 5.4002118, 1.2035517), (-10.0, 287.031031, 12.3052182, 1.2028282), ( 0.0, 611.2, 26.2025655, 1.2013721), ( 10.0, 1226.030206, 52.5607604, 1.1986102), ( 20.0, 2332.5960221, 100., 1.1936395) ] fdm = CreateFDM(self.sandbox) fdm.load_model('ball') fdm['atmosphere/delta-T'] = 5.0*self.K_to_R fdm.run_ic() Psat = fdm['atmosphere/saturated-vapor-pressure-psf']/self.Pa_to_psf self.assertAlmostEqual(Psat, humidity_table[-1][1]) self.assertAlmostEqual(fdm['atmosphere/vapor-pressure-psf'], 0.0) self.assertAlmostEqual(fdm['atmosphere/RH'], 0.0) self.assertAlmostEqual(fdm['atmosphere/dew-point-R'], 54.054) for Tdp, Pv, RH, rho in humidity_table: fdm['atmosphere/dew-point-R'] = (Tdp+273.15)*self.K_to_R fdm.run_ic() self.assertAlmostEqual(fdm['atmosphere/vapor-pressure-psf'], Pv*self.Pa_to_psf) self.assertAlmostEqual(fdm['atmosphere/RH'], RH) self.assertAlmostEqual(fdm['atmosphere/rho-slugs_ft3']/(self.kg_to_slug*math.pow(0.3048,3)), rho) 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 test_pressure_and_temperature_bias(self): fdm = CreateFDM(self.sandbox) fdm.load_model('ball') delta_T_K = 15.0 T_sl = self.T0 + delta_T_K fdm['atmosphere/delta-T'] = delta_T_K * self.K_to_R P_sl = 95000. fdm['atmosphere/P-sl-psf'] = P_sl * self.Pa_to_psf self.check_temperature(fdm, T_sl, 0.0) self.check_pressure(fdm, P_sl, T_sl, 0.0) del fdm
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 test_trim_doesnt_ignite_rockets(self): # Run a longitudinal trim with a rocket equipped with solid propellant # boosters (aka SRBs). The trim algorithm will try to reach a vertical # equilibrium by tweaking the throttle but since the rocket is nose up, # the trim cannot converge. As a result the algorithm will set full # throttle which will result in the SRBs ignition if the integration is # not suspended. This bug has been reported in FlightGear and this test # is checking that there is no regression. fdm = CreateFDM(self.sandbox) fdm.load_model('J246') fdm.load_ic( self.sandbox.path_to_jsbsim_file('aircraft', 'J246', 'LC39'), False) fdm.run_ic() # Check that the SRBs are not ignited self.assertEqual(fdm['propulsion/engine[0]/thrust-lbs'], 0.0) self.assertEqual(fdm['propulsion/engine[1]/thrust-lbs'], 0.0) 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 # Check that the trim did not ignite the SRBs self.assertEqual(fdm['propulsion/engine[0]/thrust-lbs'], 0.0) self.assertEqual(fdm['propulsion/engine[1]/thrust-lbs'], 0.0)
def test_FG_reset(self): # This test reproduces how FlightGear resets. The important thing is # that the property manager is managed by FlightGear. So it is not # deleted when the JSBSim instance is killed. pm = jsbsim.FGPropertyManager(new_instance=True) self.assertFalse(pm.hasNode('fdm/jsbsim/ic/lat-geod-deg')) fdm = CreateFDM(self.sandbox, pm) fdm.load_model('ball') self.assertAlmostEqual(fdm['ic/lat-geod-deg'], 0.0) fdm['ic/lat-geod-deg'] = 45.0 fdm.run_ic() del fdm # Check that the property ic/lat-geod-deg has survived the JSBSim # instance. self.assertTrue(pm.hasNode('fdm/jsbsim/ic/lat-geod-deg')) # Re-use the property manager just as FlightGear does. fdm = CreateFDM(self.sandbox, pm) self.assertAlmostEqual(fdm['ic/lat-geod-deg'], 45.0) del fdm
def testScripts(self): script_path = self.sandbox.path_to_jsbsim_file('scripts') for f in os.listdir(self.sandbox.elude(script_path)): fullpath = os.path.join(self.sandbox.elude(script_path), f) # Does f contains a JSBSim script ? if not CheckXMLFile(fullpath, 'runscript'): continue fdm = CreateFDM(self.sandbox) self.assertTrue(fdm.load_script(os.path.join(script_path, f)), msg="Failed to load script %s" % (fullpath,)) fdm.run_ic() self.scripts += 1 del fdm
def testScripts(self): fpectl.turnon_sigfpe() for s in self.script_list(['737_cruise_steady_turn_simplex.xml']): fdm = CreateFDM(self.sandbox) try: self.assertTrue(fdm.load_script(s), msg="Failed to load script %s" % (s,)) fdm.run_ic() ExecuteUntil(fdm, 30.) except Exception as e: self.fail("Script %s failed:\n%s" % (s, e.args[0])) del fdm
def test_output_from_file(self): tree = et.parse(self.script_path) output_tag = et.SubElement(tree.getroot(), 'output') # Relative path from the aircraft directory to the output directive # file output_tag.attrib['file'] = os.path.join('..', '..', 'tests', 'output.xml') tree.write('c1722_0.xml') fdm = CreateFDM(self.sandbox) fdm.load_script('c1722_0.xml') fdm.run_ic() ExecuteUntil(fdm, 10.) self.assertTrue(self.sandbox.exists('output.csv'), msg="The file 'output.csv' has not been created")
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 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 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[input_prop] = 1.0 self.CheckRateValue(fdm, output_prop, incr_limit) fdm[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_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 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_script_start_time_0(self): script_name = 'ball_orbit.xml' script_path = self.sandbox.path_to_jsbsim_file('scripts', script_name) fdm = CreateFDM(self.sandbox) fdm.load_script(script_path) fdm.run_ic() self.assertEqual(fdm.get_property_value('simulation/sim-time-sec'), 0.0) ExecuteUntil(fdm, 5.0) fdm.reset_to_initial_conditions(1) self.assertEqual(fdm.get_property_value('simulation/sim-time-sec'), 0.0) del fdm
def test_waypoint(self): fdm = CreateFDM(self.sandbox) fdm.load_model('c310') aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft') fdm.load_ic(os.path.join(aircraft_path, 'c310', 'reset00'), False) slr = 20925646.32546 # Sea Level Radius TestCases = ((0.25*math.pi, 0.5*math.pi, 0.0, 0.0), (0.0, 0.5*math.pi, math.pi, slr*0.25*math.pi), # North pole (0.5*math.pi, 0.5*math.pi, 0.0, slr*0.25*math.pi), # South pole (-0.5*math.pi, 0.5*math.pi, math.pi, slr*0.75*math.pi), (0.0, 0.0, 1.5*math.pi, slr*0.5*math.pi), (0.0, math.pi, 0.5*math.pi, slr*0.5*math.pi)) fdm['ic/lat-gc-rad'] = TestCases[0][0] fdm['ic/long-gc-rad'] = TestCases[0][1] for case in TestCases: fdm['guidance/target_wp_latitude_rad'] = case[0] fdm['guidance/target_wp_longitude_rad'] = case[1] fdm.run_ic() self.assertAlmostEqual(fdm['guidance/wp-heading-rad'], case[2]) self.assertAlmostEqual(fdm['guidance/wp-distance'], case[3])
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 test_trim_doesnt_ignite_rockets(self): # Run a longitudinal trim with a rocket equipped with solid propellant # boosters (aka SRBs). The trim algorithm will try to reach a vertical # equilibrium by tweaking the throttle but since the rocket is nose up, # the trim cannot converge. As a result the algorithm will set full # throttle which will result in the SRBs ignition if the integration is # not suspended. This bug has been reported in FlightGear and this test # is checking that there is no regression. fdm = CreateFDM(self.sandbox) fdm.load_model('J246') fdm.load_ic(self.sandbox.path_to_jsbsim_file('aircraft', 'J246', 'LC39'), False) fdm.run_ic() # Check that the SRBs are not ignited self.assertEqual(fdm['propulsion/engine[0]/thrust-lbs'], 0.0) self.assertEqual(fdm['propulsion/engine[1]/thrust-lbs'], 0.0) 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 # Check that the trim did not ignite the SRBs self.assertEqual(fdm['propulsion/engine[0]/thrust-lbs'], 0.0) self.assertEqual(fdm['propulsion/engine[1]/thrust-lbs'], 0.0)