579
edits
m (cat)  | 
				 (→How to install the current system on an aircraft:  version 0.11)  | 
				||
| Line 10: | Line 10: | ||
== How to install the current system on an aircraft ==  | == How to install the current system on an aircraft ==  | ||
Copy this code into your aircrafts Nasal folder as   | Copy this code into your aircrafts Nasal folder as crash-and-stress.nas file:  | ||
<syntaxhighlight lang="nasal">  | <syntaxhighlight lang="nasal">  | ||
#  | #  | ||
| Line 20: | Line 20: | ||
#  | #  | ||
#  | #  | ||
# Version 0.  | # Version 0.11  | ||
#  | #  | ||
# License:  | # License:  | ||
| Line 29: | Line 29: | ||
var FALSE = 0;  | var FALSE = 0;  | ||
var   | var CrashAndStress = {  | ||
	# pattern singleton  | 	# pattern singleton  | ||
	_instance: nil,  | 	_instance: nil,  | ||
| Line 38: | Line 38: | ||
		if(me._instance == nil) {  | 		if(me._instance == nil) {  | ||
			me._instance = {};  | 			me._instance = {};  | ||
			me._instance["parents"] = [  | 			me._instance["parents"] = [CrashAndStress];  | ||
			m = me._instance;  | 			m = me._instance;  | ||
| Line 48: | Line 48: | ||
			m.wingsAttached = TRUE;  | 			m.wingsAttached = TRUE;  | ||
			m.wingLoadLimitUpper = nil;  | 			m.wingLoadLimitUpper = nil;  | ||
			m.wingLoadLimitLower = nil;  | 			m.wingLoadLimitLower = nil;  | ||
			m.  | 			m._looptimer = maketimer(0, m, m._loop);  | ||
			m.input = {  | 			m.input = {  | ||
			#	trembleOn:  "damage/g-tremble-on",  | |||
			#	trembleMax: "damage/g-tremble-max",				  | |||
				replay:     "sim/replay/replay-state",  | 				replay:     "sim/replay/replay-state",  | ||
				lat:        "position/latitude-deg",  | 				lat:        "position/latitude-deg",  | ||
				lon:        "position/longitude-deg",  | 				lon:        "position/longitude-deg",  | ||
| Line 61: | Line 61: | ||
				altAgl:     "position/altitude-agl-ft",  | 				altAgl:     "position/altitude-agl-ft",  | ||
				elev:       "position/ground-elev-ft",  | 				elev:       "position/ground-elev-ft",  | ||
	  			crackOn:    "damage/sounds/crack-on",  | 	  			crackOn:    "damage/sounds/crack-on",  | ||
				creakOn:    "damage/sounds/creaking-on",  | 				creakOn:    "damage/sounds/creaking-on",  | ||
				crackVol:   "damage/sounds/crack-volume",  | 				crackVol:   "damage/sounds/crack-volume",  | ||
				creakVol:   "damage/sounds/creaking-volume",  | 				creakVol:   "damage/sounds/creaking-volume",  | ||
				wCrashOn:   "damage/sounds/water-crash-on",  | 				wCrashOn:   "damage/sounds/water-crash-on",  | ||
				crashOn:    "damage/sounds/crash-on",  | 				crashOn:    "damage/sounds/crash-on",  | ||
				detachOn:   "damage/sounds/detach-on",  | 				detachOn:   "damage/sounds/detach-on",  | ||
				explodeOn:  "damage/sounds/explode-on",  | 				explodeOn:  "damage/sounds/explode-on",  | ||
			};  | 			};  | ||
			foreach(var ident; keys(m.input)) {  | 			foreach(var ident; keys(m.input)) {  | ||
| Line 82: | Line 74: | ||
			}  | 			}  | ||
			m.fdm = nil;  | |||
			if(getprop("sim/flight-model") == "jsb") {  | |||
				m.fdm = jsbSimProp;  | |||
			} elsif(getprop("sim/flight-model") == "yasim") {  | |||
				m.fdm = yaSimProp;  | |||
			} else {  | |||
				return nil;  | |||
			}  | |||
			m.fdm.convert();  | |||
			m.wowStructure = [];  | 			m.wowStructure = [];  | ||
			m.wowGear = [];  | 			m.wowGear = [];  | ||
| Line 123: | Line 126: | ||
		me.lastMessageTime = 0;  | 		me.lastMessageTime = 0;  | ||
		me.repairing = TRUE;  | 		me.repairing = TRUE;  | ||
		var timer = maketimer(10, me, me._finishRepair);  | |||
		timer.start();  | |||
	},  | 	},  | ||
	# accepts a vector with failure mode IDs, they will fail when wings break off.  | 	# accepts a vector with failure mode IDs, they will fail when wings break off.  | ||
	setWingsFailureModes: func (modes) {  | 	setWingsFailureModes: func (modes) {  | ||
		me.  | 		if(modes == nil) {  | ||
			modes = [];  | |||
		}  | |||
		##  | |||
	    # Returns an actuator object that will set the serviceable property at  | |||
	    # the given node to zero when the level of failure is > 0.  | |||
	    # it will also fail additionally failure modes.  | |||
	    var set_unserviceable_cascading = func(path, casc_paths) {  | |||
	        var prop = path ~ "/serviceable";  | |||
	        if (props.globals.getNode(prop) == nil) {  | |||
	            props.globals.initNode(prop, TRUE, "BOOL");  | |||
	        } else {  | |||
	        	props.globals.getNode(prop).setValue(TRUE);#in case this gets initialized empty from a recorder signal or MP alias.  | |||
	        }  | |||
	        return {  | |||
	            parents: [FailureMgr.FailureActuator],  | |||
	            mode_paths: casc_paths,  | |||
	            set_failure_level: func(level) {  | |||
	                setprop(prop, level > 0 ? 0 : 1);  | |||
	                foreach(var mode_path ; me.mode_paths) {  | |||
	                    FailureMgr.set_failure_level(mode_path, level);  | |||
	                }  | |||
	            },  | |||
	            get_failure_level: func { getprop(prop) ? 0 : 1 }  | |||
	        }  | |||
	    }  | |||
	    var prop = me.fdm.wingsFailureID;  | |||
	    var actuator_wings = set_unserviceable_cascading(prop, modes);  | |||
	    FailureMgr.add_failure_mode(prop, "Main wings", actuator_wings);  | |||
	},  | 	},  | ||
	# set the stresslimit for the main wings  | 	# set the stresslimit for the main wings  | ||
	setStressLimit: func (stressLimit = nil) {  | 	setStressLimit: func (stressLimit = nil) {  | ||
		if (stressLimit != nil) {  | 		if (stressLimit != nil) {  | ||
			var wingloadMax = stressLimit['  | 			var wingloadMax = stressLimit['wingloadMaxLbs'];  | ||
			var wingloadMin = stressLimit['  | 			var wingloadMin = stressLimit['wingloadMinLbs'];  | ||
			var maxG = stressLimit['maxG'];  | 			var maxG = stressLimit['maxG'];  | ||
			var minG = stressLimit['minG'];  | 			var minG = stressLimit['minG'];  | ||
			var weight = stressLimit['  | 			var weight = stressLimit['weightLbs'];  | ||
			if(wingloadMax != nil) {  | 			if(wingloadMax != nil) {  | ||
				me.wingLoadLimitUpper = wingloadMax;  | 				me.wingLoadLimitUpper = wingloadMax;  | ||
| Line 150: | Line 188: | ||
				me.wingLoadLimitLower = -me.wingLoadLimitUpper * 0.4;#estimate for when lower is not specified  | 				me.wingLoadLimitLower = -me.wingLoadLimitUpper * 0.4;#estimate for when lower is not specified  | ||
			}  | 			}  | ||
			me.  | 			me._looptimer.start();  | ||
		} else {  | 		} else {  | ||
			me.  | 			me._looptimer.stop();  | ||
		}  | 		}  | ||
	},  | 	},  | ||
| Line 198: | Line 235: | ||
	},  | 	},  | ||
	_startImpactListeners: func () {  | 	_startImpactListeners: func () {  | ||
		ImpactStructureListener.crash = me;  | |||
		foreach(var structure; me.wowStructure) {  | 		foreach(var structure; me.wowStructure) {  | ||
			setlistener(structure, func {call(  | 			setlistener(structure, func {call(ImpactStructureListener.run, nil, ImpactStructureListener, ImpactStructureListener)},0,0);  | ||
		}  | 		}  | ||
	},  | 	},  | ||
	_isRunning: func () {  | |||
		if (me.inService == FALSE or me.input.replay.getBoolValue() == TRUE or me.repairing == TRUE) {  | |||
			return FALSE;  | |||
		if (me.inService ==   | |||
		}  | 		}  | ||
		var time = me.fdm.input.simTime.getValue();  | |||
		var time = me.input.simTime.getValue();  | |||
		if (time != nil and time > 1) {  | 		if (time != nil and time > 1) {  | ||
			return TRUE;  | 			return TRUE;  | ||
| Line 223: | Line 251: | ||
	},  | 	},  | ||
	_calcGroundSpeed: func () {  | 	_calcGroundSpeed: func () {  | ||
		var horzSpeed = me.input.vgFps.getValue();  | 		var horzSpeed = me.fdm.input.vgFps.getValue();  | ||
   		var vertSpeed = me.input.downFps.getValue();  |    		var vertSpeed = me.fdm.input.downFps.getValue();  | ||
   		var realSpeed = math.sqrt((horzSpeed * horzSpeed) + (vertSpeed * vertSpeed));  |    		var realSpeed = math.sqrt((horzSpeed * horzSpeed) + (vertSpeed * vertSpeed));  | ||
   		realSpeed = realSpeed   |    		realSpeed = me.fdm.fps2kt(realSpeed);  | ||
   		return realSpeed;  |    		return realSpeed;  | ||
	},  | 	},  | ||
| Line 239: | Line 267: | ||
			var failure_modes = FailureMgr._failmgr.failure_modes;  | 			var failure_modes = FailureMgr._failmgr.failure_modes;  | ||
		    var mode_list = keys(failure_modes);  | 		    var mode_list = keys(failure_modes);  | ||
		    var probability = speed / 200.0;#200kt will fail everything, 0kt will fail nothing.  | 		    var probability = speed / 200.0;# 200kt will fail everything, 0kt will fail nothing.  | ||
		    #test for explosion  | 		    # test for explosion  | ||
		    if(probability > 1.0 and me.input.fuel.getValue() > 2500) {  | 		    if(probability > 1.0 and me.fdm.input.fuel.getValue() > 2500) {  | ||
		    	#200kt+ and fuel   | 		    	# 200kt+ and fuel in tanks will explode the aircraft on impact.  | ||
		    	me._explodeBegin();  | 		    	me._explodeBegin();  | ||
		    	return;  | 		    	return;  | ||
| Line 270: | Line 298: | ||
		if (speed > 5) {#check if sound already running?  | 		if (speed > 5) {#check if sound already running?  | ||
			me.input.wCrashOn.setValue(1);  | 			me.input.wCrashOn.setValue(1);  | ||
			var timer = maketimer(3, me, me._impactSoundWaterEnd);  | |||
			timer.start();  | |||
		}  | 		}  | ||
	},  | 	},  | ||
| Line 279: | Line 308: | ||
		if (speed > 5) {  | 		if (speed > 5) {  | ||
			me.input.crashOn.setValue(1);  | 			me.input.crashOn.setValue(1);  | ||
			var timer = maketimer(3, me, me._impactSoundEnd);  | |||
			timer.start();  | |||
		}  | 		}  | ||
	},  | 	},  | ||
| Line 297: | Line 327: | ||
	    me._output("Aircraft exploded.", TRUE);  | 	    me._output("Aircraft exploded.", TRUE);  | ||
		var timer = maketimer(3, me, me._explodeEnd);  | |||
		timer.start();  | |||
	},  | 	},  | ||
	_explodeEnd: func () {  | 	_explodeEnd: func () {  | ||
| Line 305: | Line 336: | ||
		me._output("Aircraft damaged: Wings broke off, due to "~str~" G forces.");  | 		me._output("Aircraft damaged: Wings broke off, due to "~str~" G forces.");  | ||
		me.input.detachOn.setValue(1);  | 		me.input.detachOn.setValue(1);  | ||
  		FailureMgr.set_failure_level(me.fdm.wingsFailureID, 1);  | |||
		me.wingsAttached = FALSE;  | 		me.wingsAttached = FALSE;  | ||
		var timer = maketimer(3, me, me._stressDamageEnd);  | |||
		timer.start();  | |||
	},  | 	},  | ||
	_stressDamageEnd: func () {  | 	_stressDamageEnd: func () {  | ||
| Line 318: | Line 347: | ||
	},  | 	},  | ||
	_output: func (str, override = FALSE) {  | 	_output: func (str, override = FALSE) {  | ||
		var time = me.input.simTime.getValue();  | 		var time = me.fdm.input.simTime.getValue();  | ||
		if (override == TRUE or (time - me.lastMessageTime) > 3) {  | 		if (override == TRUE or (time - me.lastMessageTime) > 3) {  | ||
			me.lastMessageTime = time;  | 			me.lastMessageTime = time;  | ||
| Line 328: | Line 357: | ||
		me._testStress();  | 		me._testStress();  | ||
		me._testWaterImpact();  | 		me._testWaterImpact();  | ||
	},  | 	},  | ||
	_testWaterImpact: func () {  | 	_testWaterImpact: func () {  | ||
| Line 344: | Line 370: | ||
	},  | 	},  | ||
	_testStress: func () {  | 	_testStress: func () {  | ||
		if (  | 		if (me._isRunning() == TRUE and me.wingsAttached == TRUE) {  | ||
			var gForce = me.input.Nz.getValue() == nil?1:me.input.Nz.getValue();  | 			var gForce = me.fdm.input.Nz.getValue() == nil?1:me.fdm.input.Nz.getValue();  | ||
			var weight = me.input.weight.getValue();  | 			var weight = me.fdm.input.weight.getValue();  | ||
			var wingload = gForce * weight;  | 			var wingload = gForce * weight;  | ||
| Line 404: | Line 430: | ||
	},  | 	},  | ||
};  | };  | ||
var ImpactStructureListener = {  | |||
	crash: nil,  | |||
	run: func () {  | |||
		if (crash._isRunning() == TRUE) {  | |||
			var wow = crash._isStructureInContact();  | |||
			if (wow == TRUE) {  | |||
				crash._impactDamage();  | |||
			}  | |||
		}  | |||
	},  | |||
};  | |||
# static class  | |||
var fdmProperties = {  | |||
	input: {},  | |||
	convert: func () {  | |||
		foreach(var ident; keys(me.input)) {  | |||
		    me.input[ident] = props.globals.getNode(me.input[ident], 1);  | |||
		}  | |||
	},  | |||
	fps2kt: func (fps) {  | |||
		return fps * 0.5924838;  | |||
	},  | |||
	wingsFailureID: nil,  | |||
};  | |||
var jsbSimProp = {  | |||
	parents: [fdmProperties],  | |||
	input: {  | |||
				weight:     "fdm/jsbsim/inertia/weight-lbs",  | |||
				fuel:       "fdm/jsbsim/propulsion/total-fuel-lbs",  | |||
				simTime:    "fdm/jsbsim/simulation/sim-time-sec",  | |||
				vgFps:      "fdm/jsbsim/velocities/vg-fps",  | |||
				downFps:    "velocities/down-relground-fps",  | |||
				Nz:         "fdm/jsbsim/accelerations/Nz",  | |||
	},  | |||
	wingsFailureID: "fdm/jsbsim/structural/wings",  | |||
};  | |||
var yaSimProp = {  | |||
	parents: [fdmProperties],  | |||
	input: {  | |||
				weight:     "yasim/gross-weight-lbs",  | |||
				fuel:       "consumables/fuel/total-fuel-lbs",  | |||
				simTime:    "sim/time/elapsed-sec",  | |||
				vgFps:      "velocities/groundspeed-kt",  | |||
				Nz:         "accelerations/n-z-cg-fps_sec",  | |||
	},  | |||
	convert: func () {  | |||
		call(fdmProperties.convert, [], me);  | |||
		me.input.downFps = props.Node.new().setValue(0);  | |||
	},  | |||
	fps2kt: func (fps) {  | |||
		return fps;  | |||
	},  | |||
	wingsFailureID: "structural/wings",  | |||
};  | |||
# TODO:  | # TODO:  | ||
| Line 412: | Line 499: | ||
# Explosion depending on bumpiness and speed when sliding?  | # Explosion depending on bumpiness and speed when sliding?  | ||
# Tie in with damage from Bombable?  | # Tie in with damage from Bombable?  | ||
# Use galvedro's UpdateLoop framework when it gets merged  | |||
# example uses:  | # example uses:  | ||
#  | #  | ||
# var crashCode =   | # var crashCode = CrashAndStress.new([0,1,2];    | ||
#  | #  | ||
# var crashCode =   | # var crashCode = CrashAndStress.new([0,1,2], {"weightLbs":30000, "maxG": 12});  | ||
#  | #  | ||
# var crashCode =   | # var crashCode = CrashAndStress.new([0,1,2,3], {"weightLbs":20000, "maxG": 11, "minG": -5});  | ||
#  | #  | ||
# var crashCode =   | # var crashCode = CrashAndStress.new([0,1,2], {"wingloadMaxLbs": 90000, "wingloadMinLbs": -45000}, ["controls/flight/aileron", "controls/flight/elevator", "controls/flight/flaps"]);  | ||
#  | #  | ||
# var crashCode =   | # var crashCode = CrashAndStress.new([0,1,2], {"wingloadMaxLbs":90000}, ["controls/flight/aileron", "controls/flight/elevator", "controls/flight/flaps"]);  | ||
#  | #  | ||
# Gears parameter must be defined.  | # Gears parameter must be defined.  | ||
| Line 435: | Line 523: | ||
# use:  | # use:  | ||
var crashCode =   | var crashCode = CrashAndStress.new([0,1,2], {"weightLbs":30000, "maxG": 12}, ["controls/gear1", "controls/gear2", "controls/flight/aileron", "controls/flight/elevator", "consumables/fuel/wing-tanks"]);  | ||
crashCode.start();  | crashCode.start();  | ||
| Line 450: | Line 538: | ||
<syntaxhighlight lang="xml">  | <syntaxhighlight lang="xml">  | ||
<crash>  | <crash>  | ||
   <file>Aircraft/[aircraft name here]/Nasal/  |    <file>Aircraft/[aircraft name here]/Nasal/crash-and-stress.nas</file>  | ||
</crash>  | </crash>  | ||
</syntaxhighlight>  | </syntaxhighlight>  | ||
| Line 575: | Line 663: | ||
           <type>float</type>  |            <type>float</type>  | ||
           <property type="string">damage/sounds/creaking-volume</property>  |            <property type="string">damage/sounds/creaking-volume</property>  | ||
        </signal>  | |||
        <signal>  | |||
          <type>bool</type>  | |||
          <property type="string">fdm/jsbsim/structural/wings/serviceable</property>  | |||
        </signal>  | |||
</syntaxhighlight>  | |||
Notice that the last signal is jsbsim specific, the corresponding yasim signal is:  | |||
<syntaxhighlight lan="nasal">  | |||
        <signal>  | |||
          <type>bool</type>  | |||
          <property type="string">structural/wings/serviceable</property>  | |||
         </signal>  |          </signal>  | ||
</syntaxhighlight>  | </syntaxhighlight>  | ||
That property is also what you would use to animate wings being detached and/or changes in the aerodynamics.  | |||
== Performance ==  | == Performance ==  | ||
edits