diff --git a/Nasal/towing/hitch.nas b/Nasal/towing/hitch.nas index 13f988288..cdaf7ab7f 100644 --- a/Nasal/towing/hitch.nas +++ b/Nasal/towing/hitch.nas @@ -1,5 +1,9 @@ # -# Version: 30. December 2014 +# TOWING NASAL CODE +# +# original version by D-NXKT up to version 30.12.2014 +# updates by Benedikt Wolf and D-NXKT, version 01/2023 +# # # Purpose of this routine: # ------------------------ @@ -169,37 +173,33 @@ # 4. Set optional properties: +# +# Only aircraft-specific properties should be set by the aircraft. All other options can be adjusted by the +# winch GUI dialog and are stored by this script itself (TODO) +# +# Aircraft-specific properties are +# * weak link (aerotow/winch) +# sim/hitches/aerotow/tow/break-fource [N] +# sim/hitches/winch/tow/break-force [N] +# * typical tow speed (winch) +# sim/hitches/winch/typical-tow-speed-kph [kph] +# * automatic release angle (winch) +# sim/hitches/winch/automatic-release-angle-deg [deg] +# # # # # -# 6000 -# 9000 +# 6000 # -# -# 20 -# # # # 70. -# -# 1000. -# 1500. -# 800. -# 100. -# 15. -# 20. -# 8. -# -# +# 100 +# 5000 # # 10000 -# 40000 -# 0.01 # -# -# 40 -# # # # @@ -211,7 +211,7 @@ ################################################## general info ############################################ # # 3 different types of towplanes could exist: AI-plane, MP-plane without interaction, MP-plane with interaction. -# AI-planes are identified by the node "ai/models/aircraft/". +# AI-planes are identified by the node "ai/models/aircraft/" or "ai/models/dragger". # MP-planes (interactice/non-interactive) are identified by the existence of node "ai/models/multiplayer". # Interactive MP-plane: variables in node "ai/models/multiplayer/sim/hitches/" are updated. # Non-interactive MP-plane: variables are not updated (values are either not defined or have "wrong" values @@ -223,7 +223,7 @@ # "sim/hitches/aerotow/tow/dist" # "sim/hitches/aerotow/tow/connected-to-property-node" # "sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign" -# "sim/hitches/aerotow/tow/brake-force" +# "sim/hitches/aerotow/tow/break-force" # "sim/hitches/aerotow/tow/end-force-x" # "sim/hitches/aerotow/tow/end-force-y" # "sim/hitches/aerotow/tow/end-force-z" @@ -244,8 +244,8 @@ # ###################################################################################################################### # Check if node "sim/hitches" is defined. If not, return! - if (props.globals.getNode("sim/hitches") == nil ) return; - print("towing is active!"); +if (props.globals.getNode("sim/hitches") == nil ) return; +print("towing is active!"); # ###################################################################################################################### @@ -254,430 +254,622 @@ # set defaults for properties that are NOT already defined - # yasim properties for aerotow (should be already defined for yasim aircraft but not for JSBSim aircraft - if (props.globals.getNode("sim/hitches/aerotow/broken") == nil ) - props.globals.getNode("sim/hitches/aerotow/broken", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/aerotow/force") == nil ) - props.globals.getNode("sim/hitches/aerotow/force", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/aerotow/force-is-calculated-by-other") == nil ) - props.globals.getNode("sim/hitches/aerotow/force-is-calculated-by-other", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/aerotow/is-slave") == nil ) - props.globals.getNode("sim/hitches/aerotow/is-slave", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/aerotow/local-pos-x") == nil ) - props.globals.getNode("sim/hitches/aerotow/local-pos-x", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/aerotow/local-pos-y") == nil ) - props.globals.getNode("sim/hitches/aerotow/local-pos-y", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/aerotow/local-pos-z") == nil ) - props.globals.getNode("sim/hitches/aerotow/local-pos-z", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period") == nil ) - props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/aerotow/mp-time-lag") == nil ) - props.globals.getNode("sim/hitches/aerotow/mp-time-lag", 1).setValue(0.); - #if (props.globals.getNode("sim/hitches/aerotow/open") == nil ) - props.globals.getNode("sim/hitches/aerotow/open", 1).setBoolValue(1); - if (props.globals.getNode("sim/hitches/aerotow/speed-in-tow-direction") == nil ) - props.globals.getNode("sim/hitches/aerotow/speed-in-tow-direction", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/aerotow/tow/brake-force") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/brake-force", 1).setValue(12345.); - if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-node") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-node", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign", 1).setValue(""); - if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id", 1).setIntValue(0); - if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-mp-node") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-mp-node", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-property-node") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-property-node", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/aerotow/tow/dist") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/dist", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/aerotow/tow/elastic-constant") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/elastic-constant", 1).setValue(9111.); - if (props.globals.getNode("sim/hitches/aerotow/tow/end-force-x") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/end-force-x", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/aerotow/tow/end-force-y") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/end-force-y", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/aerotow/tow/end-force-z") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/end-force-z", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/aerotow/tow/length") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/length", 1).setValue(60.); - if (props.globals.getNode("sim/hitches/aerotow/tow/node") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/node", 1).setValue(""); - if (props.globals.getNode("sim/hitches/aerotow/tow/weight-per-m-kg-m") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/weight-per-m-kg-m", 1).setValue(0.35); +var fdm = getprop("/sim/flight-model") or ""; - # additional properties - if (props.globals.getNode("sim/hitches/aerotow/oldOpen") == nil ) - props.globals.getNode("sim/hitches/aerotow/oldOpen", 1).setBoolValue(1); +if ( !(fdm == "jsb" or fdm == "yasim") ){ + print("Unsupported FDM for towing"); + return; +} - # new properties for towrope - if (props.globals.getNode("sim/hitches/aerotow/rope/exist") == nil ) - props.globals.getNode("sim/hitches/aerotow/rope/exist", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/aerotow/rope/model_id") == nil ) - props.globals.getNode("sim/hitches/aerotow/rope/model_id", 1).setIntValue(-1); - if (props.globals.getNode("sim/hitches/aerotow/rope/path_to_model") == nil ) - props.globals.getNode("sim/hitches/aerotow/rope/path_to_model", 1).setValue("Models/Aircraft/towropes.xml"); - if (props.globals.getNode("sim/hitches/aerotow/rope/rope-diameter-mm") == nil ) - props.globals.getNode("sim/hitches/aerotow/rope/rope-diameter-mm", 1).setIntValue(20.); +var check_or_create = func ( name, value, type ) { + if ( getprop( name ) == nil ){ + return props.globals.initNode(name, value, type); + } else { + return props.globals.getNode(name); + } +} - # new properties for JSBSim aerotow - if ( getprop("sim/flight-model") == "jsb" ) { - if (props.globals.getNode("sim/hitches/aerotow/force_name_jsbsim") == nil ) - props.globals.getNode("sim/hitches/aerotow/force_name_jsbsim", 1).setValue("hitch"); - if (props.globals.getNode("sim/hitches/aerotow/mp_oldOpen") == nil ) - props.globals.getNode("sim/hitches/aerotow/mp_oldOpen", 1).setBoolValue(1); - if (props.globals.getNode("sim/hitches/aerotow/tow/mp_last_reporded_dist") == nil ) - props.globals.getNode("sim/hitches/aerotow/tow/mp_last_reported_dist", 1).setValue(0.); - } +# Load stored settings +var config_file = getprop("/sim/fg-home") ~ "/Export/hitch-config.xml"; - # yasim properties for winch (should already be defined for yasim aircraft but not for JSBSim aircraft - #if (props.globals.getNode("sim/hitches/winch/open") == nil ) - props.globals.getNode("sim/hitches/winch/open", 1).setBoolValue(1); - if (props.globals.getNode("sim/hitches/winch/broken") == nil ) - props.globals.getNode("sim/hitches/winch/broken", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/winch/winch/global-pos-x") == nil ) - props.globals.getNode("sim/hitches/winch/winch/global-pos-x", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/winch/winch/global-pos-y") == nil ) - props.globals.getNode("sim/hitches/winch/winch/global-pos-y", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/winch/winch/global-pos-z") == nil ) - props.globals.getNode("sim/hitches/winch/winch/global-pos-z", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/winch/winch/initial-tow-length-m") == nil ) - props.globals.getNode("sim/hitches/winch/winch/initial-tow-length-m", 1).setValue(1000.); - if (props.globals.getNode("sim/hitches/winch/winch/max-tow-length-m") == nil ) - props.globals.getNode("sim/hitches/winch/winch/max-tow-length-m", 1).setValue(1500.); - if (props.globals.getNode("sim/hitches/winch/winch/min-tow-length-m") == nil ) - props.globals.getNode("sim/hitches/winch/winch/min-tow-length-m", 1).setValue(1.); +var load_prop = func( property, cnfg_p ){ + if( cnfg_p != nil ){ + property.setValue( cnfg_p.getValue() ); + } +} - if (props.globals.getNode("sim/hitches/winch/tow/length") == nil ) - props.globals.getNode("sim/hitches/winch/tow/length", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/winch/tow/dist") == nil ) - props.globals.getNode("sim/hitches/winch/tow/dist", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/winch/tow/elastic-constant") == nil ) - props.globals.getNode("sim/hitches/winch/tow/elastic-constant", 1).setValue(40001.); - if (props.globals.getNode("sim/hitches/winch/tow/weight-per-m-kg-m") == nil ) - props.globals.getNode("sim/hitches/winch/tow/weight-per-m-kg-m", 1).setValue(0.1); +var config = { + aerotow: { + elastic_constant: props.globals.initNode("sim/hitches/aerotow/tow/elastic-constant", 9111.0, "DOUBLE"), # saved in config + weight_per_m_kg_m: props.globals.initNode("sim/hitches/aerotow/tow/weight-per-m-kg-m", 0.35, "DOUBLE"), # saved in config + length: props.globals.initNode("sim/hitches/aerotow/tow/length", 60.0, "DOUBLE"), # saved in config + path_to_model: props.globals.initNode("sim/hitches/aerotow/rope/path_to_model", "Models/Aircraft/towropes.xml", "STRING"), # saved in config + rope_diameter_mm: props.globals.initNode("sim/hitches/aerotow/rope/rope-diameter-mm", 20.0, "DOUBLE"), # saved in config + }, + winch: { + elastic_constant: props.globals.initNode("sim/hitches/winch/tow/elastic-constant", 40001.0, "DOUBLE"), # saved in config + weight_per_m_kg_m: props.globals.initNode("sim/hitches/winch/tow/weight-per-m-kg-m", 0.1, "DOUBLE"), # saved in config + initial_tow_length: props.globals.initNode("sim/hitches/winch/winch/initial-tow-length-m", 1000.0, "DOUBLE"), # saved in config + path_to_model: props.globals.initNode("sim/hitches/winch/rope/path_to_model", "Models/Aircraft/towropes.xml", "STRING"), # saved in config + rope_diameter_mm: props.globals.initNode("sim/hitches/winch/rope/rope-diameter-mm", 20.0, "DOUBLE"), # saved in config - # additional properties - if (props.globals.getNode("sim/hitches/winch/oldOpen") == nil ) - props.globals.getNode("sim/hitches/winch/oldOpen", 1).setBoolValue(1); - if (props.globals.getNode("sim/hitches/winch/winch/max-spool-speed-m-s") == nil ) - props.globals.getNode("sim/hitches/winch/winch/max-spool-speed-m-s", 1).setValue(40.); + rope_breakage: { + height: props.globals.initNode("sim/hitches/winch/breakage/height", 100.0, "DOUBLE"), # saved in config + random: props.globals.initNode("sim/hitches/winch/breakage/random", 0, "BOOL"), # saved in config + }, + loss_of_power: { + height: props.globals.initNode("sim/hitches/winch/loss-of-power/height", 100.0, "DOUBLE"), # saved in config + random: props.globals.initNode("sim/hitches/winch/loss-of-power/random", 0, "BOOL"), # saved in config + }, + max_tow_length_m: props.globals.initNode("sim/hitches/winch/winch/max-tow-length-m", 1500.0, "DOUBLE"), # saved in config + max_spool_speed: props.globals.initNode("sim/hitches/winch/winch/max-spool-speed-m-s", 40.0, "DOUBLE"), # saved in config - # new properties for winch-rope - if (props.globals.getNode("sim/hitches/winch/rope/exist") == nil ) - props.globals.getNode("sim/hitches/winch/rope/exist", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/winch/rope/model_id") == nil ) - props.globals.getNode("sim/hitches/winch/rope/model_id", 1).setIntValue(-1); - if (props.globals.getNode("sim/hitches/winch/rope/path_to_model") == nil ) - props.globals.getNode("sim/hitches/winch/rope/path_to_model", 1).setValue("Models/Aircraft/towropes.xml"); - if (props.globals.getNode("sim/hitches/winch/rope/rope-diameter-mm") == nil ) - props.globals.getNode("sim/hitches/winch/rope/rope-diameter-mm", 1).setIntValue(20.); + force_acceleration: props.globals.initNode("sim/hitches/winch/winch/force-acceleration-N-s", 1000.0, "DOUBLE"), # saved in config + spool_acceleration: props.globals.initNode("sim/hitches/winch/winch/spool-acceleration-m-s-s", 8.0, "DOUBLE"), # saved in config - # new properties for JSBSim winch - if ( getprop("sim/flight-model") == "jsb" ) { - if (props.globals.getNode("sim/hitches/winch/force_name_jsbsim") == nil ) - props.globals.getNode("sim/hitches/winch/force_name_jsbsim", 1).setValue("hitch"); - if (props.globals.getNode("sim/hitches/winch/automatic-release-angle-deg") == nil ) - props.globals.getNode("sim/hitches/winch/automatic-release-angle-deg", 1).setValue(361.); - if (props.globals.getNode("sim/hitches/winch/winch/clutched") == nil ) - props.globals.getNode("sim/hitches/winch/winch/clutched", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/winch/winch/actual-spool-speed-m-s") == nil ) - props.globals.getNode("sim/hitches/winch/winch/actual-spool-speed-m-s", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/winch/winch/spool-acceleration-m-s-s") == nil ) - props.globals.getNode("sim/hitches/winch/winch/spool-acceleration-m-s-s", 1).setValue(8.); - if (props.globals.getNode("sim/hitches/winch/winch/max-unspool-speed-m-s") == nil ) - props.globals.getNode("sim/hitches/winch/winch/max-unspool-speed-m-s", 1).setValue(40.); - if (props.globals.getNode("sim/hitches/winch/winch/actual-force-N") == nil ) - props.globals.getNode("sim/hitches/winch/winch/actual-force-N", 1).setValue(0.); - if (props.globals.getNode("sim/hitches/winch/winch/max-force-N") == nil ) - props.globals.getNode("sim/hitches/winch/winch/max-force-N", 1).setValue(1000.); - if (props.globals.getNode("sim/hitches/winch/winch/max-power-kW") == nil ) - props.globals.getNode("sim/hitches/winch/winch/max-power-kW", 1).setValue(123.); - if (props.globals.getNode("sim/hitches/winch/tow/break-force-N") == nil ) - props.globals.getNode("sim/hitches/winch/tow/break-force-N", 1).setValue(12345.); - if (props.globals.getNode("sim/hitches/winch/winch/magic-constant") == nil ) - props.globals.getNode("sim/hitches/winch/winch/magic-constant", 1).setValue(500.); - } + max_unspool_speed: props.globals.initNode("sim/hitches/winch/winch/max-unspool-speed-m-s", 40.0, "DOUBLE"), # saved in config + max_force: props.globals.initNode("sim/hitches/winch/winch/max-force-N", 10000.0, "DOUBLE"), # saved in config + max_power: props.globals.initNode("sim/hitches/winch/winch/max-power-kW", 123.0, "DOUBLE"), # saved in config + magic_constant: props.globals.initNode("sim/hitches/winch/winch/magic-constant", 500.0, "DOUBLE"), # saved in config - # new properties for JSBSim aerotow and winch - if ( getprop("sim/flight-model") == "jsb" ) { - if (props.globals.getNode("sim/hitches/aerotow/decoupled-force-and-rope-locations") == nil ) - props.globals.getNode("sim/hitches/aerotow/decoupled-force-and-rope-locations", 1).setBoolValue(0); - if (props.globals.getNode("sim/hitches/winch/decoupled-force-and-rope-locations") == nil ) - props.globals.getNode("sim/hitches/winch/decoupled-force-and-rope-locations", 1).setBoolValue(0); - # consider older JSBSim-versions which do NOT provide the locations of external_reactions in the property tree - var hitchname_aerotow = getprop("sim/hitches/aerotow/force_name_jsbsim"); - var hitchname_winch = getprop("sim/hitches/winch/force_name_jsbsim"); - if (props.globals.getNode("fdm/jsbsim/external_reactions/" ~ hitchname_aerotow ~ "/location-x-in") == nil ) - props.globals.getNode("sim/hitches/aerotow/decoupled-force-and-rope-locations").setBoolValue(1); - if (props.globals.getNode("fdm/jsbsim/external_reactions/" ~ hitchname_winch ~ "/location-x-in") == nil ) - props.globals.getNode("sim/hitches/winch/decoupled-force-and-rope-locations").setBoolValue(1); - } + messages: { + launch_signaller: props.globals.initNode("/sim/hitches/winch/messages/launch-signaller", 1, "BOOL"), # saved in config + winch_driver: props.globals.initNode("/sim/hitches/winch/messages/winch-driver", 1, "BOOL"), # saved in config + pilot: props.globals.initNode("/sim/hitches/winch/messages/pilot", 1, "BOOL"), # saved in config + remote_ac: props.globals.initNode("/sim/hitches/winch/messages/remote-ac", 1, "BOOL"), # saved in config + }, + }, +}; + +var write_config = func { + var c = props.Node.new(); + var a = c.addChild("aerotow"); + var w = c.addChild("winch"); + + a.initNode("elastic-constant").setDoubleValue( config.aerotow.elastic_constant.getDoubleValue() ); + a.initNode("weight-per-m-kg-m").setDoubleValue( config.aerotow.weight_per_m_kg_m.getDoubleValue() ); + a.initNode("length").setDoubleValue( config.aerotow.length.getDoubleValue() ); + a.initNode("path_to_model","","STRING").setValue( config.aerotow.path_to_model.getValue() ); + a.initNode("rope-diameter-mm").setDoubleValue( config.aerotow.rope_diameter_mm.getDoubleValue() ); + + w.initNode("elastic-constant").setDoubleValue( config.winch.elastic_constant.getDoubleValue() ); + w.initNode("weight-per-m-kg-m").setDoubleValue( config.winch.weight_per_m_kg_m.getDoubleValue() ); + w.initNode("initial-tow-length-m").setDoubleValue( config.winch.initial_tow_length.getDoubleValue() ); + w.initNode("path_to_model","","STRING").setValue( config.winch.path_to_model.getValue() ); + w.initNode("rope-diameter-mm").setDoubleValue( config.winch.rope_diameter_mm.getDoubleValue() ); + w.initNode("breakage/height").setDoubleValue(config.winch.rope_breakage.height.getDoubleValue() ); + w.initNode("breakage/random",0,"BOOL").setBoolValue(config.winch.rope_breakage.random.getBoolValue() ); + w.initNode("loss-of-power/height").setDoubleValue(config.winch.loss_of_power.height.getDoubleValue() ); + w.initNode("loss-of-power/random",0,"BOOL").setBoolValue(config.winch.loss_of_power.random.getBoolValue() ); + w.initNode("max-tow-length-m").setDoubleValue( config.winch.max_tow_length_m.getDoubleValue() ); + w.initNode("max-spool-speed-m-s").setDoubleValue( config.winch.max_spool_speed.getDoubleValue() ); + w.initNode("force-acceleration-N-s").setDoubleValue( config.winch.force_acceleration.getDoubleValue() ); + w.initNode("spool-acceleration-m-s-s").setDoubleValue( config.winch.spool_acceleration.getDoubleValue() ); + w.initNode("max-unspool-speed-m-s").setDoubleValue( config.winch.max_unspool_speed.getDoubleValue() ); + w.initNode("max-force-N").setDoubleValue( config.winch.max_force.getDoubleValue() ); + w.initNode("max-power-kW").setDoubleValue( config.winch.max_power.getDoubleValue() ); + w.initNode("magic-constant").setDoubleValue( config.winch.magic_constant.getDoubleValue() ); + w.initNode("messages/launch-signaller",1,"BOOL").setBoolValue( config.winch.messages.launch_signaller.getBoolValue() ); + w.initNode("messages/winch-driver",1,"BOOL").setBoolValue( config.winch.messages.winch_driver.getBoolValue() ); + w.initNode("messages/pilot",1,"BOOL").setBoolValue( config.winch.messages.pilot.getBoolValue() ); + w.initNode("messages/remote-ac",1,"BOOL").setBoolValue( config.winch.messages.remote_ac.getBoolValue() ); + + io.write_properties( config_file, c ); +} + +if( io.stat( config_file ) != nil ){ + var config_properties = io.read_properties( config_file ); + + load_prop( config.aerotow.elastic_constant, config_properties.getNode("aerotow/elastic-constant") ); + load_prop( config.aerotow.weight_per_m_kg_m, config_properties.getNode("aerotow/weight-per-m-kg-m") ); + load_prop( config.aerotow.length, config_properties.getNode("aerotow/length") ); + load_prop( config.aerotow.path_to_model, config_properties.getNode("aerotow/path_to_model") ); + load_prop( config.aerotow.rope_diameter_mm, config_properties.getNode("aerotow/rope-diameter-mm") ); + + load_prop( config.winch.elastic_constant, config_properties.getNode("winch/elastic-constant") ); + load_prop( config.winch.weight_per_m_kg_m, config_properties.getNode("winch/weight-per-m-kg-m") ); + load_prop( config.winch.initial_tow_length, config_properties.getNode("winch/initial-tow-length-m") ); + load_prop( config.winch.path_to_model, config_properties.getNode("winch/path_to_model") ); + load_prop( config.winch.rope_diameter_mm, config_properties.getNode("winch/rope-diameter-mm") ); + load_prop( config.winch.rope_breakage.height, config_properties.getNode("winch/breakage/height") ); + load_prop( config.winch.rope_breakage.random, config_properties.getNode("winch/breakage/random") ); + load_prop( config.winch.loss_of_power.height, config_properties.getNode("winch/loss-of-power/height") ); + load_prop( config.winch.loss_of_power.random, config_properties.getNode("winch/loss-of-power/random") ); + load_prop( config.winch.max_tow_length_m, config_properties.getNode("winch/max-tow-length-m") ); + load_prop( config.winch.max_spool_speed, config_properties.getNode("winch/max-spool-speed-m-s") ); + load_prop( config.winch.force_acceleration, config_properties.getNode("winch/force-acceleration-N-s") ); + load_prop( config.winch.spool_acceleration, config_properties.getNode("winch/spool-acceleration-m-s-s") ); + load_prop( config.winch.max_unspool_speed, config_properties.getNode("winch/max-unspool-speed-m-s") ); + load_prop( config.winch.max_force, config_properties.getNode("winch/max-force-N") ); + load_prop( config.winch.max_power, config_properties.getNode("winch/max-power-kW") ); + load_prop( config.winch.magic_constant, config_properties.getNode("winch/magic-constant") ); + load_prop( config.winch.messages.launch_signaller, config_properties.getNode("winch/messages/launch-signaller") ); + load_prop( config.winch.messages.winch_driver, config_properties.getNode("winch/messages/winch-driver") ); + load_prop( config.winch.messages.pilot, config_properties.getNode("winch/messages/pilot") ); + load_prop( config.winch.messages.remote_ac, config_properties.getNode("winch/messages/remote-ac") ); +} + +var aircraft_settings = { + aerotow: { + force_calc_by_other: check_or_create("sim/hitches/aerotow/force-is-calculated-by-other", 0, "BOOL"), # aircraft-specific + is_slave: check_or_create("sim/hitches/aerotow/is-slave", 0, "BOOL"), # aircraft-specific + break_force: check_or_create("sim/hitches/aerotow/tow/break-force", 12345.0, "DOUBLE"), # aircraft-specific + local_pos: [ + check_or_create("sim/hitches/aerotow/local-pos-x", 0.0, "DOUBLE"), # aircraft-specific + check_or_create("sim/hitches/aerotow/local-pos-y", 0.0, "DOUBLE"), # aircraft-specific + check_or_create("sim/hitches/aerotow/local-pos-z", 0.0, "DOUBLE"), # aircraft-specific + ], + }, + winch: { + typical_tow_speed: props.globals.getNode("sim/hitches/winch/typical-tow-speed-kph"), + typical_tow_force: props.globals.getNode("sim/hitches/winch/typical-tow-force-N"), + # Winch type: 0 = tow with constant force; 1 = tow with constant rope speed; aircraft-specific + type_p: check_or_create("sim/hitches/winch/type", 0, "INT"), + break_force: check_or_create("sim/hitches/winch/tow/break-force", 12345.0, "DOUBLE"), # aircraft-specific + local_pos: [ + check_or_create("sim/hitches/winch/local-pos-x", 0.0, "DOUBLE"), # aircraft-specific + check_or_create("sim/hitches/winch/local-pos-y", 0.0, "DOUBLE"), # aircraft-specific + check_or_create("sim/hitches/winch/local-pos-z", 0.0, "DOUBLE"), # aircraft-specific + ], + }, +}; + +var check_aircraft_tow_settings = func{ + if( aircraft_settings.winch.typical_tow_speed == nil ){ + aircraft_settings.winch.typical_tow_speed = props.globals.initNode( "sim/hitches/winch/typical-tow-speed-kph" ); + + # Estimate typical tow speed from aircraft weight, this will inherently be a rough guess + # Estimation basis: + # type | typical tow speed | typical weight | reference + # hang glider | 50 kph | 100 kg | https://www.safa.asn.au/resources/Tow%20Manual%20v5.4.1.pdf + # Ka6 | 90 kph | 250 kg | http://www.smbc-eferding.at/wp-content/uploads/2014/01/Flughandbuch-Ka-6.pdf + # LS8 | 120 kph | 500 kg | https://www.sglenzburg.ch/org/public/Dokumente/SGL/10-AFM/fhb_ls8a_rev3_tm8020.pdf + # DG-1000S | 130 kph | 650 kg | http://adelaidesoaring.on.net/wp-content/uploads/2014/01/DG-1000s-Flight-Manual.pdf + # + # a quadratic regression for this data leads to y = -0.0002x2 + 0.3286x + 20.839, we use (simplified): f(x) = -0.0002 * x^2 + 0.33 * x + 21 + var weight_kg = weight_lb.getDoubleValue() * LB2KG; + aircraft_settings.winch.typical_tow_speed.setDoubleValue( -0.0002 * weight_kg * weight_kg + 0.33 * weight_kg + 21 ); + } + if( aircraft_settings.winch.typical_tow_force == nil ){ + aircraft_settings.winch.typical_tow_force = props.globals.initNode( "sim/hitches/winch/typical-tow-force-N" ); + + aircraft_settings.winch.typical_tow_force.setDoubleValue( 0.15 * math.pow( weight_lb.getDoubleValue(), 1.25 ) * LB2KG * 9.81); + } +} + +# yasim properties for aerotow (should be already defined for yasim aircraft but not for JSBSim aircraft +var aerotow_hash = { + broken: props.globals.initNode("sim/hitches/aerotow/broken", 0, "BOOL"), + force: props.globals.initNode("sim/hitches/aerotow/force", 0.0, "DOUBLE"), + mp_auto_connect_period: props.globals.initNode("sim/hitches/aerotow/mp-auto-connect-period", 0.0, "DOUBLE"), + mp_time_lag: props.globals.initNode("sim/hitches/aerotow/mp-time-lag", 0.0, "DOUBLE"), + open: props.globals.initNode("sim/hitches/aerotow/open", 1, "BOOL"), #always init to true + speed_tow_direction: props.globals.initNode("sim/hitches/aerotow/speed-in-tow-direction", 0.0, "DOUBLE"), + old_open: props.globals.initNode("sim/hitches/aerotow/oldOpen", 1, "BOOL"), + tow: { + conn_ai_node: props.globals.initNode("sim/hitches/aerotow/tow/connected-to-ai-node", 0, "BOOL"), + conn_ai_or_mp_callsign: props.globals.initNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign", "", "STRING"), + conn_ai_or_mp_id: props.globals.initNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id", 0, "INT"), + conn_mp_node: props.globals.initNode("sim/hitches/aerotow/tow/connected-to-mp-node", 0, "BOOL"), + conn_prop_node: props.globals.initNode("sim/hitches/aerotow/tow/connected-to-property-node", 0, "BOOL"), + dist: props.globals.initNode("sim/hitches/aerotow/tow/dist", 0.0, "DOUBLE"), + end_force: [ + props.globals.initNode("sim/hitches/aerotow/tow/end-force-x", 0.0, "DOUBLE"), + props.globals.initNode("sim/hitches/aerotow/tow/end-force-y", 0.0, "DOUBLE"), + props.globals.initNode("sim/hitches/aerotow/tow/end-force-z", 0.0, "DOUBLE"), + ], + node: props.globals.initNode("sim/hitches/aerotow/tow/node", "", "STRING"), + }, + rope: { + exist: props.globals.initNode("sim/hitches/aerotow/rope/exist", 0, "BOOL"), + model_id: props.globals.initNode("sim/hitches/aerotow/rope/model_id", -1, "INT"), + + lat: props.globals.initNode("ai/models/aerotowrope/position/latitude-deg", 0.0, "DOUBLE"), + lon: props.globals.initNode("ai/models/aerotowrope/position/longitude-deg", 0.0, "DOUBLE"), + alt: props.globals.initNode("ai/models/aerotowrope/position/altitude-ft", 0.0, "DOUBLE"), + hdg: props.globals.initNode("ai/models/aerotowrope/orientation/true-heading-deg", 0.0, "DOUBLE"), + pitch: props.globals.initNode("ai/models/aerotowrope/orientation/pitch-deg", 0.0, "DOUBLE"), + }, +}; + +# yasim properties for winch (should already be defined for yasim aircraft but not for JSBSim aircraft +var winch_hash = { + open: props.globals.initNode("sim/hitches/winch/open", 1, "BOOL"), #always init to true + broken: props.globals.initNode("sim/hitches/winch/broken", 0, "BOOL"), + type: 0, + rope_breakage_p: props.globals.initNode("sim/hitches/winch/breakage/enabled", 0, "BOOL"), # NOT saved in config: Must be selected explicitly each time + rope_breakage: 0, + rope_breakage_height_int: 100.0, + loss_of_power: { + enabled_p: props.globals.initNode("sim/hitches/winch/loss-of-power/enabled", 0, "BOOL"), # NOT saved in config: Must be selected explicitly each time + enabled: 0, + height_int: 100.0, + }, + global_pos: [ + props.globals.initNode("sim/hitches/winch/winch/global-pos-x", 0.0, "DOUBLE"), + props.globals.initNode("sim/hitches/winch/winch/global-pos-y", 0.0, "DOUBLE"), + props.globals.initNode("sim/hitches/winch/winch/global-pos-z", 0.0, "DOUBLE"), + ], + tow: { + length: props.globals.initNode("sim/hitches/winch/tow/length", 0.0, "DOUBLE"), + dist: props.globals.initNode("sim/hitches/winch/tow/dist", 0.0, "DOUBLE"), + }, + old_open: props.globals.initNode("sim/hitches/winch/oldOpen", 1, "BOOL"), + rope: { + exist: props.globals.initNode("sim/hitches/winch/rope/exist", 0, "BOOL"), + model_id: props.globals.initNode("sim/hitches/winch/rope/model_id", -1, "INT"), + + lat: props.globals.initNode("ai/models/winchrope/position/latitude-deg", 0.0, "DOUBLE"), + lon: props.globals.initNode("ai/models/winchrope/position/longitude-deg", 0.0, "DOUBLE"), + alt: props.globals.initNode("ai/models/winchrope/position/altitude-ft", 0.0, "DOUBLE"), + hdg: props.globals.initNode("ai/models/winchrope/orientation/true-heading-deg", 0.0, "DOUBLE"), + pitch: props.globals.initNode("ai/models/winchrope/orientation/pitch-deg", 0.0, "DOUBLE"), + }, +}; + +var force_setting = 0.0; # used with constant force type winch +var speed_setting = 0.0; # used with constant speed type winch + + +if ( fdm == "jsb" ) { + # new properties for JSBSim aerotow + aircraft_settings.aerotow.force_name_jsbsim = check_or_create("sim/hitches/aerotow/force_name_jsbsim", "hitch", "STRING"); # aircraft-specific + aircraft_settings.aerotow.decoupled_locations = check_or_create("sim/hitches/aerotow/decoupled-force-and-rope-locations", 0, "BOOL"); # aircraft-specific + aircraft_settings.winch.force_name_jsbsim = check_or_create("sim/hitches/winch/force_name_jsbsim", "hitch", "STRING"); # aircraft-specific + aircraft_settings.winch.automatic_release_angle = check_or_create("sim/hitches/winch/automatic-release-angle-deg", 361.0, "DOUBLE"); # aircraft-specific + aircraft_settings.winch.decoupled_locations = check_or_create("sim/hitches/winch/decoupled-force-and-rope-locations", 0, "BOOL"); # aircraft-specific + + aerotow_hash.mp_old_open = props.globals.initNode("sim/hitches/aerotow/mp_oldOpen", 1, "BOOL"); + aerotow_hash.tow.mp_last_reported_dist = props.globals.initNode("sim/hitches/aerotow/tow/mp_last_reported_dist", 0.0, "DOUBLE"); + + # new properties for JSBSim winch + winch_hash.clutched = props.globals.initNode("sim/hitches/winch/winch/clutched", 0, "BOOL"); + winch_hash.actual_spool_speed = props.globals.initNode("sim/hitches/winch/winch/actual-spool-speed-m-s", 0.0, "DOUBLE"); + + winch_hash.actual_force = props.globals.initNode("sim/hitches/winch/winch/actual-force-N", 0.0, "DOUBLE"); + + var hitchname = aircraft_settings.winch.force_name_jsbsim.getValue(); + winch_hash.apply_force = { + magnitude: props.globals.initNode("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", 0.0, "DOUBLE"), + coord: [ + check_or_create("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", 0.0, "DOUBLE"), # aircraft-specific + check_or_create("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", 0.0, "DOUBLE"), # aircraft-specific + check_or_create("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", 0.0, "DOUBLE"), # aircraft-specific + ], + }; + + var hitchname = aircraft_settings.aerotow.force_name_jsbsim.getValue(); + aerotow_hash.apply_force = { + magnitude: props.globals.initNode("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", 0.0, "DOUBLE"), + coord: [ + check_or_create("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", 0.0, "DOUBLE"), # aircraft-specific + check_or_create("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", 0.0, "DOUBLE"), # aircraft-specific + check_or_create("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", 0.0, "DOUBLE"), # aircraft-specific + ], + }; + + # consider older JSBSim-versions which do NOT provide the locations of external_reactions in the property tree + if( getprop("fdm/jsbsim/external_reactions/" ~ aircraft_settings.aerotow.force_name_jsbsim.getValue() ~ "/location-x-in") == nil ) { + aircraft_settings.aerotow.decoupled_locations.setBoolValue( 1 ); + } + if( getprop("fdm/jsbsim/external_reactions/" ~ aircraft_settings.winch.force_name_jsbsim.getValue() ~ "/location-x-in") == nil ) { + aircraft_settings.winch.decoupled_locations.setBoolValue( 1 ); + } + + + setlistener(aircraft_settings.winch.force_name_jsbsim , func() { + var hitchname = aircraft_settings.winch.force_name_jsbsim.getValue(); + set_force_apply( winch_hash, hitchname ); + }); + setlistener(aircraft_settings.aerotow.force_name_jsbsim , func() { + var hitchname = aircraft_settings.aerotow.force_name_jsbsim.getValue(); + set_force_apply( aerotow_hash, hitchname ); + }); +} + + +var callsign = check_or_create("sim/multiplay/callsign", "", "STRING"); # session-specific + +var message_pilot = props.globals.getNode("sim/messages/pilot", 1); +var message_ai = props.globals.getNode("sim/messages/ai-plane", 1); +var message_atc = props.globals.getNode("sim/messages/atc", 1); +var write_message = func( type, message ){ + if( type == "launch-signaller" ){ + if( config.winch.messages.launch_signaller.getBoolValue() ) message_atc.setValue( message ); + } elsif( type == "winch-driver" ){ + if( config.winch.messages.winch_driver.getBoolValue() ) message_ai.setValue( message ); + } elsif( type == "pilot" ){ + if( config.winch.messages.pilot.getBoolValue() ) message_pilot.setValue( message ); + } elsif( type == "remote-ac" ){ + if( config.winch.messages.remote_ac.getBoolValue() ) message_ai.setValue( message ); + } elsif( type == "system" ){ + message_atc.setValue( message ); + } else { + screen.log.write( message ~ " [type: unknown]"); + } +} + +var delta_time = props.globals.getNode("sim/time/delta-sec", 1); + +var orientation = [ + props.globals.getNode("orientation/heading-deg", 1), + props.globals.getNode("orientation/roll-deg", 1), + props.globals.getNode("orientation/pitch-deg", 1), + ]; + +var wind_from = check_or_create("environment/wind-from-heading-deg", 0.0, "DOUBLE"); +var wind_speed_kt = check_or_create("environment/wind-speed-kt", 0.0, "DOUBLE"); + +var set_force_apply = func ( hash, hitchname ){ + hash.apply_force = { + magnitude: check_or_create("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", 0.0, "DOUBLE"), + coord: [ + check_or_create("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", 0.0, "DOUBLE"), + check_or_create("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", 0.0, "DOUBLE"), + check_or_create("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", 0.0, "DOUBLE"), + ], + }; +} + + +var loss_of_power = func () { + # The maximum power is lost to 1/10 of it over a time span of 3 +- 1 seconds + interpolate( config.winch.max_power, config.winch.max_power.getDoubleValue() / 10, 2 + 2 * rand() ); + # Afterwards it is completely lost after another 2 +- 1 seconds + settimer( func() { + interpolate( config.winch.max_power, 0, 1 + 2 * rand() ); + }, 3); +} + # ###################################################################################################################### # main function # ###################################################################################################################### var towing = func { - - #print("function towing is running"); - - var FT2M = 0.30480; - var M2FT = 1 / FT2M; - var dt = 0; - - # ------------------------------- aerotow part ------------------------------- - - var open = getprop("sim/hitches/aerotow/open"); - var oldOpen = getprop("sim/hitches/aerotow/oldOpen"); - - if ( open != oldOpen ) { # check if my hitch state has changed, if yes: message - #print("state has changed: open=",open," oldOpen=",oldOpen); - - if ( !open ) { # my hitch was open and is closed now - if ( getprop("sim/flight-model") == "jsb" ) { - var distance = getprop("sim/hitches/aerotow/tow/dist"); - var towlength_m = getprop("sim/hitches/aerotow/tow/length"); - if ( distance > towlength_m * 1.0001 ) { - setprop("sim/messages/pilot", sprintf("Could not lock hitch (tow length is insufficient) on hitch %i!", - getprop("sim/hitches/aerotow/tow/connected-to-mp-node"))); - props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(1); # open my hitch again - } # mp aircraft to far away - else { # my hitch is closed - setprop("sim/messages/pilot", sprintf("Locked hitch aerotow %i!", - getprop("sim/hitches/aerotow/tow/connected-to-mp-node"))); - } - props.globals.getNode("sim/hitches/aerotow/broken").setBoolValue(0); - } # end: JSBSim - if ( !getprop("sim/hitches/aerotow/open") ) { - # setup ai-towrope - createTowrope("aerotow"); - - # set default hitch coordinates (needed for Ai- and non-interactive MP aircraft) - setAIObjectDefaults() ; - } - } # end hitch is closed - - if ( open ) { # my hitch is now open - if ( getprop("sim/flight-model") == "jsb" ) { - if ( getprop("sim/hitches/aerotow/broken") ) { - setprop("sim/messages/pilot", sprintf("Oh no, the tow is broken")); - } - else { - setprop("sim/messages/pilot", sprintf("Opened hitch aerotow %i!", - getprop("sim/hitches/aerotow/tow/connected-to-mp-node"))); - } - releaseHitch("aerotow"); # open=1 / forces=0 - } # end: JSBSim - removeTowrope("aerotow"); # remove towrope model - } # end hitch is open - - setprop("sim/hitches/aerotow/oldOpen",open); - } # end hitch state has changed - - if (!open ) { - aerotow(open); - } # end hitch is closed (open == 0) - else { # my hitch is open - var mp_auto_connect_period = props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period").getValue(); - if ( mp_auto_connect_period != 0 ) { # if auto-connect - if ( getprop("sim/flight-model") == "jsb" ) { # only for JSBSim aircraft - findBestAIObject(); - } # end JSBSim aircraft - dt = mp_auto_connect_period; - #print("towing: running as auto connect with period=",dt); - } # end if auto-connect - else { # my hitch is open and not auto-connect - dt = 0; - } - } - - - # ------------------------------- winch part ------------------------------- - - var winchopen = getprop("sim/hitches/winch/open"); - var wincholdOpen = getprop("sim/hitches/winch/oldOpen"); - - if ( winchopen != wincholdOpen ) { # check if my hitch state has changed, if yes: message - #print("winch state has changed: open=",winchopen," oldOpen=",wincholdOpen); - if ( !winchopen ) { # my hitch was open and is closed now - if ( getprop("sim/flight-model") == "jsb" ) { - var distance = getprop("sim/hitches/winch/tow/dist"); - var towlength_m = getprop("sim/hitches/winch/tow/length"); - if ( distance > towlength_m ) { - setprop("sim/messages/pilot", sprintf("Could not lock hitch (tow length is insufficient) on hitch %i!", - getprop("sim/hitches/aerotow/tow/connected-to-mp-node"))); - props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(1); # open my hitch again - } # mp aircraft to far away - else { # my hitch is closed - setprop("sim/messages/pilot", sprintf("Locked hitch winch %i!", - getprop("sim/hitches/aerotow/tow/connected-to-mp-node"))); - setprop("sim/hitches/winch/winch/clutched","false"); - } - props.globals.getNode("sim/hitches/winch/broken").setBoolValue(0); - props.globals.getNode("sim/hitches/winch/winch/actual-spool-speed-m-s").setValue(0.); - } # end: JSBSim - if ( !getprop("sim/hitches/winch/open") ) { - # setup ai-towrope - createTowrope("winch"); - - # set default hitch coordinates (needed for Ai- and non-interactive MP aircraft) - setAIObjectDefaults() ; - } - } # end hitch is closed - - if ( winchopen ) { # my hitch is now open - if ( getprop("sim/flight-model") == "jsb" ) { - if ( getprop("sim/hitches/winch/broken") ) { - setprop("sim/messages/pilot", sprintf("Oh no, the tow is broken")); - } - releaseHitch("winch"); - } # end: JSBSim - pull_in_rope(); - } # end hitch is open - - setprop("sim/hitches/winch/oldOpen",winchopen); - } # end hitch state has changed - - if (!winchopen ) { - winch(winchopen); - } - - settimer( towing, dt ); - + + + var dt = 0; + + # ------------------------------- aerotow part ------------------------------- + + var open = aerotow_hash.open.getBoolValue(); + var oldOpen = aerotow_hash.old_open.getBoolValue(); + + if ( open != oldOpen ) { # check if my hitch state has changed, if yes: message + + if ( !open ) { # my hitch was open and is closed now + if ( fdm == "jsb" ) { + var distance = aerotow_hash.tow.dist.getDoubleValue(); + var towlength_m = aerotow_hash.tow.length.getDoubleValue(); + if ( distance > towlength_m * 1.0001 ) { + write_message( "system", sprintf("Could not lock hitch (tow length is insufficient) on hitch %i!", aerotow_hash.tow.conn_mp_node.getIntValue()) ); + aerotow_hash.open.setBoolValue( 1 ); # open my hitch again + } else { # mp aircraft to far away + # my hitch is closed + write_message( "system", sprintf("Locked hitch aerotow %i!", aerotow_hash.tow.conn_mp_node.getIntValue()) ); + } + aerotow_hash.broken.setBoolValue(0); + } # end: JSBSim + if ( !aerotow_hash.open.getBoolValue() ) { + # setup ai-towrope + createTowrope("aerotow"); + + # set default hitch coordinates (needed for Ai- and non-interactive MP aircraft) + setAIObjectDefaults() ; + } + } # end hitch is closed + + if ( open ) { # my hitch is now open + if ( fdm == "jsb" ) { + if ( aerotow_hash.broken.getBoolValue() ) { + #getprop("sim/hitches/aerotow/broken") + write_message( "pilot", "Oh no, the tow is broken" ); + } else { + write_message( "pilot", sprintf("Opened hitch aerotow %i!", aerotow_hash.tow.conn_mp_node.getIntValue() ) ); + } + releaseHitch("aerotow"); # open=1 / forces=0 + } # end: JSBSim + removeTowrope("aerotow"); # remove towrope model + } # end hitch is open + + aerotow_hash.old_open.setBoolValue( open ); + } # end hitch state has changed + + if (!open ) { + aerotow(open); + # end hitch is closed (open == 0) + } else { + # my hitch is open + var mp_auto_connect_period = aerotow_hash.mp_auto_connect_period.getDoubleValue(); + if ( mp_auto_connect_period != 0 ) { # if auto-connect + if ( fdm == "jsb" ) { # only for JSBSim aircraft + findBestAIObject(); + } # end JSBSim aircraft + dt = mp_auto_connect_period; + } else { # my hitch is open and not auto-connect + dt = 0; + } + } + + # winch part + if (!winch_hash.open.getBoolValue() ) { + winch(winch_hash.open.getBoolValue()); + } + + settimer( towing, dt ); + } # end towing +setlistener( winch_hash.open, func { + var winchopen = winch_hash.open.getBoolValue(); + var wincholdOpen = winch_hash.old_open.getBoolValue(); + + if ( winchopen != wincholdOpen ) { # check if my hitch state has changed, if yes: message + if ( !winchopen ) { # my hitch was open and is closed now + if ( fdm == "jsb" ) { + var distance = winch_hash.tow.dist.getDoubleValue(); + var towlength_m = winch_hash.tow.length.getDoubleValue(); + if ( distance > towlength_m ) { + write_message( "system", sprintf("Could not lock hitch (tow length is insufficient) on hitch %i!", aerotow_hash.tow.conn_mp_node.getIntValue() ) ); + winch_hash.open.setBoolValue( 1 ); # open my hitch again + } else { # mp aircraft to far away + # my hitch is closed + write_message( "system", sprintf("Locked hitch winch %i!", aerotow_hash.tow.conn_mp_node.getIntValue() ) ); + winch_hash.clutched.setBoolValue( 0 );; + } + winch_hash.broken.setBoolValue( 0 ); + winch_hash.actual_spool_speed.setDoubleValue( 0.0 ); + } # end: JSBSim + if ( !winch_hash.open.getBoolValue() ) { + # setup ai-towrope + createTowrope("winch"); + + # set default hitch coordinates (needed for Ai- and non-interactive MP aircraft) + setAIObjectDefaults() ; + } + } # end hitch is closed + + if ( winchopen ) { # my hitch is now open + if ( fdm == "jsb" ) { + if ( winch_hash.broken.getBoolValue() ) { + write_message( "pilot", "Oh no, the tow is broken" ); + } + releaseHitch("winch"); + } # end: JSBSim + pull_in_rope(); + } # end hitch is open + + winch_hash.old_open.setBoolValue( winchopen ); + } # end hitch state has changed + +}); + +setlistener( aircraft_settings.winch.type_p, func { + winch_hash.type = aircraft_settings.winch.type_p.getIntValue(); +}); + # ###################################################################################################################### # find best AI object # ###################################################################################################################### var findBestAIObject = func (){ - - # the nearest found plane, that is close enough will be used - # set some default variables, needed later to identify if the found object is - # an AI-Object, a "non-interactiv MP-Object or an interactive MP-Object - - # local variables - var aiobjects = []; # keeps the ai-planes from the property tree - var aiPosition = geo.Coord.new(); # current processed ai-plane - var myPosition = geo.Coord.new(); # coordinates of glider - var distance_m = 0; # distance to ai-plane - - var FT2M = 0.30480; - - var nodeIsAiAircraft = 0; - var nodeIsMpAircraft = 0; - var running_as_autoconnect = 0; - var mp_open_last_state = 0; - var isSlave = 0; - - if ( getprop("sim/flight-model") == "yasim" ) return; # bypass this routine for Yasim-aircraft - - #print("findBestAIObject"); - - if (props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period").getValue() != 0 ) { - var running_as_autoconnect = 1; - #print("findBestAIObject: running as auto connect"); - } - - var towlength_m = props.globals.getNode("sim/hitches/aerotow/tow/length").getValue(); - - var bestdist_m = towlength_m; # initial value - - myPosition = geo.aircraft_position(); - # todo: calculate exact hitch position - - if( running_as_autoconnect ) { - var mycallsign = props.globals.getNode("sim/multiplay/callsign").getValue(); - #print('mycallsign=',mycallsign); - } - - var found = 0; - aiobjects = props.globals.getNode("ai/models").getChildren(); - foreach (var aimember; aiobjects) { - if ( (var node = aimember.getName() ) != nil ) { - nodeIsAiAircraft = 0; - nodeIsMpAircraft = 0; - if ( sprintf("%8s",node) == "aircraft" or sprintf("%7s",node) == "dragger" ) nodeIsAiAircraft = 1; - if ( sprintf("%11s",node) == "multiplayer" ) nodeIsMpAircraft = 1; - #print("found NodeName=",node," nodeIsAiAircraft=",nodeIsAiAircraft," nodeIsMpAircraft=",nodeIsMpAircraft); - if ( aimember.getNode("valid") == nil ) continue; - if ( !nodeIsAiAircraft and !nodeIsMpAircraft) continue; - if ( !aimember.getNode("valid").getValue() ) continue; # node is invalid - - if( running_as_autoconnect ) { - if ( !nodeIsMpAircraft ) continue; - #if ( aimember.getValue("sim/hitches/aerotow/open") == nil ) continue; # this node MUST exist for mp-aircraft which want to be towed - #if ( aimember.getValue("sim/hitches/aerotow/open") == 1 ) continue; # if mp hook open, auto-connect is NOT possible - if ( aimember.getValue("sim/hitches/aerotow/open") != 0 ) continue; - if (mycallsign != aimember.getValue("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign") ) continue ; # I am the wrong one - if ( !getprop("sim/hitches/aerotow/mp_oldOpen") ) continue; # this prevents an unwanted immediate auto-connect after the dragger - # released its hitch. Firstly wait for a reported "open" hitch from glider - } - - var lat_deg = aimember.getNode("position/latitude-deg").getValue(); - var lon_deg = aimember.getNode("position/longitude-deg").getValue(); - var alt_m = aimember.getNode("position/altitude-ft").getValue() * FT2M; - - var aiPosition = geo.Coord.set_latlon( lat_deg, lon_deg, alt_m ); - distance_m = (myPosition.distance_to(aiPosition)); - #print('distance_m=',distance_m,' bestdist_m=',bestdist_m); - if ( distance_m < bestdist_m ) { - bestdist_m = distance_m; - - var towEndNode = node; - var nodeID = aimember.getNode("id").getValue(); - var aicallsign = aimember.getNode("callsign").getValue(); - #print('nodeId=',nodeID,' AiCallsign=',aicallsign); - - #set properties - props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(0); - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-node").setBoolValue(nodeIsAiAircraft); - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-mp-node").setBoolValue(nodeIsMpAircraft); - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign").setValue(aicallsign); - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id").setIntValue(nodeID); - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-property-node").setBoolValue(1); - props.globals.getNode("sim/hitches/aerotow/tow/node").setValue(towEndNode); - props.globals.getNode("sim/hitches/aerotow/tow/dist").setValue(bestdist_m); - props.globals.getNode("sim/hitches/aerotow/tow/mp_last_reported_dist", 1).setValue(0.); - - # Set some dummy values. In case of an "interactive"-MP plane - # the correct values will be transmitted in the following loop - aimember.getNode("sim/hitches/aerotow/local-pos-x",1).setValue(-5.); - aimember.getNode("sim/hitches/aerotow/local-pos-y",1).setValue(0.); - aimember.getNode("sim/hitches/aerotow/local-pos-z",1).setValue(0.); - aimember.getNode("sim/hitches/aerotow/tow/dist",1).setValue(-1.); - - found = 1; - } # end distance_m < bestdist_m - } # end node != nil - } # end loop aiobjects - if (found) { - if ( !running_as_autoconnect) { - setprop("sim/messages/pilot", sprintf("%s, I am on your hook, distance %4.3f meter.",aicallsign,bestdist_m)); - } - else { - setprop("sim/messages/ai-plane", sprintf("%s: I am on your hook, distance %4.3f meter.",aicallsign,bestdist_m )); - } - if ( running_as_autoconnect ) { - isSlave = 1; - props.globals.getNode("sim/hitches/aerotow/is-slave").setBoolValue(isSlave); - } - - props.globals.getNode("sim/hitches/aerotow/mp_oldOpen").setBoolValue(1); - - } # end: if found - else { - if (!running_as_autoconnect) { - setprop("sim/messages/atc", sprintf("Sorry, no aircraft for aerotow!")); - } - else{ - #print("auto-connect: found=0"); - props.globals.getNode("sim/hitches/aerotow/mp_oldOpen").setBoolValue(1); - } - } - + + # the nearest found plane, that is close enough will be used + # set some default variables, needed later to identify if the found object is + # an AI-Object, a "non-interactiv MP-Object or an interactive MP-Object + + # local variables + var aiobjects = []; # keeps the ai-planes from the property tree + var aiPosition = geo.Coord.new(); # current processed ai-plane + var myPosition = geo.Coord.new(); # coordinates of glider + var distance_m = 0; # distance to ai-plane + + var nodeIsAiAircraft = 0; + var nodeIsMpAircraft = 0; + var running_as_autoconnect = 0; + var mp_open_last_state = 0; + var isSlave = 0; + + if ( fdm == "yasim" ) return; # bypass this routine for Yasim-aircraft + + + if (aerotow_hash.mp_auto_connect_period.getDoubleValue() != 0 ) { + var running_as_autoconnect = 1; + } + + var towlength_m = aerotow_hash.tow.length.getDoubleValue(); + + var bestdist_m = towlength_m; # initial value + + myPosition = geo.aircraft_position(); + # todo: calculate exact hitch position + + if( running_as_autoconnect ) { + var mycallsign = callsign.getValue(); + } + + var found = 0; + aiobjects = props.globals.getNode("ai/models").getChildren(); + foreach (var aimember; aiobjects) { + if ( (var node = aimember.getName() ) != nil ) { + nodeIsAiAircraft = 0; + nodeIsMpAircraft = 0; + if ( sprintf("%8s",node) == "aircraft" or sprintf("%7s",node) == "dragger" ) nodeIsAiAircraft = 1; + if ( sprintf("%11s",node) == "multiplayer" ) nodeIsMpAircraft = 1; + if ( aimember.getNode("valid") == nil ) continue; + if ( !nodeIsAiAircraft and !nodeIsMpAircraft) continue; + if ( !aimember.getNode("valid").getValue() ) continue; # node is invalid + + if( running_as_autoconnect ) { + if ( !nodeIsMpAircraft ) continue; + #if ( aimember.getValue("sim/hitches/aerotow/open") == nil ) continue; # this node MUST exist for mp-aircraft which want to be towed + #if ( aimember.getValue("sim/hitches/aerotow/open") == 1 ) continue; # if mp hook open, auto-connect is NOT possible + if ( aimember.getValue("sim/hitches/aerotow/open") != 0 ) continue; + if (mycallsign != aimember.getValue("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign") ) continue ; # I am the wrong one + if ( !aerotow_hash.mp_old_open.getBoolValue() ) continue; # this prevents an unwanted immediate auto-connect after the dragger + # released its hitch. Firstly wait for a reported "open" hitch from glider + } + + var lat_deg = aimember.getNode("position/latitude-deg").getValue(); + var lon_deg = aimember.getNode("position/longitude-deg").getValue(); + var alt_m = aimember.getNode("position/altitude-ft").getValue() * FT2M; + + var aiPosition = geo.Coord.set_latlon( lat_deg, lon_deg, alt_m ); + distance_m = (myPosition.distance_to(aiPosition)); + if ( distance_m < bestdist_m ) { + bestdist_m = distance_m; + + var towEndNode = node; + var nodeID = aimember.getNode("id").getValue(); + var aicallsign = aimember.getNode("callsign").getValue(); + + #set properties + aerotow_hash.open.setBoolValue( 0 ); + aerotow_hash.tow.conn_ai_node.setBoolValue( nodeIsAiAircraft ); + aerotow_hash.tow.conn_mp_node.setBoolValue( nodeIsMpAircraft ); + aerotow_hash.tow.conn_ai_or_mp_callsign.setValue( aicallsign ); + aerotow_hash.tow.conn_ai_or_mp_id.setIntValue( nodeID ); + aerotow_hash.tow.conn_prop_node.setBoolValue( 1 ); + aerotow_hash.tow.node.setValue( towEndNode ); + aerotow_hash.tow.dist.setDoubleValue( bestdist_m ); + aerotow_hash.tow.mp_last_reported_dist.setDoubleValue( 0.0 ); + + # Set some dummy values. In case of an "interactive"-MP plane + # the correct values will be transmitted in the following loop + aimember.getNode("sim/hitches/aerotow/local-pos-x",1).setValue(-5.); + aimember.getNode("sim/hitches/aerotow/local-pos-y",1).setValue(0.); + aimember.getNode("sim/hitches/aerotow/local-pos-z",1).setValue(0.); + aimember.getNode("sim/hitches/aerotow/tow/dist",1).setValue(-1.); + + found = 1; + } # end distance_m < bestdist_m + } # end node != nil + } # end loop aiobjects + + if (found) { + if ( !running_as_autoconnect) { + write_message( "pilot", sprintf("%s, I am on your hook, distance %4.3f meter.",aicallsign,bestdist_m) ); + } else { + write_message( "remote-ac", sprintf("%s: I am on your hook, distance %4.3f meter.",aicallsign,bestdist_m ) ); + } + if ( running_as_autoconnect ) { + isSlave = 1; + aerotow_hash.is_slave.setBoolValue( isSlave ); + } + + aerotow_hash.mp_old_open.setBoolValue( 1 ); + + } # end: if found + else { + if (!running_as_autoconnect) { + write_message( "system", "Sorry, no aircraft for aerotow!" ); + } else{ + aerotow_hash.old_open.setBoolValue( 1 ); + } + } + } # End function findBestAIObject @@ -694,310 +886,293 @@ towing(); # ###################################################################################################################### var aerotow = func (open){ - - #print("function aerotow is running"); - -# if (!open ) { - - ########################################### my hitch position ############################################ - - myPosition = geo.aircraft_position(); - var my_head_deg = getprop("orientation/heading-deg"); - var my_roll_deg = getprop("orientation/roll-deg"); - var my_pitch_deg = getprop("orientation/pitch-deg"); - - # hook coordinates in Yasim-system (x-> nose / y -> left wing / z -> up) - assignHitchLocations("aerotow"); - var x = getprop("sim/hitches/aerotow/local-pos-x"); - var y = getprop("sim/hitches/aerotow/local-pos-y"); - var z = getprop("sim/hitches/aerotow/local-pos-z"); - - var alpha_deg = my_roll_deg * (1.); # roll clockwise (looking in x-direction) := + - var beta_deg = my_pitch_deg * (-1.); # pitch clockwise (looking in y-direction) := - - - # transform hook coordinates - var Xn = PointRotate3D(x:x,y:y,z:z,xr:0.,yr:0.,zr:0.,alpha_deg:alpha_deg,beta_deg:beta_deg,gamma_deg:0.); - - var install_distance_m = Xn[0]; # in front of ref-point of glider - var install_side_m = Xn[1]; - var install_alt_m = Xn[2]; - - var myHitch_pos = myPosition.apply_course_distance( my_head_deg , install_distance_m ); - var myHitch_pos = myPosition.apply_course_distance( my_head_deg - 90. , install_side_m ); - myHitch_pos.set_alt(myPosition.alt() + install_alt_m); - - ########################################### ai hitch position ############################################ - - #var aiNodeID = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id"); # id of former found ai/mp aircraft - #print("aiNodeID=",aiNodeID); - var aiCallsign = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign"); # callsign of former found ai/mp aircraft - - var found = 0; - - aiobjects = props.globals.getNode("ai/models").getChildren(); - foreach (var aimember; aiobjects) { - if ( (var c = aimember.getNode("id") ) != nil ) { - if ( !aimember.getNode("valid").getValue() ) continue; # node is invalid - - # Identifying the MP-aircraft by its node-id works fine with JSBSim-aircraft but NOT with YASim. - # In YASim the node-id is not updated which could lead to complications (e.g. node-id changes after "Pause" or "Exit"). - #var testprop = c.getValue(); - #if ( testprop == aiNodeID) { - - # Identifying the MP-aircraft by its callsign works fine with JSBSim AND YASim-aircraft - var testprop = aimember.getNode("callsign").getValue(); - if ( testprop == aiCallsign ) { - - found = found + 1; - - ###################### check status of ai hitch ###################### - if ( getprop("sim/flight-model") == "jsb" ) { - # check if the multiplayer hitch state has changed - # this trick avoids immediate opening after locking because MP-aircraft has not yet reported a locked hitch - if ( (var d = aimember.getNode("sim/hitches/aerotow/open") ) != nil ) { - var mpOpen = aimember.getNode("sim/hitches/aerotow/open").getValue(); - var mp_oldOpen = getprop("sim/hitches/aerotow/mp_oldOpen"); - #print('mpOpen=',mpOpen,' mp_oldOpen=',mp_oldOpen); - if ( mpOpen != mp_oldOpen ) { # state has changed: was open and is now locked OR was locked and is now open - if ( mpOpen ) { - setprop("sim/messages/ai-plane", sprintf("%s: I have released the tow!",getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign")) ); - releaseHitch("aerotow"); # my open=1 / forces=0 / remove towrope - } # end: open - props.globals.getNode("sim/hitches/aerotow/mp_oldOpen").setBoolValue(mpOpen); - } # end: state has changed - } # end: node is available - } #end : JSBSim - ######################################################################## - - # get coordinates - var ai_lat = aimember.getNode("position/latitude-deg").getValue(); - var ai_lon = aimember.getNode("position/longitude-deg").getValue(); - var ai_alt = (aimember.getNode("position/altitude-ft").getValue()) * FT2M; - #print("ai_lat,lon,alt",ai_lat,ai_lon,ai_alt); - - var ai_pitch_deg = aimember.getNode("orientation/pitch-deg").getValue(); - var ai_roll_deg = aimember.getNode("orientation/roll-deg").getValue(); - var ai_head_deg = aimember.getNode("orientation/true-heading-deg").getValue(); - - var aiHitchX = aimember.getNode("sim/hitches/aerotow/local-pos-x").getValue(); - var aiHitchY = aimember.getNode("sim/hitches/aerotow/local-pos-y").getValue(); - var aiHitchZ = aimember.getNode("sim/hitches/aerotow/local-pos-z").getValue(); - - var aiPosition = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt ); - - var alpha_deg = ai_roll_deg * (1.); - var beta_deg = ai_pitch_deg * (-1.); - - # transform hook coordinates - var Xn = PointRotate3D(x:aiHitchX,y:aiHitchY,z:aiHitchZ,xr:0.,yr:0.,zr:0.,alpha_deg:alpha_deg,beta_deg:beta_deg,gamma_deg:0.); - - var install_distance_m = Xn[0]; # in front of ref-point of glider - var install_side_m = Xn[1]; - var install_alt_m = Xn[2]; - - var aiHitch_pos = aiPosition.apply_course_distance( ai_head_deg , install_distance_m ); - var aiHitch_pos = aiPosition.apply_course_distance( ai_head_deg - 90. , install_side_m ); - aiHitch_pos.set_alt(aiPosition.alt() + install_alt_m); - - ########################################### distance between hitches ##################################### - - var distance = (myHitch_pos.direct_distance_to(aiHitch_pos)); # distance to plane in meter - var aiHitchheadto = (myHitch_pos.course_to(aiHitch_pos)); - var height = myHitch_pos.alt() - aiHitch_pos.alt(); - - var aiHitchpitchto = -math.asin((myHitch_pos.alt()-aiHitch_pos.alt())/distance) / 0.01745; - #print(" pitch: ", aiHitchpitchto); - - # update position of rope - setprop("ai/models/aerotowrope/position/latitude-deg", myHitch_pos.lat()); - setprop("ai/models/aerotowrope/position/longitude-deg", myHitch_pos.lon()); - setprop("ai/models/aerotowrope/position/altitude-ft", myHitch_pos.alt() * M2FT); - #print("ai_lat,lon,alt",myHitch_pos.lat()," ",myHitch_pos.lon()," ",myHitch_pos.alt() ); - - # update pitch and heading of rope - setprop("ai/models/aerotowrope/orientation/true-heading-deg", aiHitchheadto); - setprop("ai/models/aerotowrope/orientation/pitch-deg", aiHitchpitchto); - - # update length of rope - setprop("sim/hitches/aerotow/tow/dist", distance); - #print("distance=",distance); - - - ############################################# calc forces ################################################## - - # calc forces only for JSBSim-aircraft - - # tow-end-forces must be reported in N to be consiststent to Yasim-aircraft - # hitch-forces must be LBS to be consistent to the JSBSim "external_forces/.../magnitude" definition - - if ( getprop("sim/flight-model") == "jsb" ) { - #print("Force-Routine"); - - # check if the MP-aircraft properties have been updated. If not (maybe due to time-lag) bypass force calculation (use previous forces instead) - var mp_reported_dist = aimember.getNode("sim/hitches/aerotow/tow/dist").getValue(); - var mp_last_reported_dist = getprop("sim/hitches/aerotow/tow/mp_last_reported_dist"); - var mp_delta_reported_dist = mp_reported_dist - mp_last_reported_dist ; - setprop("sim/hitches/aerotow/tow/mp_last_reported_dist",mp_reported_dist); - var mp_delta_reported_dist2 = mp_delta_reported_dist * mp_delta_reported_dist ; # we need the absolute value - if ( (mp_delta_reported_dist2 > 0.0000001) or (mp_reported_dist < 0. )){ # we have the updated MP coordinates (no time lag) - # or the MP-aircraft is a non-interactive mp plane (mp_reported_dist = -1) - # => update forces else use the old forces! - - var breakforce_N = getprop("sim/hitches/aerotow/tow/brake-force"); # could be different in both aircraft - - var isSlave = getprop("sim/hitches/aerotow/is-slave"); - if ( !isSlave ){ # if we are master, we have to calculate the forces - #print("master: calc forces"); - var elastic_constant = getprop("sim/hitches/aerotow/tow/elastic-constant"); - var towlength_m = getprop("sim/hitches/aerotow/tow/length"); - - var delta_towlength_m = distance - towlength_m; - #print("towlength_m= ", towlength_m , " elastic_constant= ", elastic_constant," delta_towlength_m= ", delta_towlength_m); - - if ( delta_towlength_m < 0. ) { - var forcetow_N = 0.; - } - else{ - var forcetow_N = elastic_constant * delta_towlength_m / towlength_m; - } - } # end !isSlave - else { # we are slave and get the forces from master - #print("slave: get forces"," aimember=",aimember.getName()); - # get forces - var forcetowX_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-x").getValue() * 1; - var forcetowY_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-y").getValue() * 1; - var forcetowZ_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-z").getValue() * 1; - var forcetow_N = math.sqrt( forcetowX_N * forcetowX_N + forcetowY_N * forcetowY_N + forcetowZ_N * forcetowZ_N ); - } # end isSlave - - var forcetow_LBS = forcetow_N * 0.224809; # N -> LBF - #print(" forcetow_N ", forcetow_N , " distance ", distance," ", breakforce_N); - - if ( forcetow_N < breakforce_N ) { - - var distancepr = (myHitch_pos.distance_to(aiHitch_pos)); - - # correct a failure, if the projected length is larger than direct length - if (distancepr > distance) { distancepr = distance;} - - var alpha = math.acos( (distancepr / distance) ); - if ( aiHitch_pos.alt() > myHitch_pos.alt()) alpha = - alpha; - - var beta = ( aiHitchheadto - my_head_deg ) * 0.01745; - var gamma = my_pitch_deg * 0.01745; - var delta = my_roll_deg * 0.01745; - - var sina = math.sin(alpha); - var cosa = math.cos(alpha); - var sinb = math.sin(beta); - var cosb = math.cos(beta); - var sing = math.sin(gamma); - var cosg = math.cos(gamma); - var sind = math.sin(delta); - var cosd = math.cos(delta); - - #var forcetow = forcetow_N; # we deliver N to JSBSim - var forcetow = forcetow_LBS; # we deliver LBS to JSBSim - - # calculate unit vector of force direction in JSBSim-system - var force = 1; - - # global forces: alpha beta - var fglobalx = force * cosa * cosb; - var fglobaly = force * cosa * sinb; - var fglobalz = force * sina; - - # local forces by pitch: gamma - var flpitchx = fglobalx * cosg - fglobalz * sing; - var flpitchy = fglobaly; - var flpitchz = fglobalx * sing + fglobalz * cosg; - - # local forces by roll: delta - var flrollx = flpitchx; - var flrolly = flpitchy * cosd + flpitchz * sind; - var flrollz = - flpitchy * sind + flpitchz * cosd; - - # asigning to LOCAL coord of plane - var forcex = flrollx; - var forcey = flrolly; - var forcez = flrollz; - #print("fx=",forcex," fy=",forcey," fz=",forcez); - - # JSBSim-body-frame: x-> nose / y -> right wing / z -> down - # apply forces to hook (forces are in LBS or N see above) - var hitchname = getprop("sim/hitches/aerotow/force_name_jsbsim"); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", forcetow); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", forcex); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", forcey); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", forcez); - - } # end force < break force - else { # rope is broken - props.globals.getNode("sim/hitches/aerotow/broken").setBoolValue(1); - #setprop("sim/messages/atc", sprintf("Oh no, the tow is broken")); - releaseHitch("aerotow"); # open=1 / forces=0 / remove towrope - } - - ############################################# report forces ############################################## - - # if we are connected to a MP-aircraft and master - var nodeIsMpAircraft = getprop("sim/hitches/aerotow/tow/connected-to-mp-node"); - if ( nodeIsMpAircraft and !isSlave ){ - #print("report Forces"); - - # transform my hitch coordinates to cartesian earth coordinates - var myHitchCartEarth = geodtocart(myHitch_pos.lat(),myHitch_pos.lon(),myHitch_pos.alt() ); - var myHitchXearth_m = myHitchCartEarth[0]; - var myHitchYearth_m = myHitchCartEarth[1]; - var myHitchZearth_m = myHitchCartEarth[2]; - - # transform MP hitch coordinates to cartesian earth coordinates - var aiHitchCartEarth = geodtocart(aiHitch_pos.lat(),aiHitch_pos.lon(),aiHitch_pos.alt() ); - var aiHitchXearth_m = aiHitchCartEarth[0]; - var aiHitchYearth_m = aiHitchCartEarth[1]; - var aiHitchZearth_m = aiHitchCartEarth[2]; - - # calculate normal vector in tow direction in cartesian earth coordinates - var dx = aiHitchXearth_m - myHitchXearth_m; - var dy = aiHitchYearth_m - myHitchYearth_m; - var dz = aiHitchZearth_m - myHitchZearth_m; - var dl = math.sqrt( dx * dx + dy * dy + dz * dz ); - - var forcetowX_N = forcetow_N * dx / dl; - var forcetowY_N = forcetow_N * dy / dl; - var forcetowZ_N = forcetow_N * dz / dl; - - setprop("sim/hitches/aerotow/tow/dist", distance); - setprop("sim/hitches/aerotow/tow/end-force-x", -forcetowX_N); # force acts in - setprop("sim/hitches/aerotow/tow/end-force-y", -forcetowY_N); # opposite direction - setprop("sim/hitches/aerotow/tow/end-force-z", -forcetowZ_N); # at tow end - - } # end report forces - - } # end: timelag - else{ - #print("forces NOT updated!"); - } - } # end forces/JSBSim - - - } # end: aiNodeID - } # end: check id != nil - } # end: loop over aiobjects - - if ( found == 0 ) { - if ( getprop("sim/flight-model") == "jsb" ) { - setprop("sim/messages/atc", sprintf("MP-aircraft disappeared!" )); - props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(1); # open my hitch - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id").setIntValue(0); - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign").setValue(""); - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-node").setBoolValue(0); - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-mp-node").setBoolValue(0); - props.globals.getNode("sim/hitches/aerotow/tow/connected-to-property-node").setBoolValue(0); - } - #if ( getprop("sim/flight-model") == "yasim" ) removeTowrope("aerotow"); # remove towrope model - } # end found=0 - + + #print("function aerotow is running"); + + # if (!open ) { + + ########################################### my hitch position ############################################ + + myPosition = geo.aircraft_position(); + var my_head_deg = orientation[0].getDoubleValue(); + var my_roll_deg = orientation[1].getDoubleValue(); + var my_pitch_deg = orientation[2].getDoubleValue(); + + # hook coordinates in Yasim-system (x-> nose / y -> left wing / z -> up) + assignHitchLocations("aerotow"); + var x = aircraft_settings.aerotow.local_pos[0].getDoubleValue(); + var y = aircraft_settings.aerotow.local_pos[1].getDoubleValue(); + var z = aircraft_settings.aerotow.local_pos[2].getDoubleValue(); + + var alpha_deg = my_roll_deg * (1.); # roll clockwise (looking in x-direction) := + + var beta_deg = my_pitch_deg * (-1.); # pitch clockwise (looking in y-direction) := - + + # transform hook coordinates + var Xn = PointRotate3D(x:x,y:y,z:z,xr:0.,yr:0.,zr:0.,alpha_deg:alpha_deg,beta_deg:beta_deg,gamma_deg:0.); + + var install_distance_m = Xn[0]; # in front of ref-point of glider + var install_side_m = Xn[1]; + var install_alt_m = Xn[2]; + + var myHitch_pos = myPosition.apply_course_distance( my_head_deg , install_distance_m ); + var myHitch_pos = myPosition.apply_course_distance( my_head_deg - 90. , install_side_m ); + myHitch_pos.set_alt(myPosition.alt() + install_alt_m); + + ########################################### ai hitch position ############################################ + + #var aiNodeID = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id"); # id of former found ai/mp aircraft + #print("aiNodeID=",aiNodeID); + var aiCallsign = aerotow_hash.tow.conn_ai_or_mp_callsign.getValue(); # callsign of former found ai/mp aircraft + + var found = 0; + + aiobjects = props.globals.getNode("ai/models").getChildren(); + foreach (var aimember; aiobjects) { + if ( (var c = aimember.getNode("id") ) != nil ) { + if ( !aimember.getNode("valid").getValue() ) continue; # node is invalid + + # Identifying the MP-aircraft by its node-id works fine with JSBSim-aircraft but NOT with YASim. + # In YASim the node-id is not updated which could lead to complications (e.g. node-id changes after "Pause" or "Exit"). + #var testprop = c.getValue(); + #if ( testprop == aiNodeID) { + + # Identifying the MP-aircraft by its callsign works fine with JSBSim AND YASim-aircraft + var testprop = aimember.getNode("callsign").getValue(); + if ( testprop == aiCallsign ) { + + found = found + 1; + + ###################### check status of ai hitch ###################### + if ( fdm == "jsb" ) { + # check if the multiplayer hitch state has changed + # this trick avoids immediate opening after locking because MP-aircraft has not yet reported a locked hitch + if ( (var d = aimember.getNode("sim/hitches/aerotow/open") ) != nil ) { + var mpOpen = aimember.getNode("sim/hitches/aerotow/open").getValue(); + var mp_oldOpen = aerotow_hash.mp_old_open.getBoolValue(); + #print('mpOpen=',mpOpen,' mp_oldOpen=',mp_oldOpen); + if ( mpOpen != mp_oldOpen ) { # state has changed: was open and is now locked OR was locked and is now open + if ( mpOpen ) { + write_message( "remote-ac", sprintf("%s: I have released the tow!", aerotow_hash.tow.conn_ai_or_mp_callsign.getValue() ) ); + releaseHitch("aerotow"); # my open=1 / forces=0 / remove towrope + } # end: open + aerotow_hash.mp_old_open.setBoolValue(mpOpen); + } # end: state has changed + } # end: node is available + } #end : JSBSim + ######################################################################## + + # get coordinates + var ai_lat = aimember.getNode("position/latitude-deg").getValue(); + var ai_lon = aimember.getNode("position/longitude-deg").getValue(); + var ai_alt = (aimember.getNode("position/altitude-ft").getValue()) * FT2M; + #print("ai_lat,lon,alt",ai_lat,ai_lon,ai_alt); + + var ai_pitch_deg = aimember.getNode("orientation/pitch-deg").getValue(); + var ai_roll_deg = aimember.getNode("orientation/roll-deg").getValue(); + var ai_head_deg = aimember.getNode("orientation/true-heading-deg").getValue(); + + var aiHitchX = aimember.getNode("sim/hitches/aerotow/local-pos-x").getValue(); + var aiHitchY = aimember.getNode("sim/hitches/aerotow/local-pos-y").getValue(); + var aiHitchZ = aimember.getNode("sim/hitches/aerotow/local-pos-z").getValue(); + + var aiPosition = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt ); + + var alpha_deg = ai_roll_deg * (1.); + var beta_deg = ai_pitch_deg * (-1.); + + # transform hook coordinates + var Xn = PointRotate3D(x:aiHitchX,y:aiHitchY,z:aiHitchZ,xr:0.,yr:0.,zr:0.,alpha_deg:alpha_deg,beta_deg:beta_deg,gamma_deg:0.); + + var install_distance_m = Xn[0]; # in front of ref-point of glider + var install_side_m = Xn[1]; + var install_alt_m = Xn[2]; + + var aiHitch_pos = aiPosition.apply_course_distance( ai_head_deg , install_distance_m ); + var aiHitch_pos = aiPosition.apply_course_distance( ai_head_deg - 90. , install_side_m ); + aiHitch_pos.set_alt(aiPosition.alt() + install_alt_m); + + ########################################### distance between hitches ##################################### + + var distance = (myHitch_pos.direct_distance_to(aiHitch_pos)); # distance to plane in meter + var aiHitchheadto = (myHitch_pos.course_to(aiHitch_pos)); + var height = myHitch_pos.alt() - aiHitch_pos.alt(); + + var aiHitchpitchto = -math.asin((myHitch_pos.alt()-aiHitch_pos.alt())/distance) / 0.01745; + #print(" pitch: ", aiHitchpitchto); + + # update position of rope + aerotow_hash.rope.lat.setDoubleValue( myHitch_pos.lat() ); + aerotow_hash.rope.lon.setDoubleValue( myHitch_pos.lon() ); + aerotow_hash.rope.alt.setDoubleValue( myHitch_pos.alt() * M2FT ); + # update orientation of rope + aerotow_hash.rope.hdg.setDoubleValue( aiHitchheadto ); + aerotow_hash.rope.pitch.setDoubleValue( aiHitchpitchto ); + + # update length of rope + aerotow_hash.tow.dist.setDoubleValue( distance ); + + + ############################################# calc forces ################################################## + + # calc forces only for JSBSim-aircraft + + # tow-end-forces must be reported in N to be consiststent to Yasim-aircraft + # hitch-forces must be LBS to be consistent to the JSBSim "external_forces/.../magnitude" definition + + if ( fdm == "jsb" ) { + + # check if the MP-aircraft properties have been updated. If not (maybe due to time-lag) bypass force calculation (use previous forces instead) + var mp_reported_dist = aimember.getNode("sim/hitches/aerotow/tow/dist").getValue(); + var mp_last_reported_dist = aerotow_hash.tow.mp_last_reported_dist.getDoubleValue(); + var mp_delta_reported_dist = mp_reported_dist - mp_last_reported_dist ; + aerotow_hash.tow.mp_last_reported_dist.setDoubleValue( mp_reported_dist ); + var mp_delta_reported_dist2 = mp_delta_reported_dist * mp_delta_reported_dist ; # we need the absolute value + if ( (mp_delta_reported_dist2 > 0.0000001) or (mp_reported_dist < 0. )){ # we have the updated MP coordinates (no time lag) + # or the MP-aircraft is a non-interactive mp plane (mp_reported_dist = -1) + # => update forces else use the old forces! + + var breakforce_N = getprop("sim/hitches/aerotow/tow/break-force"); # could be different in both aircraft + + var isSlave = aerotow_hash.is_slave.getBoolValue(); + if ( !isSlave ){ # if we are master, we have to calculate the forces + var elastic_constant = getprop("sim/hitches/aerotow/tow/elastic-constant"); + var towlength_m = getprop("sim/hitches/aerotow/tow/length"); + + var delta_towlength_m = distance - towlength_m; + + if ( delta_towlength_m < 0. ) { + var forcetow_N = 0.; + } + else{ + var forcetow_N = elastic_constant * delta_towlength_m / towlength_m; + } + } else { # we are slave and get the forces from master + var forcetowX_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-x").getValue() * 1; + var forcetowY_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-y").getValue() * 1; + var forcetowZ_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-z").getValue() * 1; + var forcetow_N = math.sqrt( forcetowX_N * forcetowX_N + forcetowY_N * forcetowY_N + forcetowZ_N * forcetowZ_N ); + } + + var forcetow_LBS = forcetow_N * 0.224809; # N -> LBF + + if ( forcetow_N < breakforce_N ) { + + var distancepr = (myHitch_pos.distance_to(aiHitch_pos)); + + # correct a failure, if the projected length is larger than direct length + if (distancepr > distance) { distancepr = distance;} + + var alpha = math.acos( (distancepr / distance) ); + if ( aiHitch_pos.alt() > myHitch_pos.alt()) alpha = - alpha; + + var beta = ( aiHitchheadto - my_head_deg ) * 0.01745; + var gamma = my_pitch_deg * 0.01745; + var delta = my_roll_deg * 0.01745; + + var sina = math.sin(alpha); + var cosa = math.cos(alpha); + var sinb = math.sin(beta); + var cosb = math.cos(beta); + var sing = math.sin(gamma); + var cosg = math.cos(gamma); + var sind = math.sin(delta); + var cosd = math.cos(delta); + + var forcetow = forcetow_LBS; # we deliver LBS to JSBSim + + # calculate unit vector of force direction in JSBSim-system + var force = 1; + + # global forces: alpha beta + var fglobalx = force * cosa * cosb; + var fglobaly = force * cosa * sinb; + var fglobalz = force * sina; + + # local forces by pitch: gamma + var flpitchx = fglobalx * cosg - fglobalz * sing; + var flpitchy = fglobaly; + var flpitchz = fglobalx * sing + fglobalz * cosg; + + # local forces by roll: delta + var flrollx = flpitchx; + var flrolly = flpitchy * cosd + flpitchz * sind; + var flrollz = - flpitchy * sind + flpitchz * cosd; + + # asigning to LOCAL coord of plane + var forcex = flrollx; + var forcey = flrolly; + var forcez = flrollz; + + # JSBSim-body-frame: x-> nose / y -> right wing / z -> down + # apply forces to hook (forces are in LBS or N see above) + var hitchname = getprop("sim/hitches/aerotow/force_name_jsbsim"); + setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", forcetow); + setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", forcex); + setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", forcey); + setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", forcez); + + } else { # rope is broken + aerotow_hash.broken.setBoolValue( 1 ); + #setprop("sim/messages/atc", sprintf("Oh no, the tow is broken")); + releaseHitch("aerotow"); # open=1 / forces=0 / remove towrope + } + + ############################################# report forces ############################################## + + # if we are connected to a MP-aircraft and master + var nodeIsMpAircraft = getprop("sim/hitches/aerotow/tow/connected-to-mp-node"); + if ( nodeIsMpAircraft and !isSlave ){ + + # transform my hitch coordinates to cartesian earth coordinates + var myHitchCartEarth = geodtocart(myHitch_pos.lat(),myHitch_pos.lon(),myHitch_pos.alt() ); + var myHitchXearth_m = myHitchCartEarth[0]; + var myHitchYearth_m = myHitchCartEarth[1]; + var myHitchZearth_m = myHitchCartEarth[2]; + + # transform MP hitch coordinates to cartesian earth coordinates + var aiHitchCartEarth = geodtocart(aiHitch_pos.lat(),aiHitch_pos.lon(),aiHitch_pos.alt() ); + var aiHitchXearth_m = aiHitchCartEarth[0]; + var aiHitchYearth_m = aiHitchCartEarth[1]; + var aiHitchZearth_m = aiHitchCartEarth[2]; + + # calculate normal vector in tow direction in cartesian earth coordinates + var dx = aiHitchXearth_m - myHitchXearth_m; + var dy = aiHitchYearth_m - myHitchYearth_m; + var dz = aiHitchZearth_m - myHitchZearth_m; + var dl = math.sqrt( dx * dx + dy * dy + dz * dz ); + + var forcetowX_N = forcetow_N * dx / dl; + var forcetowY_N = forcetow_N * dy / dl; + var forcetowZ_N = forcetow_N * dz / dl; + + setprop("sim/hitches/aerotow/tow/dist", distance); + setprop("sim/hitches/aerotow/tow/end-force-x", -forcetowX_N); # force acts in + setprop("sim/hitches/aerotow/tow/end-force-y", -forcetowY_N); # opposite direction + setprop("sim/hitches/aerotow/tow/end-force-z", -forcetowZ_N); # at tow end + + } # end report forces + + } # end: timelag + } # end forces/JSBSim + + + } # end: aiNodeID + } # end: check id != nil + } # end: loop over aiobjects + + if ( found == 0 ) { + if ( fdm == "jsb" ) { + write_message( "system", "MP-aircraft disappeared!" ); + aerotow_hash.open.setBoolValue( 1 ); # open my hitch + aerotow_hash.tow.conn_ai_or_mp_id.setIntValue( 0 ); + aerotow_hash.tow.conn_ai_or_mp_callsign.setValue( "" ); + aerotow_hash.tow.conn_ai_node.setBoolValue( 0 ); + aerotow_hash.tow.conn_mp_node.setBoolValue( 0 ); + aerotow_hash.tow.conn_prop_node.setBoolValue( 0 ); + } + #if ( fdm == "yasim" ) removeTowrope("aerotow"); # remove towrope model + } # end found=0 + } # end function aerotow @@ -1007,245 +1182,280 @@ var aerotow = func (open){ # ###################################################################################################################### var winch = func (open){ + + if (!open ) { + + + ########################################### my hitch position ############################################ + + myPosition = geo.aircraft_position(); + var my_head_deg = orientation[0].getDoubleValue(); + var my_roll_deg = orientation[1].getDoubleValue(); + var my_pitch_deg = orientation[2].getDoubleValue(); + + # hook coordinates in Yasim-system (x-> nose / y -> left wing / z -> up) + assignHitchLocations("winch"); + var x = aircraft_settings.winch.local_pos[0].getDoubleValue(); + var y = aircraft_settings.winch.local_pos[1].getDoubleValue(); + var z = aircraft_settings.winch.local_pos[2].getDoubleValue(); + + var alpha_deg = my_roll_deg * (1.); # roll clockwise (looking in x-direction) := + + var beta_deg = my_pitch_deg * (-1.); # pitch clockwise (looking in y-direction) := - + + # transform hook coordinates + var Xn = PointRotate3D(x:x,y:y,z:z,xr:0.,yr:0.,zr:0.,alpha_deg:alpha_deg,beta_deg:beta_deg,gamma_deg:0.); + + var install_distance_m = Xn[0]; # in front of ref-point of glider + var install_side_m = Xn[1]; + var install_alt_m = Xn[2]; + + var myHitch_pos = myPosition.apply_course_distance( my_head_deg , install_distance_m ); + var myHitch_pos = myPosition.apply_course_distance( my_head_deg - 90. , install_side_m ); + myHitch_pos.set_alt(myPosition.alt() + install_alt_m); + + + ############################ Check for rope breakage ############################### + var my_height = myHitch_pos.alt() - geo.elevation( myHitch_pos.lat(), myHitch_pos.lon() ); + + if( winch_hash.rope_breakage and my_height > winch_hash.rope_breakage_height_int ) { + releaseHitch("winch"); + winch_hash.rope_breakage = 0; + # The pilot shouldn't immediately be notified this was a rope breakage (to train recognizing a rope breakage) + # That's why we report the breakage and its height 5 seconds later + var temp = winch_hash.rope_breakage_height_int; + settimer( func() { + write_message( "winch-driver", "Rope broke at " ~ sprintf("%4d", math.round( temp ) ) ~ "m height" ); + }, 5); + return; + } + + ############################ Check for winch loss of power ########################## + if( winch_hash.loss_of_power.enabled and my_height > winch_hash.loss_of_power.height_int ) { + loss_of_power(); + winch_hash.loss_of_power.enabled = 0; + # The pilot shouldn't immediately be notified this was a loss of power (to train recognizing a winch loss of power) + # That's why we report the loss of power and its height 15 seconds later + var temp = winch_hash.loss_of_power.height_int; + settimer( func() { + write_message( "winch-driver", "Winch lost power at " ~ sprintf("%4d", math.round( temp ) ) ~ "m height" ); + }, 15); + return; + } + + ########################################### winch hitch position ############################################ + + # get coordinates + var winch_global_pos_x = winch_hash.global_pos[0].getDoubleValue(); + var winch_global_pos_y = winch_hash.global_pos[1].getDoubleValue(); + var winch_global_pos_z = winch_hash.global_pos[2].getDoubleValue(); + + var winch_geod = carttogeod(winch_global_pos_x, winch_global_pos_y, winch_global_pos_z); + + var ai_lat = winch_geod[0]; + var ai_lon = winch_geod[1]; + #var ai_alt = winch_geod[2] * FT2M; + var ai_alt = winch_geod[2]; + #print("ai_lat,lon,alt",ai_lat,ai_lon,ai_alt); + + var aiHitch_pos = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt ); + + + ########################################### distance between hitches ##################################### + + var distance = (myHitch_pos.direct_distance_to(aiHitch_pos)); # distance to winch in meter + var aiHitchheadto = (myHitch_pos.course_to(aiHitch_pos)); + var height = myHitch_pos.alt() - aiHitch_pos.alt(); + + var aiHitchpitchto = -math.asin((myHitch_pos.alt()-aiHitch_pos.alt())/distance) * R2D; + #print(" pitch: ", aiHitchpitchto); + + #print("Update Winch Rope"); + #print("Winch Rope Lat is "~ myHitch_pos.lat() ); + # update position of rope + winch_hash.rope.lat.setDoubleValue( myHitch_pos.lat() ); + winch_hash.rope.lon.setDoubleValue( myHitch_pos.lon() ); + winch_hash.rope.alt.setDoubleValue( myHitch_pos.alt() * M2FT ); + # update orientation of rope + winch_hash.rope.hdg.setDoubleValue( aiHitchheadto ); + winch_hash.rope.pitch.setDoubleValue( aiHitchpitchto ); + + # update length of rope + winch_hash.tow.dist.setDoubleValue( distance ); + #print("distance=",distance); + + + ############################################# calc forces ################################################## + + # calc forces only for JSBSim-aircraft + + # tow-end-forces must be reported in N to be consiststent to Yasim-aircraft + # hitch-forces must be LBS to be consistent to the JSBSim "external_forces/.../magnitude" definition + + if ( fdm == "jsb" ) { + + var spool_max = config.winch.max_spool_speed.getDoubleValue(); + var unspool_max = config.winch.max_unspool_speed.getDoubleValue(); + var max_force_N = config.winch.max_force.getDoubleValue(); + var max_power_W = config.winch.max_power.getDoubleValue() * 1000.; + var breakforce_N = aircraft_settings.winch.break_force.getDoubleValue(); + var elastic_constant = config.winch.elastic_constant.getDoubleValue(); + var towlength_m = winch_hash.tow.length.getDoubleValue(); + var max_tow_length_m = config.winch.max_tow_length_m.getDoubleValue(); + var spoolspeed = winch_hash.actual_spool_speed.getDoubleValue(); + var spool_acceleration = config.winch.spool_acceleration.getDoubleValue(); + var delta_t = delta_time.getDoubleValue(); + + #print("towlength_m= ", towlength_m , " elastic_constant= ", elastic_constant," delta_towlength_m= ", delta_towlength_m); + + var forcetow_N = winch_hash.actual_force.getDoubleValue(); + var towlength_new_m = nil; + var delta_towlength_m = nil; + + if ( winch_hash.clutched.getBoolValue() ) { + + if( winch_hash.type == 0 ){ + # Concept: force is given + forcetow_N = force_setting; + + } else if ( winch_hash.type == 1 ){ + # Concept: regulate force to reach target speed + var speed_trgt = speed_setting; + if( math.abs ( spoolspeed - speed_trgt ) > 0.5 ) { + if( speed_trgt > spoolspeed ) { + forcetow_N += acceleration_N_s * delta_t; + } else if ( speed_trgt < spoolspeed ){ + forcetow_N -= acceleration_N_s * delta_t; + } + } + } + + # New towlength is distance from hook to winch; previous towlength is stored in winch_hash.towlength_m + towlength_new_m = distance; + delta_towlength_m = -1 * ( distance - towlength_m ); + spoolspeed = delta_towlength_m / delta_t; + + } else { # un-clutched + # --- experimental --- # + towlength_new_m = towlength_m - spoolspeed * delta_t; + delta_towlength_m = distance - towlength_new_m; + + # we assume that the the winch-operator avoids tow sagging ( => rigid rope; negativ forces allowed) + forcetow_N = elastic_constant * delta_towlength_m / towlength_new_m; + + # drag of tow-rope ( magic! ) + var magic_constant = config.winch.magic_constant.getDoubleValue(); + tow_drag_N = spoolspeed * spoolspeed * math.sqrt( math.sqrt( height * height ) * max_tow_length_m ) / magic_constant ; + + # mass = tow-mass only (drum-mass ignored) + var mass_kg = max_tow_length_m * config.winch.weight_per_m_kg_m.getDoubleValue(); + + var acceleration = ( forcetow_N - tow_drag_N ) / mass_kg; + var delta_spoolspeed = acceleration * delta_t; + spoolspeed = spoolspeed - delta_spoolspeed; + if ( spoolspeed < - unspool_max ) spoolspeed = - unspool_max; + + + #if ( delta_towlength_m < 0. ) { + # forcetow_N = 0.; + #} + } - var FT2M = 0.30480; - var M2FT = 1. / FT2M; - var RAD2DEG = 57.29578; - var DEG2RAD = 1. / RAD2DEG; - - if (!open ) { - - ########################################### my hitch position ############################################ - - myPosition = geo.aircraft_position(); - var my_head_deg = getprop("orientation/heading-deg"); - var my_roll_deg = getprop("orientation/roll-deg"); - var my_pitch_deg = getprop("orientation/pitch-deg"); - - # hitch coordinates in YASim-system (x-> nose / y -> left wing / z -> up) - assignHitchLocations("winch"); - var x = getprop("sim/hitches/winch/local-pos-x"); - var y = getprop("sim/hitches/winch/local-pos-y"); - var z = getprop("sim/hitches/winch/local-pos-z"); - - var alpha_deg = my_roll_deg * (1.); # roll clockwise (looking in x-direction) := + - var beta_deg = my_pitch_deg * (-1.); # pitch clockwise (looking in y-direction) := - - - # transform hook coordinates - var Xn = PointRotate3D(x:x,y:y,z:z,xr:0.,yr:0.,zr:0.,alpha_deg:alpha_deg,beta_deg:beta_deg,gamma_deg:0.); - - var install_distance_m = Xn[0]; # in front of ref-point of glider - var install_side_m = Xn[1]; - var install_alt_m = Xn[2]; - - var myHitch_pos = myPosition.apply_course_distance( my_head_deg , install_distance_m ); - var myHitch_pos = myPosition.apply_course_distance( my_head_deg - 90. , install_side_m ); - myHitch_pos.set_alt(myPosition.alt() + install_alt_m); - - ########################################### winch hitch position ############################################ - - # get coordinates - var winch_global_pos_x = getprop("sim/hitches/winch/winch/global-pos-x"); - var winch_global_pos_y = getprop("sim/hitches/winch/winch/global-pos-y"); - var winch_global_pos_z = getprop("sim/hitches/winch/winch/global-pos-z"); - - var winch_geod = carttogeod(winch_global_pos_x,winch_global_pos_y,winch_global_pos_z); - - var ai_lat = winch_geod[0]; - var ai_lon = winch_geod[1]; - #var ai_alt = winch_geod[2] * FT2M; - var ai_alt = winch_geod[2]; - #print("ai_lat,lon,alt",ai_lat,ai_lon,ai_alt); - - var aiHitch_pos = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt ); - - - ########################################### distance between hitches ##################################### - - var distance = (myHitch_pos.direct_distance_to(aiHitch_pos)); # distance to winch in meter - var aiHitchheadto = (myHitch_pos.course_to(aiHitch_pos)); - var height = myHitch_pos.alt() - aiHitch_pos.alt(); - - var aiHitchpitchto = -math.asin((myHitch_pos.alt()-aiHitch_pos.alt())/distance) / 0.01745; - #print(" pitch: ", aiHitchpitchto); - - # update position of rope - setprop("ai/models/winchrope/position/latitude-deg", myHitch_pos.lat()); - setprop("ai/models/winchrope/position/longitude-deg", myHitch_pos.lon()); - setprop("ai/models/winchrope/position/altitude-ft", myHitch_pos.alt() * M2FT); - #print("ai_lat,lon,alt",myHitch_pos.lat()," ",myHitch_pos.lon()," ",myHitch_pos.alt() ); - - # update pitch and heading of rope - setprop("ai/models/winchrope/orientation/true-heading-deg", aiHitchheadto); - setprop("ai/models/winchrope/orientation/pitch-deg", aiHitchpitchto); - - # update length of rope - setprop("sim/hitches/winch/tow/dist", distance); - #print("distance=",distance); - - - ############################################# calc forces ################################################## - - # calc forces only for JSBSim-aircraft - - # tow-end-forces must be reported in N to be consiststent to Yasim-aircraft - # hitch-forces must be LBS to be consistent to the JSBSim "external_forces/.../magnitude" definition - - if ( getprop("sim/flight-model") == "jsb" ) { - - var spool_max = getprop("sim/hitches/winch/winch/max-spool-speed-m-s"); - var unspool_max = getprop("sim/hitches/winch/winch/max-unspool-speed-m-s"); - var max_force_N = getprop("sim/hitches/winch/winch/max-force-N"); - var max_power_W = getprop("sim/hitches/winch/winch/max-power-kW") * 1000.; - var breakforce_N = getprop("sim/hitches/winch/tow/break-force-N"); - var elastic_constant = getprop("sim/hitches/winch/tow/elastic-constant"); - var towlength_m = getprop("sim/hitches/winch/tow/length"); - var max_tow_length_m = getprop("sim/hitches/winch/winch/max-tow-length-m"); - var spoolspeed = getprop("sim/hitches/winch/winch/actual-spool-speed-m-s"); - var spool_acceleration = getprop("sim/hitches/winch/winch/spool-acceleration-m-s-s"); - var delta_t = getprop("sim/time/delta-sec"); - - var towlength_new_m = towlength_m - spoolspeed * delta_t; - var delta_towlength_m = distance - towlength_new_m; - #print("towlength_m= ", towlength_m , " elastic_constant= ", elastic_constant," delta_towlength_m= ", delta_towlength_m); - - if ( getprop("sim/hitches/winch/winch/clutched") ) { - var delta_spoolspeed = spool_acceleration * delta_t; - spoolspeed = spoolspeed + delta_spoolspeed ; - if ( spoolspeed > spool_max ) spoolspeed = spool_max; - } - else { # un-clutched - # --- experimental --- # - - # we assume that the the winch-operator avoids tow sagging ( => rigid rope; negativ forces allowed) - var forcetow_N = elastic_constant * delta_towlength_m / towlength_new_m; - - # drag of tow-rope ( magic! ) - var magic_constant = getprop("sim/hitches/winch/winch/magic-constant"); - tow_drag_N = spoolspeed * spoolspeed * math.sqrt( math.sqrt( height * height ) * max_tow_length_m ) / magic_constant ; - - # mass = tow-mass only (drum-mass ignored) - var mass_kg = max_tow_length_m * getprop("sim/hitches/winch/tow/weight-per-m-kg-m"); - - var acceleration = ( forcetow_N - tow_drag_N ) / mass_kg; - var delta_spoolspeed = acceleration * delta_t; - spoolspeed = spoolspeed - delta_spoolspeed; - if ( spoolspeed < - unspool_max ) spoolspeed = - unspool_max; - #print("spoolspeed= ",spoolspeed," delta_spoolspeed= ",delta_spoolspeed," delta_towlength= ", delta_towlength_m); - #print("forcetow_N= ",forcetow_N," tow_drag_N= ",tow_drag_N," acceleration= ", acceleration); - } - - if ( delta_towlength_m < 0. ) { - var forcetow_N = 0.; - } - else{ - var forcetow_N = elastic_constant * delta_towlength_m / towlength_new_m; - } - - if ( forcetow_N > max_force_N ) { - forcetow_N = max_force_N; - var towlength_new_m = distance / ( forcetow_N / elastic_constant + 1. ); - spoolspeed = (towlength_m - towlength_new_m ) / delta_t; - } - - var power = forcetow_N * spoolspeed; - if ( power > max_power_W) { - power = max_power_W; - spoolspeed = power / forcetow_N; - towlength_new_m = towlength_m - spoolspeed * delta_t; - } - #print("power=",power," spoolspeed=",spoolspeed," force=",forcetow_N); - - setprop("sim/hitches/winch/tow/length",towlength_new_m); - setprop("sim/hitches/winch/winch/actual-spool-speed-m-s",spoolspeed); - setprop("sim/hitches/winch/winch/actual-force-N",forcetow_N); - - # force due to tow-weight (acts in tow direction at the heigher hitch) - var force_due_to_weight_N = getprop("sim/hitches/winch/tow/weight-per-m-kg-m") * 9.81 * height; - if (height < 0. ) force_due_to_weight_N = 0.; - - forcetow_N = forcetow_N + force_due_to_weight_N; - var forcetow_LBS = forcetow_N * 0.224809; # N -> LBF - #print(" forcetow_N ", forcetow_N , " distance ", distance," ", breakforce_N); - #print(" forcetow_N=", forcetow_N , " force_due_to_weight_N=", force_due_to_weight_N," height=",height); - - if ( forcetow_N < breakforce_N ) { - - var distancepr = (myHitch_pos.distance_to(aiHitch_pos)); - - # correct a failure, if the projected length is larger than direct length - if (distancepr > distance) { distancepr = distance;} - - var alpha = math.acos( (distancepr / distance) ); - if ( aiHitch_pos.alt() > myHitch_pos.alt()) alpha = - alpha; - var beta = ( aiHitchheadto - my_head_deg ) * DEG2RAD; - var gamma = my_pitch_deg * DEG2RAD; - var delta = my_roll_deg * DEG2RAD; - - var sina = math.sin(alpha); - var cosa = math.cos(alpha); - var sinb = math.sin(beta); - var cosb = math.cos(beta); - var sing = math.sin(gamma); - var cosg = math.cos(gamma); - var sind = math.sin(delta); - var cosd = math.cos(delta); - - #var forcetow = forcetow_N; # we deliver N to JSBSim - var forcetow = forcetow_LBS; # we deliver LBS to JSBSim - - # calculate unit vector of force direction in JSBSim-system - var force = 1; - - # global forces: alpha beta - var fglobalx = force * cosa * cosb; - var fglobaly = force * cosa * sinb; - var fglobalz = force * sina; - - # local forces by pitch: gamma - var flpitchx = fglobalx * cosg - fglobalz * sing; - var flpitchy = fglobaly; - var flpitchz = fglobalx * sing + fglobalz * cosg; - - # local forces by roll: delta - var flrollx = flpitchx; - var flrolly = flpitchy * cosd + flpitchz * sind; - var flrollz = - flpitchy * sind + flpitchz * cosd; - - # asigning to LOCAL coord of plane - var forcex = flrollx; - var forcey = flrolly; - var forcez = flrollz; - #print("fx=",forcex," fy=",forcey," fz=",forcez); - - # JSBSim-body-frame: x-> nose / y -> right wing / z -> down - # apply forces to hook (forces are in LBS or N see above) - var hitchname = getprop("sim/hitches/winch/force_name_jsbsim"); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", forcetow); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", forcex ); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", forcey ); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", forcez ); - - # check, if auto-release condition is reached - var rope_angle_deg = math.atan2(forcez , forcex ) * RAD2DEG; - #print("rope_angle_deg=",rope_angle_deg); - if (rope_angle_deg > getprop("sim/hitches/winch/automatic-release-angle-deg") ) releaseWinch(); - - } # end force < break force - else { # rope is broken - props.globals.getNode("sim/hitches/winch/broken").setBoolValue(1); - releaseWinch(); - } - - if ( towlength_new_m > max_tow_length_m ) { - setprop("sim/messages/atc", sprintf("tow length exceeded!")); - releaseWinch(); - } - - } # end forces/JSBSim - - } # end hitch is closed (open == 0) - + if ( forcetow_N > max_force_N ) { + forcetow_N = max_force_N; + var towlength_new_m = distance / ( forcetow_N / elastic_constant + 1. ); + spoolspeed = (towlength_m - towlength_new_m ) / delta_t; + } + + var power = forcetow_N * spoolspeed; + if ( power > max_power_W) { + power = max_power_W; + forcetow_N = power / spoolspeed; + towlength_new_m = towlength_m - spoolspeed * delta_t; + } + + winch_hash.tow.length.setDoubleValue( towlength_new_m ); + winch_hash.actual_spool_speed.setDoubleValue( spoolspeed ); + winch_hash.actual_force.setDoubleValue( forcetow_N ); + + # force due to tow-weight (acts in tow direction at the heigher hitch) + var force_due_to_weight_N = config.winch.weight_per_m_kg_m.getDoubleValue() * 9.81 * height; + if (height < 0. ) force_due_to_weight_N = 0.; + + forcetow_N = forcetow_N + force_due_to_weight_N; + var forcetow_LBS = forcetow_N * 0.224809; # N -> LBF + + if ( forcetow_N < breakforce_N ) { + + var distancepr = (myHitch_pos.distance_to(aiHitch_pos)); + + # correct a failure, if the projected length is larger than direct length + if (distancepr > distance) { distancepr = distance;} + + var alpha = math.acos( (distancepr / distance) ); + if ( aiHitch_pos.alt() > myHitch_pos.alt()) alpha = - alpha; + var beta = ( aiHitchheadto - my_head_deg ) * D2R; + var gamma = my_pitch_deg * D2R; + var delta = my_roll_deg * D2R; + + var sina = math.sin(alpha); + var cosa = math.cos(alpha); + var sinb = math.sin(beta); + var cosb = math.cos(beta); + var sing = math.sin(gamma); + var cosg = math.cos(gamma); + var sind = math.sin(delta); + var cosd = math.cos(delta); + + # calculate unit vector of force direction in JSBSim-system + var force = 1; + + # global forces: alpha beta + var fglobalx = force * cosa * cosb; + var fglobaly = force * cosa * sinb; + var fglobalz = force * sina; + + # local forces by pitch: gamma + var flpitchx = fglobalx * cosg - fglobalz * sing; + var flpitchy = fglobaly; + var flpitchz = fglobalx * sing + fglobalz * cosg; + + # local forces by roll: delta + var flrollx = flpitchx; + var flrolly = flpitchy * cosd + flpitchz * sind; + var flrollz = - flpitchy * sind + flpitchz * cosd; + + # asigning to LOCAL coord of plane + var forcex = flrollx; + var forcey = flrolly; + var forcez = flrollz; + + # JSBSim-body-frame: x-> nose / y -> right wing / z -> down + # apply forces to hook (forces are in LBS or N see above) + winch_hash.apply_force.magnitude.setDoubleValue( forcetow_LBS ); + winch_hash.apply_force.coord[0].setDoubleValue( forcex ); + winch_hash.apply_force.coord[1].setDoubleValue( forcey ); + winch_hash.apply_force.coord[2].setDoubleValue( forcez ); + + # check, if auto-release condition is reached + var rope_angle_deg = math.atan2(forcez , forcex ) * R2D; + #print("rope_angle_deg=",rope_angle_deg); + if (rope_angle_deg > aircraft_settings.winch.automatic_release_angle.getDoubleValue() ) releaseWinch(); + + } # end force < break force + else { # rope is broken + winch_hash.broken.setBoolValue( 1 ); + releaseWinch(); + } + + if ( towlength_new_m > max_tow_length_m ) { + write_message( "system", "tow length exceeded!" ); + releaseWinch(); + } + + } # end forces/JSBSim + + } # end hitch is closed (open == 0) + } # end function winch @@ -1254,41 +1464,49 @@ var winch = func (open){ # ###################################################################################################################### var createTowrope = func (device){ - - # create the towrope in the model property tree - #print("createTowrope for ",device); - - if ( getprop("sim/hitches/" ~ device ~ "/rope/exist") == 0 ) { # does the towrope exist? - - # get the next free model id - var freeModelid = getFreeModelID(); - - props.globals.getNode("sim/hitches/" ~ device ~ "/rope/model_id").setIntValue(freeModelid); - props.globals.getNode("sim/hitches/" ~ device ~ "/rope/exist").setBoolValue(1); - - var towrope_ai = props.globals.getNode("ai/models/" ~ device ~ "rope", 1); - var towrope_mod = props.globals.getNode("models", 1); - - towrope_ai.getNode("id", 1).setIntValue(4711); - towrope_ai.getNode("callsign", 1).setValue("towrope"); - towrope_ai.getNode("valid", 1).setBoolValue(1); - towrope_ai.getNode("position/latitude-deg", 1).setValue(0.); - towrope_ai.getNode("position/longitude-deg", 1).setValue(0.); - towrope_ai.getNode("position/altitude-ft", 1).setValue(0.); - towrope_ai.getNode("orientation/true-heading-deg", 1).setValue(0.); - towrope_ai.getNode("orientation/pitch-deg", 1).setValue(0.); - towrope_ai.getNode("orientation/roll-deg", 1).setValue(0.); - - towrope_mod.model = towrope_mod.getChild("model", freeModelid, 1); - towrope_mod.model.getNode("path", 1).setValue(getprop("sim/hitches/" ~ device ~ "/rope/path_to_model") ); - towrope_mod.model.getNode("longitude-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/longitude-deg"); - towrope_mod.model.getNode("latitude-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/latitude-deg"); - towrope_mod.model.getNode("elevation-ft-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/altitude-ft"); - towrope_mod.model.getNode("heading-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/true-heading-deg"); - towrope_mod.model.getNode("roll-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/roll-deg"); - towrope_mod.model.getNode("pitch-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/pitch-deg"); - towrope_mod.model.getNode("load", 1).remove(); - } # end towrope exist + + # create the towrope in the model property tree + #print("createTowrope for ",device); + + if ( getprop("sim/hitches/" ~ device ~ "/rope/exist") == 0 ) { # does the towrope exist? + + # get the next free model id + var freeModelid = getFreeModelID(); + + props.globals.getNode("sim/hitches/" ~ device ~ "/rope/model_id").setIntValue(freeModelid); + props.globals.getNode("sim/hitches/" ~ device ~ "/rope/exist").setBoolValue(1); + + var towrope_ai = props.globals.getNode("ai/models/" ~ device ~ "rope", 1); + var towrope_mod = props.globals.getNode("models", 1); + + towrope_ai.getNode("id", 1).setIntValue(4711); + towrope_ai.getNode("callsign", 1).setValue("towrope"); + towrope_ai.getNode("valid", 1).setBoolValue(1); + towrope_ai.getNode("position/latitude-deg", 1).setValue(0.); + towrope_ai.getNode("position/longitude-deg", 1).setValue(0.); + towrope_ai.getNode("position/altitude-ft", 1).setValue(0.); + towrope_ai.getNode("orientation/true-heading-deg", 1).setValue(0.); + towrope_ai.getNode("orientation/pitch-deg", 1).setValue(0.); + towrope_ai.getNode("orientation/roll-deg", 1).setValue(0.); + + towrope_mod.model = towrope_mod.getChild("model", freeModelid, 1); + towrope_mod.model.getNode("path", 1).setValue(getprop("sim/hitches/" ~ device ~ "/rope/path_to_model") ); + towrope_mod.model.getNode("longitude-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/longitude-deg"); + towrope_mod.model.getNode("latitude-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/latitude-deg"); + towrope_mod.model.getNode("elevation-ft-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/altitude-ft"); + towrope_mod.model.getNode("heading-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/true-heading-deg"); + towrope_mod.model.getNode("roll-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/roll-deg"); + towrope_mod.model.getNode("pitch-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/pitch-deg"); + towrope_mod.model.getNode("load", 1).remove(); + + if( device == "winch" ){ + winch_hash.rope.lat= check_or_create("ai/models/winchrope/position/latitude-deg", 0.0, "DOUBLE"); + winch_hash.rope.lon= check_or_create("ai/models/winchrope/position/longitude-deg", 0.0, "DOUBLE"); + winch_hash.rope.alt= check_or_create("ai/models/winchrope/position/altitude-ft", 0.0, "DOUBLE"); + winch_hash.rope.hdg= check_or_create("ai/models/winchrope/orientation/true-heading-deg", 0.0, "DOUBLE"); + winch_hash.rope.pitch= check_or_create("ai/models/winchrope/orientation/pitch-deg", 0.0, "DOUBLE"); + } + } # end towrope exist } @@ -1297,16 +1515,16 @@ var createTowrope = func (device){ # ###################################################################################################################### var getFreeModelID = func { - #print("getFreeModelID"); - var modelid = 0; # next unused id - modelobjects = props.globals.getNode("models", 1).getChildren(); - foreach ( var member; modelobjects ) { - if ( (var c = member.getIndex()) != nil) { - modelid = c + 1; - } - } - #print("modelid=",modelid); - return(modelid); + #print("getFreeModelID"); + var modelid = 0; # next unused id + modelobjects = props.globals.getNode("models", 1).getChildren(); + foreach ( var member; modelobjects ) { + if ( (var c = member.getIndex()) != nil) { + modelid = c + 1; + } + } + #print("modelid=",modelid); + return(modelid); } @@ -1315,111 +1533,113 @@ var getFreeModelID = func { # ###################################################################################################################### var closeHitch = func { - - #print("closeHitch"); - - # close only, if - # - not yet closed - # - connected to property-node - # - distance < towrope length - - var open = getprop("sim/hitches/aerotow/open"); - if ( !open ) return; - - var aiNodeID = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id"); # id of former found ai/mp aircraft - if ( aiNodeID < 1 ) { - setprop("sim/messages/atc", sprintf("No aircraft selected!")); - return; - } - - ##################################### calc distance between hitches ###################### - - ###################### my hitch position ####################### - - myPosition = geo.aircraft_position(); - var my_head_deg = getprop("orientation/heading-deg"); - var my_roll_deg = getprop("orientation/roll-deg"); - var my_pitch_deg = getprop("orientation/pitch-deg"); - - # hook coordinates in Yasim-system (x-> nose / y -> left wing / z -> up) - assignHitchLocations("aerotow"); - var x = getprop("sim/hitches/aerotow/local-pos-x"); - var y = getprop("sim/hitches/aerotow/local-pos-y"); - var z = getprop("sim/hitches/aerotow/local-pos-z"); - - var alpha_deg = my_roll_deg * (1.); # roll clockwise (looking in x-direction) := + - var beta_deg = my_pitch_deg * (-1.); # pitch clockwise (looking in y-direction) := - - - # transform hook coordinates - var Xn = PointRotate3D(x:x,y:y,z:z,xr:0.,yr:0.,zr:0.,alpha_deg:alpha_deg,beta_deg:beta_deg,gamma_deg:0.); - - var install_distance_m = Xn[0]; # in front of ref-point of glider - var install_side_m = Xn[1]; - var install_alt_m = Xn[2]; - - var myHitch_pos = myPosition.apply_course_distance( my_head_deg , install_distance_m ); - var myHitch_pos = myPosition.apply_course_distance( my_head_deg - 90. , install_side_m ); - myHitch_pos.set_alt(myPosition.alt() + install_alt_m); - - ###################### ai hitch position ####################### - - var found = 0; - - aiobjects = props.globals.getNode("ai/models").getChildren(); - foreach (var aimember; aiobjects) { - if ( (var c = aimember.getNode("id") ) != nil ) { - var testprop = c.getValue(); - if ( testprop == aiNodeID) { - found = found + 1; - - # get coordinates - var ai_lat = aimember.getNode("position/latitude-deg").getValue(); - var ai_lon = aimember.getNode("position/longitude-deg").getValue(); - var ai_alt = (aimember.getNode("position/altitude-ft").getValue()) * FT2M; - - var ai_pitch_deg = aimember.getNode("orientation/pitch-deg").getValue(); - var ai_roll_deg = aimember.getNode("orientation/roll-deg").getValue(); - var ai_head_deg = aimember.getNode("orientation/true-heading-deg").getValue(); - - var aiHitchX = aimember.getNode("sim/hitches/aerotow/local-pos-x").getValue(); - var aiHitchY = aimember.getNode("sim/hitches/aerotow/local-pos-y").getValue(); - var aiHitchZ = aimember.getNode("sim/hitches/aerotow/local-pos-z").getValue(); - - var aiPosition = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt ); - - var alpha_deg = ai_roll_deg * (1.); - var beta_deg = ai_pitch_deg * (-1.); - - # transform hook coordinates - var Xn = PointRotate3D(x:aiHitchX,y:aiHitchY,z:aiHitchZ,xr:0.,yr:0.,zr:0.,alpha_deg:alpha_deg,beta_deg:beta_deg,gamma_deg:0.); - - var install_distance_m = Xn[0]; # in front of ref-point of glider - var install_side_m = Xn[1]; - var install_alt_m = Xn[2]; - - var aiHitch_pos = aiPosition.apply_course_distance( ai_head_deg , install_distance_m ); - var aiHitch_pos = aiPosition.apply_course_distance( ai_head_deg - 90. , install_side_m ); - aiHitch_pos.set_alt(aiPosition.alt() + install_alt_m); - - var distance = (myHitch_pos.direct_distance_to(aiHitch_pos)); - - var towlength_m = props.globals.getNode("sim/hitches/aerotow/tow/length").getValue(); - if ( distance > towlength_m ) { - var aicallsign = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign"); - #setprop("sim/messages/atc", sprintf("Aircraft with callsign %s is too far away (distance is %4.0f meter).",aicallsign, distance)); - setprop("sim/messages/atc", sprintf("Selected aircraft is too far away (distance to %s is %4.0f meter).",aicallsign, distance)); - return; - } - - setprop("sim/hitches/aerotow/tow/dist", distance); - - } - } - } - - setprop("sim/hitches/aerotow/open", "false"); - setprop("sim/hitches/aerotow/mp_oldOpen", "true"); - + + #print("closeHitch"); + + # close only, if + # - not yet closed + # - connected to property-node + # - distance < towrope length + + if ( !aerotow_hash.open.getBoolValue() ) return; + + var aiNodeID = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id"); # id of former found ai/mp aircraft + if ( aiNodeID < 1 ) { + setprop("sim/messages/atc", sprintf("No aircraft selected!")); + return; + } + + ##################################### calc distance between hitches ###################### + + ###################### my hitch position ####################### + + myPosition = geo.aircraft_position(); + var my_head_deg = getprop("orientation/heading-deg"); + var my_roll_deg = getprop("orientation/roll-deg"); + var my_pitch_deg = getprop("orientation/pitch-deg"); + + # hook coordinates in Yasim-system (x-> nose / y -> left wing / z -> up) + assignHitchLocations("aerotow"); + var x = aircraft_settings.aerotow.local_pos[0].getDoubleValue(); + var y = aircraft_settings.aerotow.local_pos[1].getDoubleValue(); + var z = aircraft_settings.aerotow.local_pos[2].getDoubleValue(); + #var x = getprop("sim/hitches/aerotow/local-pos-x"); + #var y = getprop("sim/hitches/aerotow/local-pos-y"); + #var z = getprop("sim/hitches/aerotow/local-pos-z"); + + var alpha_deg = my_roll_deg * (1.); # roll clockwise (looking in x-direction) := + + var beta_deg = my_pitch_deg * (-1.); # pitch clockwise (looking in y-direction) := - + + # transform hook coordinates + var Xn = PointRotate3D(x:x,y:y,z:z,xr:0.,yr:0.,zr:0.,alpha_deg:alpha_deg,beta_deg:beta_deg,gamma_deg:0.); + + var install_distance_m = Xn[0]; # in front of ref-point of glider + var install_side_m = Xn[1]; + var install_alt_m = Xn[2]; + + var myHitch_pos = myPosition.apply_course_distance( my_head_deg , install_distance_m ); + var myHitch_pos = myPosition.apply_course_distance( my_head_deg - 90. , install_side_m ); + myHitch_pos.set_alt(myPosition.alt() + install_alt_m); + + ###################### ai hitch position ####################### + + var found = 0; + + aiobjects = props.globals.getNode("ai/models").getChildren(); + foreach (var aimember; aiobjects) { + if ( (var c = aimember.getNode("id") ) != nil ) { + var testprop = c.getValue(); + if ( testprop == aiNodeID) { + found = found + 1; + + # get coordinates + var ai_lat = aimember.getNode("position/latitude-deg").getValue(); + var ai_lon = aimember.getNode("position/longitude-deg").getValue(); + var ai_alt = (aimember.getNode("position/altitude-ft").getValue()) * FT2M; + + var ai_pitch_deg = aimember.getNode("orientation/pitch-deg").getValue(); + var ai_roll_deg = aimember.getNode("orientation/roll-deg").getValue(); + var ai_head_deg = aimember.getNode("orientation/true-heading-deg").getValue(); + + var aiHitchX = aimember.getNode("sim/hitches/aerotow/local-pos-x").getValue(); + var aiHitchY = aimember.getNode("sim/hitches/aerotow/local-pos-y").getValue(); + var aiHitchZ = aimember.getNode("sim/hitches/aerotow/local-pos-z").getValue(); + + var aiPosition = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt ); + + var alpha_deg = ai_roll_deg * (1.); + var beta_deg = ai_pitch_deg * (-1.); + + # transform hook coordinates + var Xn = PointRotate3D(x:aiHitchX,y:aiHitchY,z:aiHitchZ,xr:0.,yr:0.,zr:0.,alpha_deg:alpha_deg,beta_deg:beta_deg,gamma_deg:0.); + + var install_distance_m = Xn[0]; # in front of ref-point of glider + var install_side_m = Xn[1]; + var install_alt_m = Xn[2]; + + var aiHitch_pos = aiPosition.apply_course_distance( ai_head_deg , install_distance_m ); + var aiHitch_pos = aiPosition.apply_course_distance( ai_head_deg - 90. , install_side_m ); + aiHitch_pos.set_alt(aiPosition.alt() + install_alt_m); + + var distance = (myHitch_pos.direct_distance_to(aiHitch_pos)); + + var towlength_m = props.globals.getNode("sim/hitches/aerotow/tow/length").getValue(); + if ( distance > towlength_m ) { + var aicallsign = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign"); + #setprop("sim/messages/atc", sprintf("Aircraft with callsign %s is too far away (distance is %4.0f meter).",aicallsign, distance)); + setprop("sim/messages/atc", sprintf("Selected aircraft is too far away (distance to %s is %4.0f meter).",aicallsign, distance)); + return; + } + + setprop("sim/hitches/aerotow/tow/dist", distance); + + } + } + } + + aerotow_hash.open.setBoolValue( 0 ); + setprop("sim/hitches/aerotow/mp_oldOpen", "true"); + } # End function closeHitch @@ -1428,25 +1648,28 @@ var closeHitch = func { # ###################################################################################################################### var releaseHitch = func (device){ - - #print("releaseHitch"); - - if ( getprop("sim/flight-model") == "yasim" ) return; # bypass this routine for Yasim-aircraft - - setprop("sim/hitches/" ~ device ~ "/open", "true"); - - var hitchname = getprop("sim/hitches/" ~ device ~ "/force_name_jsbsim"); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", 0.); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", 0.); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", 0.); - setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", 0.); - - if ( device == "aerotow" ) { - setprop("sim/hitches/aerotow/tow/end-force-x", 0.); # MP tow-end forces - setprop("sim/hitches/aerotow/tow/end-force-y", 0.); # - setprop("sim/hitches/aerotow/tow/end-force-z", 0.); # - } - + + #print("releaseHitch"); + + if ( fdm == "yasim" ) return; # bypass this routine for Yasim-aircraft + + setprop("sim/hitches/" ~ device ~ "/open", "true"); + + var hitchname = getprop("sim/hitches/" ~ device ~ "/force_name_jsbsim"); + setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", 0.); + setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", 0.); + setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", 0.); + setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", 0.); + + if ( device == "aerotow" ) { + setprop("sim/hitches/aerotow/tow/end-force-x", 0.); # MP tow-end forces + setprop("sim/hitches/aerotow/tow/end-force-y", 0.); # + setprop("sim/hitches/aerotow/tow/end-force-z", 0.); # + } else { + # Put automatic winch driver in after-tow phase + phase = 5; + } + } # End function releaseHitch @@ -1455,22 +1678,22 @@ var releaseHitch = func (device){ # ###################################################################################################################### var removeTowrope = func (device){ - - # remove the towrope from the property tree ai/models - # remove the towrope from the property tree models/ - - if ( getprop("sim/hitches/" ~ device ~ "/rope/exist") == 1 ) { # does the towrope exist? - - # remove 3d model from scenery - # identification is /models/model[x] with x=id_model - var id_model = getprop("sim/hitches/" ~ device ~ "/rope/model_id"); - var modelsNode = "models/model[" ~ id_model ~ "]"; - props.globals.getNode(modelsNode).remove(); - props.globals.getNode("ai/models/" ~ device ~ "rope").remove(); - #print("towrope removed"); - setprop("sim/hitches/" ~ device ~ "/rope/exist", 0); - } - + + # remove the towrope from the property tree ai/models + # remove the towrope from the property tree models/ + + if ( getprop("sim/hitches/" ~ device ~ "/rope/exist") == 1 ) { # does the towrope exist? + + # remove 3d model from scenery + # identification is /models/model[x] with x=id_model + var id_model = getprop("sim/hitches/" ~ device ~ "/rope/model_id"); + var modelsNode = "models/model[" ~ id_model ~ "]"; + props.globals.getNode(modelsNode).remove(); + props.globals.getNode("ai/models/" ~ device ~ "rope").remove(); + #print("towrope removed"); + setprop("sim/hitches/" ~ device ~ "/rope/exist", 0); + } + } @@ -1478,62 +1701,89 @@ var removeTowrope = func (device){ # pull in towrope after hitch has been opened # ###################################################################################################################### +var sink_rate_mps = 0.0; + var pull_in_rope = func { - - var deg2rad = math.pi / 180.; - var FT2M = 0.30480; - - if ( getprop("sim/hitches/winch/open") ) { - - # get length of rope - #var distance = getprop("sim/hitches/winch/tow/dist"); - - var towlength_m = getprop("sim/hitches/winch/tow/length"); - var spoolspeed = getprop("sim/hitches/winch/winch/max-spool-speed-m-s"); - var delta_t = getprop("sim/time/delta-sec"); - - var delta_length_m = spoolspeed * delta_t; - var towlength_new_m = towlength_m - delta_length_m; - var towlength_min_m = getprop("sim/hitches/winch/winch/min-tow-length-m"); - - if ( towlength_new_m > towlength_min_m ) { - #print("actual towlength= ",towlength_new_m); - - # get position of rope end (former myHitch_pos) - var tow_lat = getprop("ai/models/winchrope/position/latitude-deg"); - var tow_lon = getprop("ai/models/winchrope/position/longitude-deg"); - var tow_alt_m = getprop("ai/models/winchrope/position/altitude-ft") * FT2M; - # get pitch and heading of rope - var tow_heading_deg = getprop("ai/models/winchrope/orientation/true-heading-deg"); - var tow_pitch_rad = getprop("ai/models/winchrope/orientation/pitch-deg") * deg2rad; - - var aiTow_pos = geo.Coord.set_latlon( tow_lat, tow_lon, tow_alt_m ); - - var delta_distance_m = delta_length_m * math.cos(tow_pitch_rad); - var delta_alt_m = delta_length_m * math.sin(tow_pitch_rad); - # vertical sink rate not yet taken into account! - aiTow_pos = aiTow_pos.apply_course_distance( tow_heading_deg , delta_distance_m ); - aiTow_pos.set_alt(tow_alt_m + delta_alt_m); - #print("aiTow_pos.alt()= ",aiTow_pos.alt()," ",tow_alt_m + delta_alt_m); - - # update position of rope - setprop("ai/models/winchrope/position/latitude-deg", aiTow_pos.lat()); - setprop("ai/models/winchrope/position/longitude-deg", aiTow_pos.lon()); - setprop("ai/models/winchrope/position/altitude-ft", aiTow_pos.alt() * M2FT); - - # update length of rope - setprop("sim/hitches/winch/tow/length",towlength_new_m); - - settimer( pull_in_rope , 0 ); - } # end towlength > min - else { - #print("pull in finished!"); - setprop("sim/hitches/winch/winch/actual-spool-speed-m-s", 0. ); - removeTowrope("winch"); # remove towrope model - } - - } # end if open - + + + if ( winch_hash.open.getBoolValue() ) { + + var current_tow_length = winch_hash.tow.length.getDoubleValue(); + + if ( current_tow_length > 15.0 ) { + + var dt = delta_time.getDoubleValue(); + + # get position of rope end (former myHitch_pos) + var tow_lat = winch_hash.rope.lat.getDoubleValue(); + var tow_lon = winch_hash.rope.lon.getDoubleValue(); + var tow_alt_m = winch_hash.rope.alt.getDoubleValue() * FT2M; + # get pitch and heading of rope + var tow_heading_deg = winch_hash.rope.hdg.getDoubleValue(); + var tow_pitch_rad = winch_hash.rope.pitch.getDoubleValue() * D2R; + + var aiTow_pos = geo.Coord.set_latlon( tow_lat, tow_lon, tow_alt_m ); + + var delta_length_m = config.winch.max_spool_speed.getDoubleValue() * dt; + var delta_distance_m = delta_length_m * math.cos(tow_pitch_rad); + + aiTow_pos = aiTow_pos.apply_course_distance( tow_heading_deg , delta_distance_m ); + + # Wind influence + aiTow_pos = aiTow_pos.apply_course_distance( wind_from.getDoubleValue() - 180, wind_speed_kt.getDoubleValue() * KT2MPS * dt ); + + var elevation = geo.elevation( aiTow_pos.lat(), aiTow_pos.lon() ); + if( elevation == nil ) elevation = tow_alt_m; + if( tow_alt_m > elevation ){ + var delta_alt_m = delta_length_m * math.sin(tow_pitch_rad); + # Calculate vertical sink rate + # assume: drag is exclusively due to the chute, chute size is about 3.14m2 (circle radius 1m), rho is static at 1.29kg/m3 + var mass_kg = current_tow_length * config.winch.weight_per_m_kg_m.getDoubleValue(); + sink_rate_mps = sink_rate_mps + dt * ( 9.81 - 1.29 * math.pow( sink_rate_mps, 2 ) * 3.14 / mass_kg ); + + delta_alt_m = delta_alt_m - sink_rate_mps * dt; + + aiTow_pos.set_alt( tow_alt_m + delta_alt_m ); + } else { + aiTow_pos.set_alt( elevation ); + } + #print("aiTow_pos.alt()= ",aiTow_pos.alt()," ",tow_alt_m + delta_alt_m); + + # Winch position + var winch_global_pos_x = winch_hash.global_pos[0].getDoubleValue(); + var winch_global_pos_y = winch_hash.global_pos[1].getDoubleValue(); + var winch_global_pos_z = winch_hash.global_pos[2].getDoubleValue(); + + var winch_geod = carttogeod(winch_global_pos_x, winch_global_pos_y, winch_global_pos_z); + var aiWinch_pos = geo.Coord.new(); + aiWinch_pos.set_latlon( winch_geod[0], winch_geod[1], winch_geod[2] ); + + var new_tow_length_m = aiTow_pos.direct_distance_to(aiWinch_pos); + var aiHitchpitchto = -math.asin( (aiTow_pos.alt()-aiWinch_pos.alt()) / new_tow_length_m ) * R2D; + + # update position of rope + #print("pull in active"); + winch_hash.rope.lat.setDoubleValue( aiTow_pos.lat() ); + winch_hash.rope.lon.setDoubleValue( aiTow_pos.lon() ); + winch_hash.rope.alt.setDoubleValue( aiTow_pos.alt() * M2FT ); + # update orientation of rope + winch_hash.rope.hdg.setDoubleValue( aiTow_pos.course_to( aiWinch_pos ) ); + winch_hash.rope.pitch.setDoubleValue( aiHitchpitchto ); + + # update length of rope + winch_hash.tow.length.setDoubleValue( new_tow_length_m ); + winch_hash.tow.dist.setDoubleValue( new_tow_length_m ); + + settimer( pull_in_rope , 0 ); + } # end towlength > min + else { + #print("pull in finished!"); + winch_hash.actual_spool_speed.setDoubleValue( 0 ); + removeTowrope("winch"); # remove towrope model + } + + } # end if open + } @@ -1542,28 +1792,28 @@ var pull_in_rope = func { # ###################################################################################################################### var setAIObjectDefaults = func (){ - - # set some default variables, needed to identify, if the found object is an AI-object, a "non-interactiv MP-object or - # an interactive MP-object - - var aiNodeID = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id"); # id of former found ai/mp aircraft - - aiobjects = props.globals.getNode("ai/models").getChildren(); - foreach (var aimember; aiobjects) { - if ( (var c = aimember.getNode("id") ) != nil ) { - var testprop = c.getValue(); - if ( testprop == aiNodeID) { - # Set some dummy values. In case of an "interactive"-MP plane - # the correct values will be transmitted in the following loop. - # Create this variables if not present. - aimember.getNode("sim/hitches/aerotow/local-pos-x",1).setValue(-5.); - aimember.getNode("sim/hitches/aerotow/local-pos-y",1).setValue(0.); - aimember.getNode("sim/hitches/aerotow/local-pos-z",1).setValue(0.); - aimember.getNode("sim/hitches/aerotow/tow/dist",1).setValue(-1.); - } - } - } - + + # set some default variables, needed to identify, if the found object is an AI-object, a "non-interactiv MP-object or + # an interactive MP-object + + var aiNodeID = aerotow_hash.tow.conn_ai_or_mp_id.getIntValue(); # id of former found ai/mp aircraft + + aiobjects = props.globals.getNode("ai/models").getChildren(); + foreach (var aimember; aiobjects) { + if ( (var c = aimember.getNode("id") ) != nil ) { + var testprop = c.getValue(); + if ( testprop == aiNodeID) { + # Set some dummy values. In case of an "interactive"-MP plane + # the correct values will be transmitted in the following loop. + # Create this variables if not present. + aimember.getNode("sim/hitches/aerotow/local-pos-x",1).setValue(-5.); + aimember.getNode("sim/hitches/aerotow/local-pos-y",1).setValue(0.); + aimember.getNode("sim/hitches/aerotow/local-pos-z",1).setValue(0.); + aimember.getNode("sim/hitches/aerotow/tow/dist",1).setValue(-1.); + } + } + } + } @@ -1572,54 +1822,74 @@ var setAIObjectDefaults = func (){ # ###################################################################################################################### var setWinchPositionAuto = func { - - # remove already existing winch model - if ( getprop("/sim/hitches/winch/winch/winch-model-index") != nil ) { - var id_model = getprop("/sim/hitches/winch/winch/winch-model-index"); - var modelsNode = "models/model[" ~ id_model ~ "]"; - props.globals.getNode(modelsNode).remove(); - #print("winch model removed"); - } - - var initial_length_m = getprop("sim/hitches/winch/winch/initial-tow-length-m"); - var ac_pos = geo.aircraft_position(); # get position of aircraft - var ac_hd = getprop("orientation/heading-deg"); # get heading of aircraft - - # setup winch - # get initial runway position - var ipos_lat_deg = getprop("sim/presets/latitude-deg"); - var ipos_lon_deg = getprop("sim/presets/longitude-deg"); - var ipos_hd_deg = getprop("sim/presets/heading-deg"); - var ipos_alt_m = geo.elevation(ipos_lat_deg,ipos_lon_deg); - var ipos_geo = geo.Coord.new().set_latlon(ipos_lat_deg, ipos_lon_deg, ipos_alt_m); - # offset to initial position - var deviation = (ac_pos.distance_to(ipos_geo)); - # if deviation is too much, locate winch in front of glider, otherwise locate winch to end of runway - if ( deviation > 200) { - var w = ac_pos.apply_course_distance( ac_hd , initial_length_m -1. ); - } - else { - var w = ipos_geo.apply_course_distance( ipos_hd_deg , initial_length_m - 1. ); - } - var wpalt = geo.elevation(w.lat(), w.lon()); - w.set_alt(wpalt); - - var winchModel = geo.put_model("Models/Airport/supacat_winch.xml", w.lat(), w.lon(), (w.alt()+0.81), (w.course_to(ac_pos) )); - - setprop("/sim/hitches/winch/winch/global-pos-x", w.x()); - setprop("/sim/hitches/winch/winch/global-pos-y", w.y()); - setprop("/sim/hitches/winch/winch/global-pos-z", w.z()); - - setprop("sim/hitches/winch/tow/dist",initial_length_m - 1.); - setprop("sim/hitches/winch/tow/length",initial_length_m); - - #print("name=",winchModel.getName()," Index=",winchModel.getIndex()," Type=",winchModel.getType() ); - #print("val=",winchModel.getValue()," children=",winchModel.getChildren()," size=",size(winchModel) ); - setprop("/sim/hitches/winch/winch/winch-model-index",winchModel.getIndex() ); - setprop("sim/messages/pilot", sprintf("Connected to winch!")); - - props.globals.getNode("sim/hitches/winch/open").setBoolValue(0); - + + # remove already existing winch model + if ( getprop("/sim/hitches/winch/winch/winch-model-index") != nil ) { + var id_model = getprop("/sim/hitches/winch/winch/winch-model-index"); + var modelsNode = "models/model[" ~ id_model ~ "]"; + props.globals.getNode(modelsNode).remove(); + #print("winch model removed"); + } + + var initial_length_m = config.winch.initial_tow_length.getDoubleValue(); + var ac_pos = geo.aircraft_position(); # get position of aircraft + var ac_hd = orientation[0].getDoubleValue(); # get heading of aircraft + + # setup winch + # get initial runway position + var ipos_lat_deg = getprop("sim/presets/latitude-deg"); + var ipos_lon_deg = getprop("sim/presets/longitude-deg"); + var ipos_hd_deg = getprop("sim/presets/heading-deg"); + var ipos_alt_m = geo.elevation(ipos_lat_deg,ipos_lon_deg); + var ipos_geo = geo.Coord.new().set_latlon(ipos_lat_deg, ipos_lon_deg, ipos_alt_m); + # offset to initial position + var deviation = (ac_pos.distance_to(ipos_geo)); + # if deviation is too much, locate winch in front of glider, otherwise locate winch to end of runway + if ( deviation > 200) { + var w = ac_pos.apply_course_distance( ac_hd , initial_length_m -1. ); + } + else { + var w = ipos_geo.apply_course_distance( ipos_hd_deg , initial_length_m - 1. ); + } + var wpalt = geo.elevation(w.lat(), w.lon()); + w.set_alt(wpalt); + + var winchModel = geo.put_model("Models/Airport/supacat_winch.xml", w.lat(), w.lon(), (w.alt()+0.81), (w.course_to(ac_pos) )); + + winch_hash.global_pos[0].setDoubleValue( w.x() ); + winch_hash.global_pos[1].setDoubleValue( w.y() ); + winch_hash.global_pos[2].setDoubleValue( w.z() ); + + winch_hash.tow.dist.setDoubleValue( initial_length_m - 1.0 ); + winch_hash.tow.length.setDoubleValue( initial_length_m ); + + #print("name=",winchModel.getName()," Index=",winchModel.getIndex()," Type=",winchModel.getType() ); + #print("val=",winchModel.getValue()," children=",winchModel.getChildren()," size=",size(winchModel) ); + setprop("/sim/hitches/winch/winch/winch-model-index",winchModel.getIndex() ); + + + winch_hash.rope_breakage = winch_hash.rope_breakage_p.getBoolValue(); + if( winch_hash.rope_breakage ){ + if( config.winch.rope_breakage.random.getBoolValue() ){ + winch_hash.rope_breakage_height_int = 1 + rand() * 300; + } else { + winch_hash.rope_breakage_height_int = config.winch.rope_breakage.height.getDoubleValue(); + } + } + + winch_hash.loss_of_power.enabled = winch_hash.loss_of_power.enabled_p.getBoolValue(); + if( winch_hash.loss_of_power.enabled ){ + if( config.winch.loss_of_power.random.getBoolValue() ){ + winch_hash.loss_of_power.height_int = 1 + rand() * 300; + } else { + winch_hash.loss_of_power.height_int = config.winch.loss_of_power.height.getDoubleValue(); + } + } + + write_message( "pilot", "Connected to winch!" ); + + winch_hash.open.setBoolValue( 0 ); + } # End function setWinchPositionAuto @@ -1628,16 +1898,19 @@ var setWinchPositionAuto = func { # ###################################################################################################################### var runWinch = func { - - if ( !getprop("sim/hitches/winch/winch/clutched") ) { - setprop("sim/hitches/winch/winch/clutched","true"); - setprop("sim/messages/pilot", sprintf("Winch clutched!")); - } - else { - setprop("sim/hitches/winch/winch/clutched","false"); - setprop("sim/messages/pilot", sprintf("Winch un-clutched!")); - } - + + if ( !winch_hash.clutched.getBoolValue() ) { + winch_hash.clutched.setBoolValue( 1 ); + write_message( "pilot", "Winch clutched!" ); + if( alt_agl_ft.getDoubleValue() < 10.0 ){ + auto_winch_driver_init(); + auto_winch_driver_update.restart( 0.0 ); + } + } else { + winch_hash.clutched.setBoolValue( 0 ); + write_message( "pilot", "Winch un-clutched!" ); + } + } # End function runWinch @@ -1646,9 +1919,9 @@ var runWinch = func { # ###################################################################################################################### var releaseWinch = func { - - setprop("sim/hitches/winch/open","true"); - + + winch_hash.open.setBoolValue( 1 ); + } # End function releaseWinch @@ -1657,27 +1930,22 @@ var releaseWinch = func { # ###################################################################################################################### var assignHitchLocations = func (device){ - - if ( getprop("sim/flight-model") == "yasim" ) return; # bypass this routine for Yasim-aircraft - - if ( getprop("sim/hitches/" ~ device ~ "/decoupled-force-and-rope-locations") ) return; # bypass this routine - - #print("assignHitchLocations"); - - var in2m = 0.0254; - - var hitchname = getprop("sim/hitches/" ~ device ~ "/force_name_jsbsim"); - - # location-x(yz)-in: JSBSim Structural Frame: x points to tail, y points to right wing, z points upward - # local-pos-x(yz): YaSim frame: x points to nose, y points to left wing, z points upward - - setprop("sim/hitches/" ~ device ~ "/local-pos-x", - - getprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/location-x-in") * in2m ); - setprop("sim/hitches/" ~ device ~ "/local-pos-y", - - getprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/location-y-in") * in2m ); - setprop("sim/hitches/" ~ device ~ "/local-pos-z", - getprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/location-z-in") * in2m ); - + + if ( fdm == "yasim" ) return; # bypass this routine for Yasim-aircraft + + if ( getprop("sim/hitches/" ~ device ~ "/decoupled-force-and-rope-locations") ) return; # bypass this routine + + #print("assignHitchLocations"); + + var hitchname = getprop("sim/hitches/" ~ device ~ "/force_name_jsbsim"); + + # location-x(yz)-in: JSBSim Structural Frame: x points to tail, y points to right wing, z points upward + # local-pos-x(yz): YaSim frame: x points to nose, y points to left wing, z points upward + + setprop("sim/hitches/" ~ device ~ "/local-pos-x", - getprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/location-x-in") * IN2M ); + setprop("sim/hitches/" ~ device ~ "/local-pos-y", - getprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/location-y-in") * IN2M ); + setprop("sim/hitches/" ~ device ~ "/local-pos-z", getprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/location-z-in") * IN2M ); + } # End function assignHitchLocations @@ -1686,132 +1954,394 @@ var assignHitchLocations = func (device){ # ###################################################################################################################### var PointRotate3D = func (x,y,z,xr,yr,zr,alpha_deg,beta_deg,gamma_deg){ - - # --------------------------------------------------------------------------------- - # rotates point (x,y,z) about all 3 cartesian axis - # center of rotation (xr,yr,zr) - # angle of rotation about x-axis = alpha - # angle of rotation about y-axis = beta - # angle of rotation about z-axis = gamma - # delivers new point coordinates (x_new,y_new,z_new) - # --------------------------------------------------------------------------------- - # - # - # Definitions: - # ---------------- - # - # x y z - # alpha beta gamma - # - # - # z - # | y - # | / - # |/ - # ----->x - # - #---------------------------------------------------------------------------------- - - # Transformation in rotation-system X_rel = X-Xr = (x-xr, y-yr, z-zr) - var x_rel = x-xr; - var y_rel = y-yr; - var z_rel = z-zr; - - # Trigonometry - var deg2rad = math.pi / 180.; - - var alpha_rad = deg2rad * alpha_deg; - var beta_rad = deg2rad * beta_deg; - var gamma_rad = deg2rad * gamma_deg; - - var sin_alpha = math.sin(alpha_rad); - var cos_alpha = math.cos(alpha_rad); - - var sin_beta = math.sin(beta_rad); - var cos_beta = math.cos(beta_rad); - - var sin_gamma = math.sin(gamma_rad); - var cos_gamma = math.cos(gamma_rad); - - # Matrices - # - # Rotate about x-axis Rx(alpha) - # - # Rx11 Rx12 Rx13 1 0 0 - # Rx(alpha)= Rx21 Rx22 Rx23 = 0 cos(alpha) -sin(alpha) - # Rx31 Rx32 Rx33 0 sin(alpha) cos(alpha) - # - var Rx11 = 1.; - var Rx12 = 0.; - var Rx13 = 0.; - var Rx21 = 0.; - var Rx22 = cos_alpha; - var Rx23 = - sin_alpha; - var Rx31 = 0.; - var Rx32 = sin_alpha; - var Rx33 = cos_alpha; - # - # Rotate about y-axis Ry(beta) - # - # Ry11 Ry12 Ry13 cos(beta) 0 sin(beta) - # Ry(beta)= Ry21 Ry22 Ry23 = 0 1 0 - # Ry31 Ry32 Ry33 -sin(beta) 0 cos(beta) - # - var Ry11 = cos_beta; - var Ry12 = 0.; - var Ry13 = sin_beta; - var Ry21 = 0.; - var Ry22 = 1.; - var Ry23 = 0.; - var Ry31 = - sin_beta; - var Ry32 = 0.; - var Ry33 = cos_beta; - # - # Rotate about z-axis Rz(gamma) - # - # Rz11 Rz12 Rz13 cos(gamma) -sin(gamma) 0 - # Rz(gamma)= Rz21 Rz22 Rz23 = sin(gamma) cos(gamma) 0 - # Rz31 Rz32 Rz33 0 0 1 - # - var Rz11 = cos_gamma; - var Rz12 = - sin_gamma; - var Rz13 = 0.; - var Rz21 = sin_gamma; - var Rz22 = cos_gamma; - var Rz23 = 0.; - var Rz31 = 0.; - var Rz32 = 0.; - var Rz33 = 1.; - # - # First rotation about x-axis - # X_x = Rx*X_rel - var x_x = Rx11 * x_rel + Rx12 * y_rel + Rx13 * z_rel; - var y_x = Rx21 * x_rel + Rx22 * y_rel + Rx23 * z_rel; - var z_x = Rx31 * x_rel + Rx32 * y_rel + Rx33 * z_rel; - # - # subsequent rotation about y-axis - # X_xy = Ry*X_x - var x_xy = Ry11 * x_x + Ry12 * y_x + Ry13 * z_x; - var y_xy = Ry21 * x_x + Ry22 * y_x + Ry23 * z_x; - var z_xy = Ry31 * x_x + Ry32 * y_x + Ry33 * z_x; - # - # subsequent rotation about z-axis: - # X_xyz = Rz*X_xy - var x_xyz = Rz11 * x_xy + Rz12 * y_xy + Rz13 * z_xy; - var y_xyz = Rz21 * x_xy + Rz22 * y_xy + Rz23 * z_xy; - var z_xyz = Rz31 * x_xy + Rz32 * y_xy + Rz33 * z_xy; - - # Back transformation X_rel = X-Xr = (x-xr, y-yr, z-zr) - var xn = xr + x_xyz; - var yn = yr + y_xyz; - var zn = zr + z_xyz; - - var Xn = [xn,yn,zn]; - - return Xn; - + + # --------------------------------------------------------------------------------- + # rotates point (x,y,z) about all 3 cartesian axis + # center of rotation (xr,yr,zr) + # angle of rotation about x-axis = alpha + # angle of rotation about y-axis = beta + # angle of rotation about z-axis = gamma + # delivers new point coordinates (x_new,y_new,z_new) + # --------------------------------------------------------------------------------- + # + # + # Definitions: + # ---------------- + # + # x y z + # alpha beta gamma + # + # + # z + # | y + # | / + # |/ + # ----->x + # + #---------------------------------------------------------------------------------- + + # Transformation in rotation-system X_rel = X-Xr = (x-xr, y-yr, z-zr) + var x_rel = x-xr; + var y_rel = y-yr; + var z_rel = z-zr; + + # Trigonometry + + var alpha_rad = D2R * alpha_deg; + var beta_rad = D2R * beta_deg; + var gamma_rad = D2R * gamma_deg; + + var sin_alpha = math.sin(alpha_rad); + var cos_alpha = math.cos(alpha_rad); + + var sin_beta = math.sin(beta_rad); + var cos_beta = math.cos(beta_rad); + + var sin_gamma = math.sin(gamma_rad); + var cos_gamma = math.cos(gamma_rad); + + # Matrices + # + # Rotate about x-axis Rx(alpha) + # + # Rx11 Rx12 Rx13 1 0 0 + # Rx(alpha)= Rx21 Rx22 Rx23 = 0 cos(alpha) -sin(alpha) + # Rx31 Rx32 Rx33 0 sin(alpha) cos(alpha) + # + var Rx11 = 1.; + var Rx12 = 0.; + var Rx13 = 0.; + var Rx21 = 0.; + var Rx22 = cos_alpha; + var Rx23 = - sin_alpha; + var Rx31 = 0.; + var Rx32 = sin_alpha; + var Rx33 = cos_alpha; + # + # Rotate about y-axis Ry(beta) + # + # Ry11 Ry12 Ry13 cos(beta) 0 sin(beta) + # Ry(beta)= Ry21 Ry22 Ry23 = 0 1 0 + # Ry31 Ry32 Ry33 -sin(beta) 0 cos(beta) + # + var Ry11 = cos_beta; + var Ry12 = 0.; + var Ry13 = sin_beta; + var Ry21 = 0.; + var Ry22 = 1.; + var Ry23 = 0.; + var Ry31 = - sin_beta; + var Ry32 = 0.; + var Ry33 = cos_beta; + # + # Rotate about z-axis Rz(gamma) + # + # Rz11 Rz12 Rz13 cos(gamma) -sin(gamma) 0 + # Rz(gamma)= Rz21 Rz22 Rz23 = sin(gamma) cos(gamma) 0 + # Rz31 Rz32 Rz33 0 0 1 + # + var Rz11 = cos_gamma; + var Rz12 = - sin_gamma; + var Rz13 = 0.; + var Rz21 = sin_gamma; + var Rz22 = cos_gamma; + var Rz23 = 0.; + var Rz31 = 0.; + var Rz32 = 0.; + var Rz33 = 1.; + # + # First rotation about x-axis + # X_x = Rx*X_rel + var x_x = Rx11 * x_rel + Rx12 * y_rel + Rx13 * z_rel; + var y_x = Rx21 * x_rel + Rx22 * y_rel + Rx23 * z_rel; + var z_x = Rx31 * x_rel + Rx32 * y_rel + Rx33 * z_rel; + # + # subsequent rotation about y-axis + # X_xy = Ry*X_x + var x_xy = Ry11 * x_x + Ry12 * y_x + Ry13 * z_x; + var y_xy = Ry21 * x_x + Ry22 * y_x + Ry23 * z_x; + var z_xy = Ry31 * x_x + Ry32 * y_x + Ry33 * z_x; + # + # subsequent rotation about z-axis: + # X_xyz = Rz*X_xy + var x_xyz = Rz11 * x_xy + Rz12 * y_xy + Rz13 * z_xy; + var y_xyz = Rz21 * x_xy + Rz22 * y_xy + Rz23 * z_xy; + var z_xyz = Rz31 * x_xy + Rz32 * y_xy + Rz33 * z_xy; + + # Back transformation X_rel = X-Xr = (x-xr, y-yr, z-zr) + var xn = xr + x_xyz; + var yn = yr + y_xyz; + var zn = zr + z_xyz; + + var Xn = [xn,yn,zn]; + + return Xn; + } +var groundspeed_kt = props.globals.getNode("velocities/groundspeed-kt"); +var alt_agl_ft = props.globals.getNode("position/altitude-agl-ft"); + +var weight_lb = nil; +var empty_weight_lb = nil; + +var ls = setlistener("/sim/signals/fdm-initialized", func() { + if( fdm == "jsb" ) { + weight_lb = props.globals.getNode("fdm/jsbsim/inertia/weight-lbs"); + empty_weight_lb = props.globals.getNode("fdm/jsbsim/inertia/empty-weight-lbs"); + } else if ( fdm == "yasim" ) { + weight_lb = props.globals.getNode("yasim/gross-weight-lbs"); + } + check_aircraft_tow_settings(); + removelistener(ls); +}); + +var winch_target = 0.8; +var winch_throttle = 0.0; + +var winch_idle_force = 1000; # N +var winch_idle_speed = 4; # mps + +var acceleration_N_s = nil; +var acceleration_m_s_s = nil; + +var phase = 0; +var alpha = 0.0; +var requested_change = 0.0; # positive: request to speed up, negative: request to slow down, 0: auto speed + +########### +## AUTOMATIC WINCH DRIVER +########### + +# Phase Action description Triggered by +# 0 slowly pull rope until tight (currently omitted) runWinch() +# 1 winch running on idle until aircraft is moving "rope tight" command +# 2 winch accelerating to pre-defined RPM "ready" command +# 3 glider in air, winch adjusts RPM based on angular velocity and faster/slower commands by pilot "clear of ground" command +# 4 winch slowly decelerates to idle aircraft 10-20 deg from automatic release angle +# 5 winch pulls in remaining rope "rope released" command + +## Calculation of predefined targets: +# -weight factor, accounts for additional weight (second pilot, water ballast) +# -wind factors, account for different tow speed due to headwind/tailwind and crosswind + +var target_force = 0.0; # N +var target_speed = 0.0; # m/s + +var auto_winch_driver_init = func () { + target_force = aircraft_settings.winch.typical_tow_force.getDoubleValue(); + target_speed = aircraft_settings.winch.typical_tow_speed.getDoubleValue() / 3.6; # convert kph to mps + + # Adjust target speed and force for current wind (increase for tail- and crosswind, reduce for headwind) + + # define wind frame: expect from 15 kts tailwind to 15 kts headwind -> translates to a change of +- 0.1 + var launch_heading_deg = winch_hash.rope.hdg.getDoubleValue(); + var wind_from_heading = wind_from.getDoubleValue(); + var wind_speed = wind_speed_kt.getDoubleValue(); + + # calculate linear component + var rel_wind_heading = launch_heading_deg - wind_from_heading; + var wind_linear = wind_speed * math.cos( rel_wind_heading * D2R ); # negative values mean tailwind + var wind_normal = wind_speed * math.cos( -( 90 - rel_wind_heading ) * D2R ); # negative values mean wind from the left + # compensate for tail/headwind + var wind_linear_add = math.clamp( -wind_linear / 150, -0.1, 0.1 ); + # compensate for crosswind + var wind_normal_add = math.min( math.abs( wind_normal ) / 150, 0.1 ); + + # apply compensation to winch driver variables + target_force += ( wind_linear_add + wind_normal_add ) * config.winch.max_force.getDoubleValue(); + target_speed += ( wind_linear_add + wind_normal_add ) * config.winch.max_spool_speed.getDoubleValue(); + + # set acceleration/deceleration variables + acceleration_N_s = config.winch.force_acceleration.getDoubleValue(); + acceleration_m_s_s = config.winch.spool_acceleration.getDoubleValue(); + + # put winch driver into initial state + phase = 0; +} + +var my_pos = nil; +var winch_pos = geo.Coord.new(); + +var auto_winch_driver = func () { + if( !winch_hash.clutched.getBoolValue() ) return; + + var dt = delta_time.getDoubleValue(); + if( winch_hash.type != 0 and winch_hash.type != 1 ) { + die("(towing system): FATAL: unsupported winch type!"); + } + + if( phase == 3 or phase == 4 ){ + my_pos = geo.aircraft_position(); + # Winch position + var winch_global_pos_x = winch_hash.global_pos[0].getDoubleValue(); + var winch_global_pos_y = winch_hash.global_pos[1].getDoubleValue(); + var winch_global_pos_z = winch_hash.global_pos[2].getDoubleValue(); + + var winch_geod = carttogeod(winch_global_pos_x, winch_global_pos_y, winch_global_pos_z); + winch_pos.set_latlon( winch_geod[0], winch_geod[1], winch_geod[2] ); + + alpha = math.asin( ( my_pos.alt() - winch_pos.alt() ) / winch_pos.direct_distance_to( my_pos ) ) * R2D; + } + + if( phase == 0 ) { + phase = 1; + } + if( phase == 1 ) { + if( winch_hash.type == 0 ){ + force_setting = winch_idle_force; + } else { + speed_setting = winch_idle_speed; + } + + # Condition for going into the next phase + if( groundspeed_kt.getDoubleValue() > 1.0 ) { + phase = 2; + write_message( "launch-signaller", "Ready!" ); + write_message( "winch-driver", "Ready"); + } + } else if ( phase == 2 or phase == 2.5 ){ + # Accelerate to target value + if( winch_hash.type == 0 ){ + if( force_setting < target_force ){ + force_setting += acceleration_N_s * dt; + } else { + force_setting = target_force; + if( phase == 2.5 ){ + phase = 3; + } + } + } else { + if( speed_setting < target_speed ){ + speed_setting += acceleration_m_s_s * dt; + } else { + speed_setting = target_speed; + if( phase == 2.5 ){ + phase = 3; + } + } + } + + # Condition for going into the next phase + if( alt_agl_ft.getDoubleValue() > 10 and phase < 2.5 ){ + phase = 2.5; # stay here until we have reached the target value + write_message( "launch-signaller", "Clear of ground!" ); + write_message( "winch-driver", "Clear"); + } + } else if ( phase == 3 ){ + + if( requested_change != 0 ){ + if( winch_hash.type == 0 ){ + force_setting += requested_change * config.winch.max_force.getDoubleValue() * dt; + } else { + speed_setting += requested_change * config.winch.max_spool_speed.getDoubleValue() * dt; + } + requested_change -= requested_change * dt; + if( math.abs( requested_change ) < dt ){ + requested_change = 0.0; + } + } + + var thr_speed = 0.8; + var thr_angle = 0.5; + + if( winch_hash.type == 0 ){ + + var frac_speed = math.clamp( 0.2 + 1 - ( 1 / ( 1 - thr_speed ) ) * ( ( winch_hash.actual_spool_speed.getDoubleValue() / config.winch.max_spool_speed.getDoubleValue() ) - thr_speed ) * 0.8, 0.0, 1.0 ); + var frac_angle = math.clamp( 0.2 + 1 - ( 1 / ( 1 - thr_angle ) ) * ( ( alpha / ( aircraft_settings.winch.automatic_release_angle.getDoubleValue() - 30 ) ) - thr_angle ) * 0.8, 0.0, 1.0 ); + force_setting = target_force * frac_speed * frac_angle; + + } elsif( winch_hash.type == 1 ){ + + var frac_speed = math.clamp( 0.2 + 1 - ( 1 / ( 1 - thr_speed ) ) * ( ( winch_hash.actual_spool_speed.getDoubleValue() / config.winch.max_spool_speed.getDoubleValue() ) - thr_speed ) * 0.8, 0.0, 1.0 ); + var frac_angle = math.clamp( 0.2 + 1 - ( 1 / ( 1 - thr_angle ) ) * ( ( alpha / ( aircraft_settings.winch.automatic_release_angle.getDoubleValue() - 30 ) ) - thr_angle ) * 0.8, 0.0, 1.0 ); + speed_setting = target_speed * frac_speed * frac_angle; + + } + + if( ( alpha >= ( aircraft_settings.winch.automatic_release_angle.getDoubleValue() - 10 ) ) ) { + phase = 4; + } + } else if ( phase == 4 ){ + # Reduce values back to idle + if( speed_setting > winch_idle_speed ){ + var deceleration = 3 * math.clamp( ( speed_setting - winch_idle_speed ) / ( aircraft_settings.winch.automatic_release_angle.getDoubleValue() - 2 - alpha ), 5, acceleration_m_s_s ); + speed_setting -= deceleration * dt; + } else { + speed_setting = winch_idle_speed; + } + + # Condition for going into the next phase is set by the releaseHitch() command + } else if ( phase == 5 ){ + if( winch_hash.type == 0 ){ + force_setting = winch_idle_force; + } else { + speed_setting = winch_idle_speed; + } + auto_winch_driver_update.stop(); + } +} + +var auto_winch_driver_update = maketimer( 0.0, auto_winch_driver ); +auto_winch_driver_update.simulatedTime = 1; + + +var winch_faster = func ( d = 0.1 ) { + write_message( "pilot", "Faster!"); + + # Skip acceleration to pre-set target force/speed in case of pilot command + if( phase == 2.5 ){ + phase = 3; + } + + if( phase != 3 ){ + write_message( "winch-driver", "Unable!"); + return; + } + + if( ( winch_hash.type == 0 and ( force_setting + d * config.winch.max_force.getDoubleValue() ) <= config.winch.max_force.getDoubleValue() ) or + ( winch_hash.type == 1 and ( speed_setting + d * config.winch.max_spool_speed.getDoubleValue() ) <= config.winch.max_spool_speed.getDoubleValue() ) ){ + + requested_change = d; + write_message( "winch-driver", "Faster"); + } else { + if( winch_hash.type == 0 ){ + force_setting = config.winch.max_force.getDoubleValue(); + } else { + speed_setting = config.winch.max_spool_speed.getDoubleValue(); + } + write_message( "winch-driver", "At maximum!"); + } +} +var winch_slower = func ( d = 0.1 ) { + write_message( "pilot", "Slower!"); + + # Skip acceleration to pre-set target force/speed in case of pilot command + if( phase == 2.5 ){ + phase = 3; + } + + if( phase != 3 ){ + write_message( "winch-driver", "Unable!"); + return; + } + + if( ( winch_hash.type == 0 and force_setting - d * config.winch.max_force.getDoubleValue() >= winch_idle_force ) or + ( winch_hash.type == 1 and speed_setting - d * config.winch.max_spool_speed.getDoubleValue() >= winch_idle_speed ) ){ + + requested_change = -d; + write_message( "winch-driver", "Slower"); + } else { + if( winch_hash.type == 0 ){ + force_setting = winch_idle_force; + } else { + speed_setting = winch_idle_speed; + } + write_message( "winch-driver", "At minimum!"); + } +} + + ################################################################################################################################## @@ -1819,7 +2349,6 @@ var PointRotate3D = func (x,y,z,xr,yr,zr,alpha_deg,beta_deg,gamma_deg){ # ------ # # - animate rope slack -# - pull in towrope: take sink rate of rope into account # - dynamic ID for ai-rope-model # # Please contact D_NXKT at yahoo.de for bug-reports, suggestions, ... diff --git a/gui/dialogs/hitch.xml b/gui/dialogs/hitch.xml new file mode 100644 index 000000000..c60292ffd --- /dev/null +++ b/gui/dialogs/hitch.xml @@ -0,0 +1,965 @@ + + + + + hitch_dialog + vbox + 5 + false + 500 + + + + + + + removelistener( w_type_ls ); + removelistener( w_cofo_ls ); + removelistener( w_cosp_ls ); + + + + + + hbox + + true + + + + + + true + + + + + + + + + hbox + + true + + + + + + true + + + + + hbox + + left + + + + hbox + right + + + true + %6dN + /sim/hitches/aerotow/tow/elastic-constant + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + aerotow_elastic_constant + /sim/hitches/aerotow/tow/elastic-constant + 0 + 100000 + 10 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %1.3fkg/m + /sim/hitches/aerotow/tow/weight-per-m-kg-m + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + aerotow_weight_m + /sim/hitches/aerotow/tow/weight-per-m-kg-m + 0 + 1 + 0.001 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %3dm + sim/hitches/aerotow/tow/length + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + aerotow_tow_length + sim/hitches/aerotow/tow/length + 5 + 200 + 5 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %3.1fmm + /sim/hitches/aerotow/rope/rope-diameter-mm + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + aerotow_rope_diameter + /sim/hitches/aerotow/rope/rope-diameter-mm + 0 + 50 + 0.5 + + dialog-apply + + + + 60 + + + + + + + + + + + + hbox + + true + + + + + + true + + + + + hbox + + left + + + + hbox + right + + true + + + const_force + + 1 + /sim/hitches/winch/constant-force + + dialog-apply + const_force + + + + const_speed + + 1 + /sim/hitches/winch/constant-speed + + dialog-apply + const_speed + + + + + + hbox + + left + + + + hbox + right + + + true + %6dN + /sim/hitches/winch/tow/elastic-constant + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + winch_elastic_constant + /sim/hitches/winch/tow/elastic-constant + 0 + 100000 + 10 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %1.3fkg/m + /sim/hitches/winch/tow/weight-per-m-kg-m + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + winch_weight_m + /sim/hitches/winch/tow/weight-per-m-kg-m + 0 + 1 + 0.001 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %4.0fm + /sim/hitches/winch/winch/initial-tow-length-m + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + winch_tow_length + /sim/hitches/winch/winch/initial-tow-length-m + 20 + 2000 + 5 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %2.1fmm + /sim/hitches/winch/rope/rope-diameter-mm + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + winch_rope_diameter + /sim/hitches/winch/rope/rope-diameter-mm + 0 + 50 + 0.5 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %4.0fm + /sim/hitches/winch/winch/max-tow-length-m + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + max_tow_length + /sim/hitches/winch/winch/max-tow-length-m + 20 + 2000 + 5 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %2d m/s + sim/hitches/winch/winch/max-spool-speed-m-s + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + max_spool_speed + sim/hitches/winch/winch/max-spool-speed-m-s + 5 + 80 + 5 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %4d N/s + sim/hitches/winch/winch/force-acceleration-N-s + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + force_accel + sim/hitches/winch/winch/force-acceleration-N-s + 100 + 5000 + 100 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %3.1f m/s^2 + sim/hitches/winch/winch/spool-acceleration-m-s-s + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + spool_accel + sim/hitches/winch/winch/spool-acceleration-m-s-s + 1 + 50 + 1 + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %3d m/s + sim/hitches/winch/winch/max-unspool-speed-m-s + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + max_unspool_speed + sim/hitches/winch/winch/max-unspool-speed-m-s + 0 + 10000 + 100. + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %5dN + /sim/hitches/winch/winch/max-force-N + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + max_force + /sim/hitches/winch/winch/max-force-N + 0 + 10000 + 100. + + dialog-apply + + + + 60 + + + + + + + + hbox + + left + + + + hbox + right + + + true + %3d kW + /sim/hitches/winch/winch/max-power-kW + + 0.2 + 0.9 + 0.2 + + + + true + + + + 60 + + + + + + max_power + /sim/hitches/winch/winch/max-power-kW + 0 + 300 + 1 + + dialog-apply + + + + 60 + + + + + + + + + + + + hbox + + left + + + + hbox + right + + true + + + msg_launch_signaller + + 1 + /sim/hitches/winch/messages/launch-signaller + + dialog-apply + msg_launch_signaller + + + + msg_winch_driver + + 1 + /sim/hitches/winch/messages/winch-driver + + dialog-apply + msg_winch_driver + + + + + + hbox + right + + true + + + msg_pilot + + 1 + /sim/hitches/winch/messages/pilot + + dialog-apply + msg_pilot + + + + msg_remote_ac + + 1 + /sim/hitches/winch/messages/remote-ac + + dialog-apply + msg_remote_ac + + + + + + + + + + hbox + + true + + + + true + + + + diff --git a/gui/dialogs/winch_failure.xml b/gui/dialogs/winch_failure.xml new file mode 100644 index 000000000..46d66a24b --- /dev/null +++ b/gui/dialogs/winch_failure.xml @@ -0,0 +1,184 @@ + + + + winch_failure_dialog + vbox + + + + hbox + + true + + + center + + + + + true + + + + + + + + + + + + vbox + + + left + brkge_enable + + /sim/hitches/winch/breakage/enabled + + dialog-apply + brkge_enable + + + + + hbox + + + + + + /sim/hitches/winch/breakage/enabled + + /sim/hitches/winch/breakage/random + + + 0 + 500 + /sim/hitches/winch/breakage/height + + dialog-apply + + + + + /sim/hitches/winch/breakage/enabled + + /sim/hitches/winch/breakage/random + + + /sim/hitches/winch/breakage/height + + %3d m AGL + true + + + + + /sim/hitches/winch/breakage/enabled + + + + + + + /sim/hitches/winch/breakage/enabled + + + brkge_random + /sim/hitches/winch/breakage/random + + dialog-apply + brkge_random + + + + + + + + + + + + vbox + + + left + l_o_p_enable + + /sim/hitches/winch/loss-of-power/enabled + + dialog-apply + l_o_p_enable + + + + + hbox + + + + + + /sim/hitches/winch/loss-of-power/enabled + + /sim/hitches/winch/loss-of-power/random + + + 0 + 500 + /sim/hitches/winch/loss-of-power/height + + dialog-apply + + + + + /sim/hitches/winch/loss-of-power/enabled + + /sim/hitches/winch/loss-of-power/random + + + /sim/hitches/winch/loss-of-power/height + + %3d m AGL + true + + + + + /sim/hitches/winch/loss-of-power/enabled + + + + + + + /sim/hitches/winch/loss-of-power/enabled + + + l_o_p_random + /sim/hitches/winch/loss-of-power/random + + dialog-apply + l_o_p_random + + + + + +