1
0
Fork 0
fgdata/Nasal/towing/hitch.nas

1648 lines
73 KiB
Text

#
# Version: 10. March 2014
#
# Purpose of this routine:
# ------------------------
#
# - Create visible winch- and towropes for gliders and towplanes
# - Support of aerotowing and winch for JSBSim-aircrafts (glider and towplanes)
#
# This routine is very similar to /FDM/YASim/Hitch.cpp
# Aerotowing is fully compatible to the YASim functionality.
# This means that YASim-gliders could be towed by JSBSim-aircrafts and vice versa.
# Setup-instructions with copy and paste examples are given below:
#
#
# Setup of visible winch/towropes for Yasim-aircrafts:
# ----------------------------------------------------
#
# YASim-aircrafts with winch/aerotowing functionality should work out of the box.
# Optional you can customize the rope-diameter by adding the following to "your_aircraft-set.xml":
# </sim>
# <hitches>
# <aerotow>
# <rope>
# <rope-diameter-mm type ="float">10</rope-diameter-mm>
# </rope>
# </aerotow>
# <winch>
# <rope>
# <rope-diameter-mm type ="float">20</rope-diameter-mm>
# </rope>
# </winch>
# </hitches>
# </sim>
#
# That's all!
#
#
#
# Support of aerotowing and winch for JSBSim-aircrafts (glider and towplanes):
# ----------------------------------------------------------------------------
#
# 1. Define a hitch in the JSBSim-File. Coordinates according to JSBSims structural frame of reference
# (x points to the tail, y points to the right wing, z points upwards). Be careful! This
# coordinates don't appear in the property tree. You can only check them with test flights!
# The visible towrope is NOT an indicator of correct settings!
# Unit must be "LBS", frame must be "BODY". The force name is arbitrary.
#
# <external_reactions>
# <force name="hitch" frame="BODY" unit="LBS" >
# <location unit="M">
# <x>3.65</x>
# <y> 0.0</y>
# <z>-0.12</z>
# </location>
# <direction>
# <x>0.0</x>
# <y>0.0</y>
# <z>0.0</z>
# </direction>
# </force>
# </external_reactions>
# 2. Define controls for aerotowing and winch.
# Add the following key bindings in "yourAircraft-set.xml":
# <input>
# <keyboard>
#
# <key n="15">
# <name>Ctrl-o</name>
# <desc>Find aircraft for aerotow</desc>
# <binding>
# <command>nasal</command>
# <script>towing.findBestAIObject()</script>
# </binding>
# </key>
#
# <key n="111">
# <name>o</name>
# <desc>Lock aerotow-hook</desc>
# <binding>
# <command>nasal</command>
# <script>towing.closeHitch()</script>
# </binding>
# </key>
#
# <key n="79">
# <name>O</name>
# <desc>Open aerotow-hook</desc>
# <binding>
# <command>nasal</command>
# <script>towing.releaseHitch("aerotow")</script>
# </binding>
# </key>
#
# <key n="23">
# <name>Ctrl-w</name>
# <desc>Place Winch and hook in</desc>
# <binding>
# <command>nasal</command>
# <script>towing.setWinchPositionAuto()</script>
# </binding>
# </key>
#
# <key n="119">
# <name>w</name>
# <desc>Start winch</desc>
# <binding>
# <command>nasal</command>
# <script>towing.runWinch()</script>
# </binding>
# </key>
#
# <key n="87">
# <name>W</name>
# <desc>Open winch-hook</desc>
# <binding>
# <command>nasal</command>
# <script>towing.releaseHitch("winch")</script>
# </binding>
# </key>
#
# </keyboard>
# </input>
#
# For towplanes only "key n=79" (Open aerotow-hook) is required!
#
#
# 3. Set mandatory properties:
#<sim>
# <hitches>
# <aerotow>
# <force_name_jsbsim type="string">hitch</force_name_jsbsim>
# <local-pos-x type="float">1.5</local-pos-x>
# <local-pos-y type="float"> 0.00</local-pos-y>
# <local-pos-z type="float">-0.3</local-pos-z>
# <force-is-calculated-by-other type="bool">false</force-is-calculated-by-other>
# <mp-auto-connect-period type="float">0.0</mp-auto-connect-period>
# </aerotow>
# <winch>
# <force_name_jsbsim type="string">hitch</force_name_jsbsim>
# <local-pos-x type="float">0.0</local-pos-x>
# <local-pos-y type="float">0.0</local-pos-y>
# <local-pos-z type="float">0.0</local-pos-z>
# </winch>
# </hitches>
#</sim>
#
# "force_name_jsbsim" must be the external force name in JSBSim.
#
# IMPORTANT:
# JSBSim doesn't provide the hitch coordinates in the property tree. Hence you must set them again to get a
# visible towrope (local-pos-x/y/z). Unfortunately the coordinate systems are different. Here the coordinates
# for the "YASim-System" are needed (x points to the nose, y points to the left wing, z points upwards).
# If you see the rope at the expected position "local-pos-x/y/z" are correct.
#
# "force-is-calculated-by-other" should be "false" for gliders and "true" for towplanes.
# "mp-auto-connect-period" is only needed for towplanes and should be "1".
# 4. Set optional properties:
#<sim>
# <hitches>
# <aerotow>
# <tow>
# <brake-force type="float">6000</brake-force>
# <elastic-constant type="float">9000</elastic-constant>
# </tow>
# <rope>
# <rope-diameter-mm type="float">20</rope-diameter-mm>
# </rope>
# </aerotow>
# <winch>
# <automatic-release-angle-deg type="float">70.</automatic-release-angle-deg>
# <winch>
# <initial-tow-length-m type="float">1000.</initial-tow-length-m>
# <max-tow-length-m type="float">1500.</max-tow-length-m>
# <max-force type="float">800.</max-force>
# <max-power-kW type="float">100.</max-power-kW>
# <max-spool-speed-m-s type="float">15.</max-spool-speed-m-s>
# <max-unspool-speed-m-s type="float">20.</max-unspool-speed-m-s>
# <spool-acceleration-m-s-s type="float">8.</spool-acceleration-m-s-s>
# <rel-speed alias="/sim/hitches/winch/winch/actual-spool-speed-m-s"/>
# </winch>
# <tow>
# <break-force type="float">10000</break-force>
# <elastic-constant type="float">40000</elastic-constant>
# <weight-per-m-kg-m type="float">0.01</weight-per-m-kg-m>
# </tow>
# <rope>
# <rope-diameter-mm type="float">40</rope-diameter-mm>
# </rope>
# </winch>
# </hitches>
#<sim>
#
# That's it!
################################################## 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/".
# 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
# from a former owner of this node.
#
# The following properties are transmitted in multiplayer:
# "sim/hitches/aerotow/tow/elastic-constant"
# "sim/hitches/aerotow/tow/weight-per-m-kg-m"
# "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/end-force-x"
# "sim/hitches/aerotow/tow/end-force-y"
# "sim/hitches/aerotow/tow/end-force-z"
# "sim/hitches/aerotow/is-slave"
# "sim/hitches/aerotow/speed-in-tow-direction"
# "sim/hitches/aerotow/open", open);
# "sim/hitches/aerotow/local-pos-x"
# "sim/hitches/aerotow/local-pos-y"
# "sim/hitches/aerotow/local-pos-z"
#
##############################################################################################################
# ######################################################################################################################
# check, if towing support makes sense
# ######################################################################################################################
# Check if node "sim/hitches" is defined. If not, return!
if (props.globals.getNode("sim/hitches") == nil ) return;
print("towing is active!");
# ######################################################################################################################
# set defaults / initialize at startup
# ######################################################################################################################
# set defaults for properties that are NOT already defined
# yasim properties for aerotow (should be already defined for yasim aircrafts but not for JSBSim aircrafts
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);
# additional properties
if (props.globals.getNode("sim/hitches/aerotow/oldOpen") == nil )
props.globals.getNode("sim/hitches/aerotow/oldOpen", 1).setBoolValue(1);
# 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.);
# 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.);
}
# yasim properties for winch (should be already defined for yasim aircrafts but not for JSBSim aircrafts
#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.);
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);
# 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.);
# 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.);
# 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.);
}
# ######################################################################################################################
# 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 aircrafts)
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 aircrafts)
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 );
} # end towing
# ######################################################################################################################
# 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 AI_id = 0; # id of towplane
var callsign = 0; # callsign of towplane
var aiPosition = geo.Coord.new(); # current processed ai-plane
var lat_deg = 0; # latitude of current processed aiobject
var lon_deg = 0; # longitude of current processed aiobject
var alt_m = 0; # altitude of current processed aiobject
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-aircrafts
#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" ) nodeIsAiAircraft = 1;
if ( sprintf("%11s",node) == "multiplayer" ) nodeIsMpAircraft = 1;
#print("found NodeName=",node," nodeIsAiAircraft=",nodeIsAiAircraft," nodeIsMpAircraft=",nodeIsMpAircraft );
if( !nodeIsAiAircraft and !nodeIsMpAircraft ) continue;
if( running_as_autoconnect ) {
if ( !nodeIsMpAircraft ) continue;
if ( aimember.getValue("sim/hitches/aerotow/open") == 1 ) continue; # if mp hook open, auto-connect isn't possible
if (mycallsign != aimember.getValue("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign") ) continue ; # I'm the wrong one
}
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;
}
}
}
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);
}
# set the dist value to some value below the tow length (if not, the hitch would open the next calc force run
distance_m = towlength_m * 0.5;
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!"));
}
}
} # End function findBestAIObject
# ######################################################################################################################
# Start the towing animation ASAP
towing();
# ######################################################################################################################
# aerotow function
# ######################################################################################################################
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)
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);
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) {
# 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();
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
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-aircrafts
# tow-end-forces must be reported in N to be consiststent to Yasim-aircrafts
# 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 aircrafts
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 an 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
else{
if ( !aimember.getNode("valid").getValue() ) { # MP-aircraft is accessable (node is valid)
#print("MP-aircraft isn't valid!");
props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(1); # open my hitch
setprop("sim/messages/atc", sprintf("MP-aircraft disappeared!" ));
}
}
} # end: check id != nil
} # end: loop over aiobjects
} # end function aerotow
# ######################################################################################################################
# winch function
# ######################################################################################################################
var winch = func (open){
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)
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-aircrafts
# tow-end-forces must be reported in N to be consiststent to Yasim-aircrafts
# 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)
} # end function winch
# ######################################################################################################################
# create towrope
# ######################################################################################################################
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
}
# ######################################################################################################################
# get the next free id of "models/model" members
# ######################################################################################################################
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);
}
# ######################################################################################################################
# close hitch
# ######################################################################################################################
var closeHitch = func {
#print("closeHitch");
setprop("sim/hitches/aerotow/open", "false");
setprop("sim/hitches/aerotow/mp_oldOpen", "true");
} # End function closeHitch
# ######################################################################################################################
# release hitch
# ######################################################################################################################
var releaseHitch = func (device){
#print("releaseHitch");
if ( getprop("sim/flight-model") == "yasim" ) return; # bypass this routine for Yasim-aircrafts
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.); #
}
} # End function releaseHitch
# ######################################################################################################################
# remove/delete towrope
# ######################################################################################################################
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);
}
}
# ######################################################################################################################
# pull in towrope after hitch has been opened
# ######################################################################################################################
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
}
# ######################################################################################################################
# set some AI-object default values
# ######################################################################################################################
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.);
}
}
}
}
# ######################################################################################################################
# place winch model
# ######################################################################################################################
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);
} # End function setWinchPositionAuto
# ######################################################################################################################
# clutch / un-clutch winch
# ######################################################################################################################
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!"));
}
} # End function runWinch
# ######################################################################################################################
# release winch
# ######################################################################################################################
var releaseWinch = func {
setprop("sim/hitches/winch/open","true");
} # End function releaseWinch
# ######################################################################################################################
# point transformation
# ######################################################################################################################
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;
}
##################################################################################################################################
# todo:
# ------
#
# - 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, ...
#