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 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 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_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_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 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 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_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_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_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['propulsion/tank/contents-lbs'] ixx0 = fdm['propulsion/tank/local-ixx-slug_ft2'] iyy0 = fdm['propulsion/tank/local-iyy-slug_ft2'] izz0 = fdm['propulsion/tank/local-izz-slug_ft2'] # Remove half of the tank contents and check that the inertias are # updated accordingly fdm['propulsion/tank/contents-lbs'] = 0.5*contents0 contents = fdm['propulsion/tank/contents-lbs'] ixx = fdm['propulsion/tank/local-ixx-slug_ft2'] iyy = fdm['propulsion/tank/local-iyy-slug_ft2'] izz = fdm['propulsion/tank/local-izz-slug_ft2'] self.assertAlmostEqual(contents, 0.5*contents0, delta=1E-7, msg="The tank content (%f lbs) should be %f lbs" % (contents, 0.5*contents0)) self.assertAlmostEqual(ixx, 0.5*ixx0, delta=1E-7, msg="The tank inertia Ixx (%f slug*ft^2) should be %f slug*ft^2" % (ixx, 0.5*ixx0)) self.assertAlmostEqual(iyy, 0.5*iyy0, delta=1E-7, msg="The tank inertia Iyy (%f slug*ft^2) should be %f slug*ft^2" % (iyy, 0.5*iyy0)) self.assertAlmostEqual(izz, 0.5*izz0, delta=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['propulsion/tank/contents-lbs'] ixx = fdm['propulsion/tank/local-ixx-slug_ft2'] iyy = fdm['propulsion/tank/local-iyy-slug_ft2'] izz = fdm['propulsion/tank/local-izz-slug_ft2'] contents_ratio = contents / contents0 ixx_ratio = ixx / ixx0 iyy_ratio = iyy / iyy0 izz_ratio = izz / izz0 self.assertAlmostEqual(contents_ratio, ixx_ratio, delta=1E-7, msg="Ixx does not vary as the tank content does\nIxx ratio=%f\nContents ratio=%f" % (ixx_ratio, contents_ratio)) self.assertAlmostEqual(contents_ratio, iyy_ratio, delta=1E-7, msg="Iyy does not vary as the tank content does\nIyy ratio=%f\nContents ratio=%f" % (iyy_ratio, contents_ratio)) self.assertAlmostEqual(contents_ratio, izz_ratio, delta=1E-7, msg="Izz does not vary as the tank content does\nIzz ratio=%f\nContents ratio=%f" % (izz_ratio, contents_ratio))
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 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 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_grain_tanks_content(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'J2460.xml') tree, aircraft_name, b = CopyAircraftDef(script_path, self.sandbox) id = 0 for tank in tree.getroot().findall('propulsion/tank'): grain_config = tank.find('grain_config') if grain_config and grain_config.attrib['type'] == 'CYLINDRICAL': break ++id capacity = float(tank.find('capacity').text) tank.find('contents').text = str(0.5 * capacity) tree.write( self.sandbox('aircraft', aircraft_name, aircraft_name + '.xml')) radius_tag = tank.find('radius') radius = float(radius_tag.text) if 'unit' in radius_tag.attrib and radius_tag.attrib['unit'] == 'IN': radius /= 12.0 bore_diameter_tag = tank.find('grain_config/bore_diameter') bore_radius = 0.5 * float(bore_diameter_tag.text) if 'unit' in bore_diameter_tag.attrib and bore_diameter_tag.attrib[ 'unit'] == 'IN': bore_radius /= 12.0 fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_path) fdm.run_ic() tank_name = 'propulsion/tank[%g]' % (id, ) self.assertAlmostEqual(fdm[tank_name + '/contents-lbs'], 0.5 * capacity) fdm['propulsion/tank/contents-lbs'] = capacity mass = capacity / 32.174049 # Converting lbs to slugs ixx = 0.5 * mass * (radius * radius + bore_radius * bore_radius) self.assertAlmostEqual(fdm[tank_name + 'local-ixx-slug_ft2'], ixx) del fdm tank.find('contents').text = '0.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() self.assertAlmostEqual(fdm[tank_name + '/contents-lbs'], 0.0) fdm['propulsion/tank/contents-lbs'] = capacity
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 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 test_grain_tanks_content(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'J2460.xml') tree, aircraft_name, b = CopyAircraftDef(script_path, self.sandbox) id = 0 for tank in tree.getroot().findall('propulsion/tank'): grain_config = tank.find('grain_config') if grain_config and grain_config.attrib['type'] == 'CYLINDRICAL': break ++id capacity = float(tank.find('capacity').text) tank.find('contents').text = str(0.5*capacity) tree.write(self.sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) radius_tag = tank.find('radius') radius = float(radius_tag.text) if 'unit' in radius_tag.attrib and radius_tag.attrib['unit'] == 'IN': radius /= 12.0 bore_diameter_tag = tank.find('grain_config/bore_diameter') bore_radius = 0.5*float(bore_diameter_tag.text) if 'unit' in bore_diameter_tag.attrib and bore_diameter_tag.attrib['unit'] == 'IN': bore_radius /= 12.0 fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(script_path) fdm.run_ic() tank_name = 'propulsion/tank[%g]' % (id,) self.assertAlmostEqual(fdm[tank_name+'/contents-lbs'], 0.5*capacity) fdm['propulsion/tank/contents-lbs'] = capacity mass = capacity / 32.174049 # Converting lbs to slugs ixx = 0.5 * mass * (radius * radius + bore_radius*bore_radius) self.assertAlmostEqual(fdm[tank_name+'local-ixx-slug_ft2'], ixx) del fdm tank.find('contents').text = '0.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() self.assertAlmostEqual(fdm[tank_name+'/contents-lbs'], 0.0) fdm['propulsion/tank/contents-lbs'] = capacity
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_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 steerType(self, hasSteerPosDeg, hasSteeringAngle, hasCastered): self.tree.write(self.sandbox('aircraft', self.aircraft_name, self.aircraft_name+'.xml')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(self.script_path) fdm.run_ic() pm = fdm.get_property_manager() self.assertTrue(pm.hasNode('fcs/steer-pos-deg') == hasSteerPosDeg) self.assertTrue(pm.hasNode('gear/unit/steering-angle-deg') == hasSteeringAngle) self.assertTrue(pm.hasNode('gear/unit/castered') == hasCastered) return 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 CheckRateLimit(self, incr_limit, decr_limit): fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') self.ScriptExecution(fdm, 1.0) fdm[self.input_prop] = 1.0 self.CheckRateValue(fdm, incr_limit) fdm[self.input_prop] = 0.0 self.CheckRateValue(fdm, 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_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 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['inertia/pointmass-weight-lbs[1]'] = 50.0 aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft', 'ball') fdm.load_ic(os.path.join(aircraft_path, 'reset00.xml'), False) # Switch the accel on fdm['fcs/accelerometer/on'] = 1.0 # Set the orientation such that the spinning axis is Z. fdm['ic/phi-rad'] = 0.5 * math.pi # Set the angular velocities so that angular velocity R_{inertial} will # be equal to 1.0 rad/sec. omega = 0.00007292115 # Earth rotation rate in rad/sec fdm['ic/p-rad_sec'] = 0.0 fdm['ic/q-rad_sec'] = 0.0 fdm['ic/r-rad_sec'] = 1.0 - omega fdm.run_ic() fax = fdm['fcs/accelerometer/X'] fay = fdm['fcs/accelerometer/Y'] faz = fdm['fcs/accelerometer/Z'] cgy_ft = fdm['inertia/cg-y-in'] / 12. self.assertAlmostEqual(fdm['accelerations/a-pilot-x-ft_sec2'], fax, delta=1E-8) self.assertAlmostEqual(fdm['accelerations/a-pilot-y-ft_sec2'], fay, delta=1E-8) self.assertAlmostEqual(fdm['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 d*r_dot^2 self.assertAlmostEqual(fay / cgy_ft, 1.0, delta=1E-7) # Acceleration along Z should be zero self.assertAlmostEqual(faz, 0.0, delta=1E-8)
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_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 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 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 testKinematicNoScale(self): # Test the <nocale/> feature script_path = self.sandbox.path_to_jsbsim_file('scripts', 'c1721.xml') tree, aircraft_name, b = CopyAircraftDef(script_path, self.sandbox) kinematic_tag = tree.getroot().find('flight_control/channel/kinematic') et.SubElement(kinematic_tag, 'noscale') tree.write( self.sandbox('aircraft', aircraft_name, aircraft_name + '.xml')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_model(aircraft_name) fdm.load_ic('reset00', True) fdm.run_ic() fdm['fcs/flap-cmd-norm'] = 12. ExecuteUntil(fdm, 2.2) self.assertAlmostEqual(fdm['fcs/flap-pos-deg'], 12.)
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['inertia/pointmass-weight-lbs[1]'] = 50.0 aircraft_path = self.sandbox.path_to_jsbsim_file('aircraft', 'ball') fdm.load_ic(os.path.join(aircraft_path, 'reset00.xml'), False) # Switch the accel on fdm['fcs/accelerometer/on'] = 1.0 # Set the orientation such that the spinning axis is Z. fdm['ic/phi-rad'] = 0.5*math.pi # Set the angular velocities so that angular velocity R_{inertial} will # be equal to 1.0 rad/sec. omega = 0.00007292115 # Earth rotation rate in rad/sec fdm['ic/p-rad_sec'] = 0.0 fdm['ic/q-rad_sec'] = 0.0 fdm['ic/r-rad_sec'] = 1.0 - omega fdm.run_ic() fax = fdm['fcs/accelerometer/X'] fay = fdm['fcs/accelerometer/Y'] faz = fdm['fcs/accelerometer/Z'] cgy_ft = fdm['inertia/cg-y-in'] / 12. self.assertAlmostEqual(fdm['accelerations/a-pilot-x-ft_sec2'], fax, delta=1E-8) self.assertAlmostEqual(fdm['accelerations/a-pilot-y-ft_sec2'], fay, delta=1E-8) self.assertAlmostEqual(fdm['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 d*r_dot^2 self.assertAlmostEqual(fay / cgy_ft, 1.0, delta=1E-7) # Acceleration along Z should be zero self.assertAlmostEqual(faz, 0.0, delta=1E-8)
def testKinematicNoScale(self): # Test the <nocale/> feature script_path = self.sandbox.path_to_jsbsim_file('scripts', 'c1721.xml') tree, aircraft_name, b = CopyAircraftDef(script_path, self.sandbox) kinematic_tag = tree.getroot().find('flight_control/channel/kinematic') et.SubElement(kinematic_tag, 'noscale') tree.write(self.sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_model(aircraft_name) fdm.load_ic(self.sandbox.path_to_jsbsim_file('aircraft', aircraft_name, 'reset00'), False) fdm.run_ic() fdm['fcs/flap-cmd-norm'] = 12. ExecuteUntil(fdm, 2.2) self.assertAlmostEqual(fdm['fcs/flap-pos-deg'], 12.)
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_alt_mod_vs_CAS(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, b = CopyAircraftDef(script_path, self.sandbox) self.addPitotTube(tree.getroot(), 10.0) tree.write(self.sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_model('Short_S23') fdm['ic/beta-deg'] = 15.0 # Add some sideslip fdm['ic/vc-kts'] = 172.0 fdm['ic/h-sl-ft'] = 15000. self.assertAlmostEqual(fdm['ic/vc-kts'], 172.0, delta=1E-7) self.assertAlmostEqual(fdm['ic/beta-deg'], 15.0, delta=1E-7) fdm.run_ic() self.assertAlmostEqual(fdm['velocities/vc-kts'], 172.0, delta=1E-7) self.assertAlmostEqual(fdm['aero/beta-deg'], 15.0, delta=1E-7)
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 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(aircraft_path, 'c172x') fdm.load_ic(os.path.join(aircraft_path, 'reset01.xml'), False) fdm.run_ic() self.assertEqual(fdm['simulation/sim-time-sec'], 0.0) ExecuteUntil(fdm, 5.0) t = fdm['simulation/sim-time-sec'] fdm['simulation/do_simple_trim'] = 1 self.assertEqual(fdm['simulation/sim-time-sec'], t) fdm.reset_to_initial_conditions(1) self.assertEqual(fdm['simulation/sim-time-sec'], 0.0) del fdm
def test_regression_bug_1503(self): # First, the execution time of the script c1724.xml is measured. It # will be used as a reference to check if JSBSim hangs or not. fdm = CreateFDM(self.sandbox) start_time = time.time() self.ScriptExecution(fdm) exec_time = time.time() - start_time del fdm # Now the copy of the aircraft definition file will be altered: the # <rate_limit> element is split in two: one with the 'decr' sense, the # other with 'incr' sense. actuator_element = self.tree.getroot().find( 'flight_control/channel/actuator//rate_limit/..') rate_element = actuator_element.find('rate_limit') rate_element.attrib['sense'] = 'decr' new_rate_element = et.SubElement(actuator_element, 'rate_limit') new_rate_element.attrib['sense'] = 'incr' new_rate_element.text = str(float(rate_element.text) * 0.5) self.tree.write( self.sandbox('aircraft', self.aircraft_name, self.aircraft_name + '.xml')) # Run the script with the modified aircraft fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') # A new process is created that launches the script. We wait for 10 # times the reference execution time for the script completion. Beyond # that time, if the process is not completed, it is terminated and the # test is failed. p = Process(target=self.ScriptExecution, args=(fdm, )) p.start() p.join(exec_time * 10.0) # Wait 10 times the reference time alive = p.is_alive() if alive: p.terminate() self.assertFalse(alive, msg="The script has hanged")
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_regression_bug_1503(self): # First, the execution time of the script c1724.xml is measured. It # will be used as a reference to check if JSBSim hangs or not. fdm = CreateFDM(self.sandbox) start_time = time.time() self.ScriptExecution(fdm) exec_time = time.time() - start_time del fdm # Now the copy of the aircraft definition file will be altered: the # <rate_limit> element is split in two: one with the 'decr' sense, the # other with 'incr' sense. actuator_element = self.tree.getroot().find('flight_control/channel/actuator//rate_limit/..') rate_element = actuator_element.find('rate_limit') rate_element.attrib['sense'] = 'decr' new_rate_element = et.SubElement(actuator_element, 'rate_limit') new_rate_element.attrib['sense'] = 'incr' new_rate_element.text = str(float(rate_element.text) * 0.5) self.tree.write(self.sandbox('aircraft', self.aircraft_name, self.aircraft_name+'.xml')) # Run the script with the modified aircraft fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') # A new process is created that launches the script. We wait for 10 # times the reference execution time for the script completion. Beyond # that time, if the process is not completed, it is terminated and the # test is failed. p = Process(target=self.ScriptExecution, args=(fdm,)) p.start() p.join(exec_time * 10.0) # Wait 10 times the reference time alive = p.is_alive() if alive: p.terminate() self.assertFalse(alive, msg="The script has hanged")
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
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')
def test_point_mass_inertia(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'J2460.xml') fdm = CreateFDM(self.sandbox) fdm.set_output_directive(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.load_script(script_path) fdm.run_ic() ExecuteUntil(fdm, 50.0) ref = Table() ref.ReadCSV(self.sandbox("output.csv")) tree, aircraft_name, path_to_jsbsim_aircrafts = CopyAircraftDef(script_path, self.sandbox) pointmass_element = tree.getroot().find('mass_balance/pointmass//form/..') weight_element = pointmass_element.find('weight') weight = float(weight_element.text) form_element = pointmass_element.find('form') radius_element = form_element.find('radius') radius, length = (0.0, 0.0) if radius_element is not None: radius = float(radius_element.text) length_element = form_element.find('length') if length_element is not None: length = float(length_element.text) shape = form_element.attrib['shape'] pointmass_element.remove(form_element) inertia = numpy.zeros((3,3)) if string.strip(shape) == 'tube': inertia[0,0] = radius * radius inertia[1,1] = (6.0 * inertia[0,0] + length * length) / 12.0 inertia[2,2] = inertia[1,1] inertia = inertia * weight / 32.174049 # conversion between slug and lb ixx_element = et.SubElement(pointmass_element, 'ixx') ixx_element.text = str(inertia[0,0]) iyy_element = et.SubElement(pointmass_element, 'iyy') iyy_element.text = str(inertia[1,1]) izz_element = et.SubElement(pointmass_element, 'izz') izz_element.text = str(inertia[2,2]) tree.write(self.sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.set_output_directive(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.load_script(script_path) fdm.run_ic() ExecuteUntil(fdm, 50.0) mod = Table() mod.ReadCSV(self.sandbox("output.csv")) diff = ref.compare(mod) self.assertTrue(diff.empty(), msg='\n'+repr(diff))
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')
tree, aircraft_name, path_to_jsbsim_aircrafts = CopyAircraftDef(script_path, sandbox) # Now the copy of the aircraft definition file will be altered: the <rate_limit> # element is split in two: one with sense 'decr', the other with sense 'incr'. actuator_element = tree.getroot().find('flight_control/channel/actuator//rate_limit/..') rate_element = actuator_element.find('rate_limit') rate_element.attrib['sense'] = 'decr' new_rate_element = et.SubElement(actuator_element, 'rate_limit') new_rate_element.attrib['sense'] = 'incr' new_rate_element.text = str(float(rate_element.text) * 0.5) tree.write(sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) # Run the script with the modified aircraft fdm = CreateFDM(sandbox) fdm.set_aircraft_path('aircraft') # A new process is created that launches the script. We wait for 10 times the # reference execution time for the script completion. Beyond that time, if the # process is not completed, it is terminated and the test is failed. p = Process(target=ScriptExecution, args=(fdm, script_path)) p.start() p.join(exec_time * 10.0) # Wait 10 times the reference time if p.is_alive(): # The process has not yet completed: it means it probably hanged. p.terminate() sys.exit(-1) # Second part of the test. # ####################### #
def test_clipto(self): tree, flight_control_element, actuator_element = self.prepare_actuator( ) rate_limit = float(actuator_element.find('rate_limit').text) self.saturated_prop = actuator_element.attrib['name'] + "/saturated" self.fail_hardover = actuator_element.attrib[ 'name'] + "/malfunction/fail_hardover" clipto = actuator_element.find('clipto') clipmax = clipto.find('max') clipmin = clipto.find('min') output_file = os.path.join('aircraft', self.aircraft_name, self.aircraft_name + '.xml') tree.write(output_file) self.CheckClip(float(clipmin.text), float(clipmax.text), rate_limit) property = et.SubElement(flight_control_element, 'property') property.text = 'fcs/clip-min-value' property.attrib['value'] = '-0.15' property = et.SubElement(flight_control_element, 'property') property.text = 'fcs/clip-max-value' property.attrib['value'] = '0.05' # Check a property for min and a value for max clipmin.text = 'fcs/clip-min-value' tree.write(output_file) self.CheckClip(-0.15, float(clipmax.text), rate_limit) # Check a property with minus sign for min and a value for max clipmin.text = '-fcs/clip-max-value' tree.write(output_file) self.CheckClip(-0.05, float(clipmax.text), rate_limit) # Check a property for max and a value for min clipmin.text = '-0.1' clipmax.text = 'fcs/clip-max-value' tree.write(output_file) self.CheckClip(-0.1, 0.05, rate_limit) # Check a property with minus sign for max and a value for min clipmax.text = '-fcs/clip-min-value' tree.write(output_file) self.CheckClip(-0.1, 0.15, rate_limit) # Check a property for max and min clipmin.text = '-fcs/clip-max-value' clipmax.text = 'fcs/clip-max-value' tree.write(output_file) self.CheckClip(-0.05, 0.05, rate_limit) # Check the cyclic clip clipmin.text = str(-math.pi) clipmax.text = str(math.pi) clipto.attrib['type'] = 'cyclic' tree.write(output_file) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(self.script_path) fdm.run_ic() fdm[self.input_prop] = 2.0 * math.pi dt = math.pi / rate_limit while fdm['simulation/sim-time-sec'] <= dt: self.assertTrue(fdm[self.output_prop] <= math.pi) self.assertTrue(fdm[self.output_prop] >= 0.0) self.assertAlmostEqual(fdm[self.output_prop], fdm['simulation/sim-time-sec'] * rate_limit) fdm.run() while fdm['simulation/sim-time-sec'] <= 2.0 * dt: self.assertTrue(fdm[self.output_prop] >= -math.pi) self.assertTrue(fdm[self.output_prop] <= 0.0) self.assertAlmostEqual( fdm[self.output_prop], fdm['simulation/sim-time-sec'] * rate_limit - 2.0 * math.pi) fdm.run() # Check that the output value does not go beyond 0.0 self.assertAlmostEqual(fdm[self.output_prop], 0.0) fdm.run() self.assertAlmostEqual(fdm[self.output_prop], 0.0) t = fdm['simulation/sim-time-sec'] fdm[self.input_prop] = -0.5 * math.pi while fdm['simulation/sim-time-sec'] <= t + dt: self.assertTrue(fdm[self.output_prop] >= -math.pi) self.assertTrue(fdm[self.output_prop] <= 0.0) self.assertAlmostEqual(fdm[self.output_prop], (t - fdm['simulation/sim-time-sec']) * rate_limit) fdm.run() while fdm['simulation/sim-time-sec'] <= t + 1.5 * dt: self.assertTrue(fdm[self.output_prop] <= math.pi) self.assertTrue(fdm[self.output_prop] >= -0.5 * math.pi) self.assertAlmostEqual( fdm[self.output_prop], (t - fdm['simulation/sim-time-sec']) * rate_limit + 2.0 * math.pi) fdm.run() del fdm # Check the cyclic clip handles correctly negative numbers (GH issue # #211) # Case 1 : The interval is positive clipmin.text = '0.0' clipmax.text = str(math.pi) tree.write(output_file) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(self.script_path) fdm.run_ic() fdm[self.input_prop] = -2.0 * math.pi t0 = math.pi / rate_limit t = fdm['simulation/sim-time-sec'] while t <= t0: self.assertTrue(fdm[self.output_prop] <= math.pi) self.assertTrue(fdm[self.output_prop] >= 0.0) if t == 0: self.assertAlmostEqual(fdm[self.output_prop], 0.0) else: self.assertAlmostEqual(fdm[self.output_prop], math.pi - t * rate_limit) fdm.run() t = fdm['simulation/sim-time-sec'] while t <= 2.0 * t0: self.assertTrue(fdm[self.output_prop] <= math.pi) self.assertTrue(fdm[self.output_prop] >= 0.0) self.assertAlmostEqual(fdm[self.output_prop], math.pi - (t - t0) * rate_limit) fdm.run() t = fdm['simulation/sim-time-sec'] del fdm # Case 2 : The interval is negative clipmin.text = str(-math.pi) clipmax.text = '0.0' tree.write(output_file) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(self.script_path) fdm.run_ic() fdm[self.input_prop] = math.pi dt = math.pi / rate_limit t = fdm['simulation/sim-time-sec'] while t <= dt: self.assertAlmostEqual(fdm[self.output_prop], t * rate_limit - math.pi) self.assertTrue(fdm[self.output_prop] >= -math.pi - 1E-8) self.assertTrue(fdm[self.output_prop] <= 0.0) fdm.run() t = fdm['simulation/sim-time-sec'] t0 = t fdm[self.input_prop] = -2.0 * math.pi fdm.run() t = fdm['simulation/sim-time-sec'] while t <= t0 + dt: self.assertTrue(fdm[self.output_prop] >= -math.pi) self.assertTrue(fdm[self.output_prop] <= 0.0) self.assertAlmostEqual(fdm[self.output_prop], (t0 - t) * rate_limit) fdm.run() t = fdm['simulation/sim-time-sec'] t0 += dt while t <= t0 + dt: self.assertTrue(fdm[self.output_prop] >= -math.pi) self.assertTrue(fdm[self.output_prop] <= 0.0) self.assertAlmostEqual(fdm[self.output_prop], (t0 - t) * rate_limit) fdm.run() t = fdm['simulation/sim-time-sec']
def CheckClip(self, clipmin, clipmax, rate_limit): fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(self.script_path) fdm.run_ic() fdm[self.input_prop] = 2.0 * clipmax dt = clipmax / rate_limit while fdm['simulation/sim-time-sec'] <= dt: self.assertFalse(fdm[self.saturated_prop]) fdm.run() self.assertTrue(fdm[self.saturated_prop]) self.assertAlmostEqual(fdm[self.output_prop], clipmax) # Check that the actuator output can't go beyond clipmax t = fdm['simulation/sim-time-sec'] while fdm['simulation/sim-time-sec'] <= t + dt: fdm.run() self.assertTrue(fdm[self.saturated_prop]) self.assertAlmostEqual(fdm[self.output_prop], clipmax) fdm[self.input_prop] = 2.0 * clipmin dt = (2.0 * clipmax - clipmin) / rate_limit t = fdm['simulation/sim-time-sec'] while fdm['simulation/sim-time-sec'] <= t + dt: if fdm[self.output_prop] < clipmax: self.assertFalse(fdm[self.saturated_prop]) else: self.assertTrue(fdm[self.saturated_prop]) fdm.run() self.assertTrue(fdm[self.saturated_prop]) self.assertAlmostEqual(fdm[self.output_prop], clipmin) # Check that the actuator output can't go below clipmin t = fdm['simulation/sim-time-sec'] while fdm['simulation/sim-time-sec'] <= t + dt: fdm.run() self.assertTrue(fdm[self.saturated_prop]) self.assertAlmostEqual(fdm[self.output_prop], clipmin) fdm[self.input_prop] = 1E-6 dt = (fdm[self.input_prop] - 2.0 * clipmin) / rate_limit t = fdm['simulation/sim-time-sec'] while fdm['simulation/sim-time-sec'] <= t + 2.0 * dt: if fdm[self.output_prop] > clipmin: self.assertFalse(fdm[self.saturated_prop]) else: self.assertTrue(fdm[self.saturated_prop]) fdm.run() self.assertAlmostEqual(fdm[self.output_prop], fdm[self.input_prop]) fdm[self.fail_hardover] = 1.0 dt = (clipmax - fdm[self.input_prop]) / rate_limit t = fdm['simulation/sim-time-sec'] while fdm['simulation/sim-time-sec'] <= t + dt: if fdm[self.output_prop] < clipmax: self.assertFalse(fdm[self.saturated_prop]) else: self.assertTrue(fdm[self.saturated_prop]) fdm.run() self.assertTrue(fdm[self.saturated_prop]) self.assertAlmostEqual(fdm[self.output_prop], clipmax) fdm[self.input_prop] = -1E-6 dt = (clipmax - clipmin) / rate_limit t = fdm['simulation/sim-time-sec'] while fdm['simulation/sim-time-sec'] <= t + dt: if fdm[self.output_prop] > clipmin and fdm[ self.output_prop] < clipmax: self.assertFalse(fdm[self.saturated_prop]) else: self.assertTrue(fdm[self.saturated_prop]) fdm.run() self.assertTrue(fdm[self.saturated_prop]) self.assertAlmostEqual(fdm[self.output_prop], clipmin) del fdm
def test_point_mass_inertia(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'J2460.xml') fdm = CreateFDM(self.sandbox) fdm.set_output_directive( self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.load_script(script_path) fdm.run_ic() ExecuteUntil(fdm, 50.0) ref = pd.read_csv("output.csv", index_col=0) tree, aircraft_name, path_to_jsbsim_aircrafts = CopyAircraftDef( script_path, self.sandbox) pointmass_element = tree.getroot().find( 'mass_balance/pointmass//form/..') weight_element = pointmass_element.find('weight') weight = float(weight_element.text) form_element = pointmass_element.find('form') radius_element = form_element.find('radius') radius, length = (0.0, 0.0) if radius_element is not None: radius = float(radius_element.text) length_element = form_element.find('length') if length_element is not None: length = float(length_element.text) shape = form_element.attrib['shape'] pointmass_element.remove(form_element) inertia = np.zeros((3, 3)) if string.strip(shape) == 'tube': inertia[0, 0] = radius * radius inertia[1, 1] = (6.0 * inertia[0, 0] + length * length) / 12.0 inertia[2, 2] = inertia[1, 1] inertia = inertia * weight / 32.174049 # converting slugs to lbs ixx_element = et.SubElement(pointmass_element, 'ixx') ixx_element.text = str(inertia[0, 0]) iyy_element = et.SubElement(pointmass_element, 'iyy') iyy_element.text = str(inertia[1, 1]) izz_element = et.SubElement(pointmass_element, 'izz') izz_element.text = str(inertia[2, 2]) tree.write( self.sandbox('aircraft', aircraft_name, aircraft_name + '.xml')) # 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 fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.set_output_directive( self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.load_script(script_path) 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(ref, mod)) # Find all the data that are differing by more than 1E-8 between the # two data sets. diff = FindDifferences(ref, mod, 1E-8) self.longMessage = True self.assertEqual(len(diff), 0, msg='\n' + diff.to_string())
def test_input_socket(self): # The aircraft c172x does not contain an <input> tag so we need # to add one. tree, aircraft_name, b = CopyAircraftDef(self.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')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(self.script_path) fdm.run_ic() fdm.hold() tn = TelnetInterface(fdm, 5., 1137) self.sanityCheck(tn) # Check the aircraft name and its version msg = string.split(tn.sendCommand("info"), '\n') self.assertEqual(string.strip(string.split(msg[2], ':')[1]), string.strip(self.root.attrib['name'])) self.assertEqual(string.strip(string.split(msg[1], ':')[1]), string.strip(self.root.attrib['version'])) # Check that the simulation time is 0.0 self.assertEqual(float(string.strip(string.split(msg[3], ':')[1])), 0.0) self.assertEqual(tn.getSimTime(), 0.0) self.assertEqual(tn.getPropertyValue("simulation/sim-time-sec"), 0.0) # Check that 'iterate' iterates the correct number of times tn.sendCommand("iterate 19") self.assertEqual(tn.getSimTime(), 19. * tn.getDeltaT()) self.assertAlmostEqual(tn.getPropertyValue("simulation/sim-time-sec"), tn.getSimTime(), delta=1E-5) # Wait a little bit and make sure that the simulation time has not # changed meanwhile thus confirming that the simulation is on hold. tn.wait(0.1) self.assertEqual(tn.getSimTime(), 19. * tn.getDeltaT()) self.assertAlmostEqual(tn.getPropertyValue("simulation/sim-time-sec"), tn.getSimTime(), delta=1E-5) # Modify the tank[0] contents via the "send" command half_contents = 0.5 * tn.getPropertyValue( "propulsion/tank/contents-lbs") tn.sendCommand("set propulsion/tank/contents-lbs " + str(half_contents)) self.assertEqual(tn.getPropertyValue("propulsion/tank/contents-lbs"), half_contents) # Check the resume/hold commands tn.setRealTime(True) t = tn.getSimTime() tn.sendCommand("resume") tn.wait(0.5) self.assertNotEqual(tn.getSimTime(), t) tn.wait(0.5) tn.sendCommand("hold") tn.setRealTime(False) t = tn.getSimTime() self.assertAlmostEqual(tn.getPropertyValue("simulation/sim-time-sec"), t, delta=1E-5) # Wait a little bit and make sure that the simulation time has not # changed meanwhile thus confirming that the simulation is on hold. tn.wait(0.1) self.assertEqual(tn.getSimTime(), t) self.assertAlmostEqual(tn.getPropertyValue("simulation/sim-time-sec"), t, delta=1E-5)
class TestInputSocket(unittest.TestCase): 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 tearDown(self): self.tn.close() self.thread.quit = True self.thread.join() self.sandbox.erase() def sendCommand(self, command): self.cond.acquire() self.tn.write(command+"\n") # Wait for a time step to be executed before reading the output from telnet self.cond.wait() msg = self.tn.read_very_eager() self.cond.release() self.thread.join(0.1) return msg def getSimTime(self): self.cond.acquire() self.cond.wait() t = self.fdm.get_sim_time() self.cond.release() return t def getDeltaT(self): self.cond.acquire() self.cond.wait() dt = self.fdm.get_delta_t() self.cond.release() return dt def getPropertyValue(self, property): msg = string.split(self.sendCommand("get "+property),'\n') return float(string.split(msg[0], '=')[1]) def test_input_socket(self): # Check that the connection has been established self.cond.acquire() self.cond.wait() out = self.tn.read_very_eager() self.cond.release() self.assertTrue(string.split(out, '\n')[0] == 'Connected to JSBSim server', msg="Not connected to the JSBSim server.\nGot message '%s' instead" % (out,)) # Check that "help" returns the minimum set of commands that will be # tested self.assertEqual(sorted(map(lambda x : string.strip(string.split(x, '{')[0]), string.split(self.sendCommand("help"), '\n')[2:-2])), ['get', 'help', 'hold', 'info', 'iterate', 'quit', 'resume', 'set']) # Check the aircraft name and its version msg = string.split(self.sendCommand("info"), '\n') self.assertEqual(string.strip(string.split(msg[2], ':')[1]), string.strip(self.root.attrib['name'])) self.assertEqual(string.strip(string.split(msg[1], ':')[1]), string.strip(self.root.attrib['version'])) # Check that the simulation time is 0.0 self.assertEqual(float(string.strip(string.split(msg[3], ':')[1])), 0.0) self.assertEqual(self.getSimTime(), 0.0) self.assertEqual(self.getPropertyValue("simulation/sim-time-sec"), 0.0) # Check that 'iterate' iterates the correct number of times self.sendCommand("iterate 19") self.assertEqual(self.getSimTime(), 19. * self.getDeltaT()) self.assertAlmostEqual(self.getPropertyValue("simulation/sim-time-sec"), self.getSimTime(), delta=1E-5) # Wait a little bit and make sure that the simulation time has not # changed meanwhile thus confirming that the simulation is on hold. self.thread.join(0.1) self.assertEqual(self.getSimTime(), 19. * self.getDeltaT()) self.assertAlmostEqual(self.getPropertyValue("simulation/sim-time-sec"), self.getSimTime(), delta=1E-5) # Modify the tank[0] contents via the "send" command half_contents = 0.5 * self.getPropertyValue("propulsion/tank/contents-lbs") self.sendCommand("set propulsion/tank/contents-lbs "+ str(half_contents)) self.cond.acquire() self.cond.wait() self.assertEqual(self.fdm.get_property_value("propulsion/tank/contents-lbs"), half_contents) self.cond.release() # Check the resume/hold commands self.thread.realTime = True t = self.getSimTime() self.sendCommand("resume") self.thread.join(0.5) self.assertNotEqual(self.getSimTime(), t) self.thread.join(0.5) self.sendCommand("hold") self.thread.realTime = False t = self.getSimTime() self.assertAlmostEqual(self.getPropertyValue("simulation/sim-time-sec"), t, delta=1E-5) # Wait a little bit and make sure that the simulation time has not # changed meanwhile thus confirming that the simulation is on hold. self.thread.join(0.1) self.assertEqual(self.getSimTime(), t) self.assertAlmostEqual(self.getPropertyValue("simulation/sim-time-sec"), t, delta=1E-5)
def test_input_socket(self): # The aircraft c172x does not contain an <input> tag so we need # to add one. tree, aircraft_name, b = CopyAircraftDef(self.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')) fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.load_script(self.script_path) fdm.run_ic() fdm.hold() tn = TelnetInterface(fdm, 5., 1137) self.sanityCheck(tn) # Check the aircraft name and its version msg = string.split(tn.sendCommand("info"), '\n') self.assertEqual(string.strip(string.split(msg[2], ':')[1]), string.strip(self.root.attrib['name'])) self.assertEqual(string.strip(string.split(msg[1], ':')[1]), string.strip(self.root.attrib['version'])) # Check that the simulation time is 0.0 self.assertEqual(float(string.strip(string.split(msg[3], ':')[1])), 0.0) self.assertEqual(tn.getSimTime(), 0.0) self.assertEqual(tn.getPropertyValue("simulation/sim-time-sec"), 0.0) # Check that 'iterate' iterates the correct number of times tn.sendCommand("iterate 19") self.assertEqual(tn.getSimTime(), 19. * tn.getDeltaT()) self.assertAlmostEqual(tn.getPropertyValue("simulation/sim-time-sec"), tn.getSimTime(), delta=1E-5) # Wait a little bit and make sure that the simulation time has not # changed meanwhile thus confirming that the simulation is on hold. tn.wait(0.1) self.assertEqual(tn.getSimTime(), 19. * tn.getDeltaT()) self.assertAlmostEqual(tn.getPropertyValue("simulation/sim-time-sec"), tn.getSimTime(), delta=1E-5) # Modify the tank[0] contents via the "send" command half_contents = 0.5 * tn.getPropertyValue("propulsion/tank/contents-lbs") tn.sendCommand("set propulsion/tank/contents-lbs " + str(half_contents)) self.assertEqual(tn.getPropertyValue("propulsion/tank/contents-lbs"), half_contents) # Check the resume/hold commands tn.setRealTime(True) t = tn.getSimTime() tn.sendCommand("resume") tn.wait(0.5) self.assertNotEqual(tn.getSimTime(), t) tn.wait(0.5) tn.sendCommand("hold") tn.setRealTime(False) t = tn.getSimTime() self.assertAlmostEqual(tn.getPropertyValue("simulation/sim-time-sec"), t, delta=1E-5) # Wait a little bit and make sure that the simulation time has not # changed meanwhile thus confirming that the simulation is on hold. tn.wait(0.1) self.assertEqual(tn.getSimTime(), t) self.assertAlmostEqual(tn.getPropertyValue("simulation/sim-time-sec"), t, delta=1E-5)
def test_point_mass_inertia(self): script_path = self.sandbox.path_to_jsbsim_file('scripts', 'J2460.xml') fdm = CreateFDM(self.sandbox) fdm.set_output_directive(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.load_script(script_path) fdm.run_ic() ExecuteUntil(fdm, 50.0) ref = pd.read_csv("output.csv", index_col=0) tree, aircraft_name, path_to_jsbsim_aircrafts = CopyAircraftDef(script_path, self.sandbox) pointmass_element = tree.getroot().find('mass_balance/pointmass//form/..') weight_element = pointmass_element.find('weight') weight = float(weight_element.text) form_element = pointmass_element.find('form') radius_element = form_element.find('radius') radius, length = (0.0, 0.0) if radius_element is not None: radius = float(radius_element.text) length_element = form_element.find('length') if length_element is not None: length = float(length_element.text) shape = form_element.attrib['shape'] pointmass_element.remove(form_element) inertia = np.zeros((3, 3)) if string.strip(shape) == 'tube': inertia[0, 0] = radius * radius inertia[1, 1] = (6.0 * inertia[0, 0] + length * length) / 12.0 inertia[2, 2] = inertia[1, 1] inertia = inertia * weight / 32.174049 # converting slugs to lbs ixx_element = et.SubElement(pointmass_element, 'ixx') ixx_element.text = str(inertia[0, 0]) iyy_element = et.SubElement(pointmass_element, 'iyy') iyy_element.text = str(inertia[1, 1]) izz_element = et.SubElement(pointmass_element, 'izz') izz_element.text = str(inertia[2, 2]) tree.write(self.sandbox('aircraft', aircraft_name, aircraft_name+'.xml')) # 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 fdm = CreateFDM(self.sandbox) fdm.set_aircraft_path('aircraft') fdm.set_output_directive(self.sandbox.path_to_jsbsim_file('tests', 'output.xml')) fdm.load_script(script_path) 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(ref, mod)) # Find all the data that are differing by more than 1E-8 between the # two data sets. diff = FindDifferences(ref, mod, 1E-8) self.longMessage = True self.assertEqual(len(diff), 0, msg='\n'+diff.to_string())