2 # Version: 10. March 2014
4 # Purpose of this routine:
5 # ------------------------
7 # - Create visible winch- and towropes for gliders and towplanes
8 # - Support of aerotowing and winch for JSBSim-aircrafts (glider and towplanes)
10 # This routine is very similar to /FDM/YASim/Hitch.cpp
11 # Aerotowing is fully compatible to the YASim functionality.
12 # This means that YASim-gliders could be towed by JSBSim-aircrafts and vice versa.
13 # Setup-instructions with copy and paste examples are given below:
16 # Setup of visible winch/towropes for Yasim-aircrafts:
17 # ----------------------------------------------------
19 # YASim-aircrafts with winch/aerotowing functionality should work out of the box.
20 # Optional you can customize the rope-diameter by adding the following to "your_aircraft-set.xml":
25 # <rope-diameter-mm type ="float">10</rope-diameter-mm>
30 # <rope-diameter-mm type ="float">20</rope-diameter-mm>
40 # Support of aerotowing and winch for JSBSim-aircrafts (glider and towplanes):
41 # ----------------------------------------------------------------------------
43 # 1. Define a hitch in the JSBSim-File. Coordinates according to JSBSims structural frame of reference
44 # (x points to the tail, y points to the right wing, z points upwards). Be careful! This
45 # coordinates don't appear in the property tree. You can only check them with test flights!
46 # The visible towrope is NOT an indicator of correct settings!
47 # Unit must be "LBS", frame must be "BODY". The force name is arbitrary.
49 # <external_reactions>
50 # <force name="hitch" frame="BODY" unit="LBS" >
62 # </external_reactions>
64 # 2. Define controls for aerotowing and winch.
65 # Add the following key bindings in "yourAircraft-set.xml":
71 # <desc>Find aircraft for aerotow</desc>
73 # <command>nasal</command>
74 # <script>towing.findBestAIObject()</script>
80 # <desc>Lock aerotow-hook</desc>
82 # <command>nasal</command>
83 # <script>towing.closeHitch()</script>
89 # <desc>Open aerotow-hook</desc>
91 # <command>nasal</command>
92 # <script>towing.releaseHitch("aerotow")</script>
98 # <desc>Place Winch and hook in</desc>
100 # <command>nasal</command>
101 # <script>towing.setWinchPositionAuto()</script>
107 # <desc>Start winch</desc>
109 # <command>nasal</command>
110 # <script>towing.runWinch()</script>
116 # <desc>Open winch-hook</desc>
118 # <command>nasal</command>
119 # <script>towing.releaseHitch("winch")</script>
126 # For towplanes only "key n=79" (Open aerotow-hook) is required!
129 # 3. Set mandatory properties:
133 # <force_name_jsbsim type="string">hitch</force_name_jsbsim>
134 # <local-pos-x type="float">1.5</local-pos-x>
135 # <local-pos-y type="float"> 0.00</local-pos-y>
136 # <local-pos-z type="float">-0.3</local-pos-z>
137 # <force-is-calculated-by-other type="bool">false</force-is-calculated-by-other>
138 # <mp-auto-connect-period type="float">0.0</mp-auto-connect-period>
141 # <force_name_jsbsim type="string">hitch</force_name_jsbsim>
142 # <local-pos-x type="float">0.0</local-pos-x>
143 # <local-pos-y type="float">0.0</local-pos-y>
144 # <local-pos-z type="float">0.0</local-pos-z>
149 # "force_name_jsbsim" must be the external force name in JSBSim.
152 # JSBSim doesn't provide the hitch coordinates in the property tree. Hence you must set them again to get a
153 # visible towrope (local-pos-x/y/z). Unfortunately the coordinate systems are different. Here the coordinates
154 # for the "YASim-System" are needed (x points to the nose, y points to the left wing, z points upwards).
155 # If you see the rope at the expected position "local-pos-x/y/z" are correct.
157 # "force-is-calculated-by-other" should be "false" for gliders and "true" for towplanes.
158 # "mp-auto-connect-period" is only needed for towplanes and should be "1".
161 # 4. Set optional properties:
166 # <brake-force type="float">6000</brake-force>
167 # <elastic-constant type="float">9000</elastic-constant>
170 # <rope-diameter-mm type="float">20</rope-diameter-mm>
174 # <automatic-release-angle-deg type="float">70.</automatic-release-angle-deg>
176 # <initial-tow-length-m type="float">1000.</initial-tow-length-m>
177 # <max-tow-length-m type="float">1500.</max-tow-length-m>
178 # <max-force type="float">800.</max-force>
179 # <max-power-kW type="float">100.</max-power-kW>
180 # <max-spool-speed-m-s type="float">15.</max-spool-speed-m-s>
181 # <max-unspool-speed-m-s type="float">20.</max-unspool-speed-m-s>
182 # <spool-acceleration-m-s-s type="float">8.</spool-acceleration-m-s-s>
183 # <rel-speed alias="/sim/hitches/winch/winch/actual-spool-speed-m-s"/>
186 # <break-force type="float">10000</break-force>
187 # <elastic-constant type="float">40000</elastic-constant>
188 # <weight-per-m-kg-m type="float">0.01</weight-per-m-kg-m>
191 # <rope-diameter-mm type="float">40</rope-diameter-mm>
200 ################################################## general info ############################################
202 # 3 different types of towplanes could exist: AI-plane, MP-plane without interaction, MP-plane with interaction.
203 # AI-planes are identified by the node "ai/models/aircraft/".
204 # MP-planes (interactice/non-interactive) are identified by the existence of node "ai/models/multiplayer".
205 # Interactive MP-plane: variables in node "ai/models/multiplayer/sim/hitches/" are updated.
206 # Non-interactive MP-plane: variables are not updated (values are either not defined or have "wrong" values
207 # from a former owner of this node.
209 # The following properties are transmitted in multiplayer:
210 # "sim/hitches/aerotow/tow/elastic-constant"
211 # "sim/hitches/aerotow/tow/weight-per-m-kg-m"
212 # "sim/hitches/aerotow/tow/dist"
213 # "sim/hitches/aerotow/tow/connected-to-property-node"
214 # "sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign"
215 # "sim/hitches/aerotow/tow/brake-force"
216 # "sim/hitches/aerotow/tow/end-force-x"
217 # "sim/hitches/aerotow/tow/end-force-y"
218 # "sim/hitches/aerotow/tow/end-force-z"
219 # "sim/hitches/aerotow/is-slave"
220 # "sim/hitches/aerotow/speed-in-tow-direction"
221 # "sim/hitches/aerotow/open", open);
222 # "sim/hitches/aerotow/local-pos-x"
223 # "sim/hitches/aerotow/local-pos-y"
224 # "sim/hitches/aerotow/local-pos-z"
226 ##############################################################################################################
231 # ######################################################################################################################
232 # check, if towing support makes sense
233 # ######################################################################################################################
235 # Check if node "sim/hitches" is defined. If not, return!
236 if (props.globals.getNode("sim/hitches") == nil ) return;
237 print("towing is active!");
240 # ######################################################################################################################
241 # set defaults / initialize at startup
242 # ######################################################################################################################
244 # set defaults for properties that are NOT already defined
246 # yasim properties for aerotow (should be already defined for yasim aircrafts but not for JSBSim aircrafts
247 if (props.globals.getNode("sim/hitches/aerotow/broken") == nil )
248 props.globals.getNode("sim/hitches/aerotow/broken", 1).setBoolValue(0);
249 if (props.globals.getNode("sim/hitches/aerotow/force") == nil )
250 props.globals.getNode("sim/hitches/aerotow/force", 1).setValue(0.);
251 if (props.globals.getNode("sim/hitches/aerotow/force-is-calculated-by-other") == nil )
252 props.globals.getNode("sim/hitches/aerotow/force-is-calculated-by-other", 1).setBoolValue(0);
253 if (props.globals.getNode("sim/hitches/aerotow/is-slave") == nil )
254 props.globals.getNode("sim/hitches/aerotow/is-slave", 1).setBoolValue(0);
255 if (props.globals.getNode("sim/hitches/aerotow/local-pos-x") == nil )
256 props.globals.getNode("sim/hitches/aerotow/local-pos-x", 1).setValue(0.);
257 if (props.globals.getNode("sim/hitches/aerotow/local-pos-y") == nil )
258 props.globals.getNode("sim/hitches/aerotow/local-pos-y", 1).setValue(0.);
259 if (props.globals.getNode("sim/hitches/aerotow/local-pos-z") == nil )
260 props.globals.getNode("sim/hitches/aerotow/local-pos-z", 1).setValue(0.);
261 if (props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period") == nil )
262 props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period", 1).setValue(0.);
263 if (props.globals.getNode("sim/hitches/aerotow/mp-time-lag") == nil )
264 props.globals.getNode("sim/hitches/aerotow/mp-time-lag", 1).setValue(0.);
265 #if (props.globals.getNode("sim/hitches/aerotow/open") == nil )
266 props.globals.getNode("sim/hitches/aerotow/open", 1).setBoolValue(1);
267 if (props.globals.getNode("sim/hitches/aerotow/speed-in-tow-direction") == nil )
268 props.globals.getNode("sim/hitches/aerotow/speed-in-tow-direction", 1).setValue(0.);
269 if (props.globals.getNode("sim/hitches/aerotow/tow/brake-force") == nil )
270 props.globals.getNode("sim/hitches/aerotow/tow/brake-force", 1).setValue(12345.);
271 if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-node") == nil )
272 props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-node", 1).setBoolValue(0);
273 if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign") == nil )
274 props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign", 1).setValue("");
275 if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id") == nil )
276 props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id", 1).setIntValue(0);
277 if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-mp-node") == nil )
278 props.globals.getNode("sim/hitches/aerotow/tow/connected-to-mp-node", 1).setBoolValue(0);
279 if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-property-node") == nil )
280 props.globals.getNode("sim/hitches/aerotow/tow/connected-to-property-node", 1).setBoolValue(0);
281 if (props.globals.getNode("sim/hitches/aerotow/tow/dist") == nil )
282 props.globals.getNode("sim/hitches/aerotow/tow/dist", 1).setValue(0.);
283 if (props.globals.getNode("sim/hitches/aerotow/tow/elastic-constant") == nil )
284 props.globals.getNode("sim/hitches/aerotow/tow/elastic-constant", 1).setValue(9111.);
285 if (props.globals.getNode("sim/hitches/aerotow/tow/end-force-x") == nil )
286 props.globals.getNode("sim/hitches/aerotow/tow/end-force-x", 1).setValue(0.);
287 if (props.globals.getNode("sim/hitches/aerotow/tow/end-force-y") == nil )
288 props.globals.getNode("sim/hitches/aerotow/tow/end-force-y", 1).setValue(0.);
289 if (props.globals.getNode("sim/hitches/aerotow/tow/end-force-z") == nil )
290 props.globals.getNode("sim/hitches/aerotow/tow/end-force-z", 1).setValue(0.);
291 if (props.globals.getNode("sim/hitches/aerotow/tow/length") == nil )
292 props.globals.getNode("sim/hitches/aerotow/tow/length", 1).setValue(60.);
293 if (props.globals.getNode("sim/hitches/aerotow/tow/node") == nil )
294 props.globals.getNode("sim/hitches/aerotow/tow/node", 1).setValue("");
295 if (props.globals.getNode("sim/hitches/aerotow/tow/weight-per-m-kg-m") == nil )
296 props.globals.getNode("sim/hitches/aerotow/tow/weight-per-m-kg-m", 1).setValue(0.35);
298 # additional properties
299 if (props.globals.getNode("sim/hitches/aerotow/oldOpen") == nil )
300 props.globals.getNode("sim/hitches/aerotow/oldOpen", 1).setBoolValue(1);
302 # new properties for towrope
303 if (props.globals.getNode("sim/hitches/aerotow/rope/exist") == nil )
304 props.globals.getNode("sim/hitches/aerotow/rope/exist", 1).setBoolValue(0);
305 if (props.globals.getNode("sim/hitches/aerotow/rope/model_id") == nil )
306 props.globals.getNode("sim/hitches/aerotow/rope/model_id", 1).setIntValue(-1);
307 if (props.globals.getNode("sim/hitches/aerotow/rope/path_to_model") == nil )
308 props.globals.getNode("sim/hitches/aerotow/rope/path_to_model", 1).setValue("Models/Aircraft/towropes.xml");
309 if (props.globals.getNode("sim/hitches/aerotow/rope/rope-diameter-mm") == nil )
310 props.globals.getNode("sim/hitches/aerotow/rope/rope-diameter-mm", 1).setIntValue(20.);
312 # new properties for JSBSim aerotow
313 if ( getprop("sim/flight-model") == "jsb" ) {
314 if (props.globals.getNode("sim/hitches/aerotow/force_name_jsbsim") == nil )
315 props.globals.getNode("sim/hitches/aerotow/force_name_jsbsim", 1).setValue("hitch");
316 if (props.globals.getNode("sim/hitches/aerotow/mp_oldOpen") == nil )
317 props.globals.getNode("sim/hitches/aerotow/mp_oldOpen", 1).setBoolValue(1);
318 if (props.globals.getNode("sim/hitches/aerotow/tow/mp_last_reporded_dist") == nil )
319 props.globals.getNode("sim/hitches/aerotow/tow/mp_last_reported_dist", 1).setValue(0.);
322 # yasim properties for winch (should be already defined for yasim aircrafts but not for JSBSim aircrafts
323 #if (props.globals.getNode("sim/hitches/winch/open") == nil )
324 props.globals.getNode("sim/hitches/winch/open", 1).setBoolValue(1);
325 if (props.globals.getNode("sim/hitches/winch/broken") == nil )
326 props.globals.getNode("sim/hitches/winch/broken", 1).setBoolValue(0);
327 if (props.globals.getNode("sim/hitches/winch/winch/global-pos-x") == nil )
328 props.globals.getNode("sim/hitches/winch/winch/global-pos-x", 1).setValue(0.);
329 if (props.globals.getNode("sim/hitches/winch/winch/global-pos-y") == nil )
330 props.globals.getNode("sim/hitches/winch/winch/global-pos-y", 1).setValue(0.);
331 if (props.globals.getNode("sim/hitches/winch/winch/global-pos-z") == nil )
332 props.globals.getNode("sim/hitches/winch/winch/global-pos-z", 1).setValue(0.);
333 if (props.globals.getNode("sim/hitches/winch/winch/initial-tow-length-m") == nil )
334 props.globals.getNode("sim/hitches/winch/winch/initial-tow-length-m", 1).setValue(1000.);
335 if (props.globals.getNode("sim/hitches/winch/winch/max-tow-length-m") == nil )
336 props.globals.getNode("sim/hitches/winch/winch/max-tow-length-m", 1).setValue(1500.);
337 if (props.globals.getNode("sim/hitches/winch/winch/min-tow-length-m") == nil )
338 props.globals.getNode("sim/hitches/winch/winch/min-tow-length-m", 1).setValue(1.);
340 if (props.globals.getNode("sim/hitches/winch/tow/length") == nil )
341 props.globals.getNode("sim/hitches/winch/tow/length", 1).setValue(0.);
342 if (props.globals.getNode("sim/hitches/winch/tow/dist") == nil )
343 props.globals.getNode("sim/hitches/winch/tow/dist", 1).setValue(0.);
344 if (props.globals.getNode("sim/hitches/winch/tow/elastic-constant") == nil )
345 props.globals.getNode("sim/hitches/winch/tow/elastic-constant", 1).setValue(40001.);
346 if (props.globals.getNode("sim/hitches/winch/tow/weight-per-m-kg-m") == nil )
347 props.globals.getNode("sim/hitches/winch/tow/weight-per-m-kg-m", 1).setValue(0.1);
349 # additional properties
350 if (props.globals.getNode("sim/hitches/winch/oldOpen") == nil )
351 props.globals.getNode("sim/hitches/winch/oldOpen", 1).setBoolValue(1);
352 if (props.globals.getNode("sim/hitches/winch/winch/max-spool-speed-m-s") == nil )
353 props.globals.getNode("sim/hitches/winch/winch/max-spool-speed-m-s", 1).setValue(40.);
355 # new properties for winch-rope
356 if (props.globals.getNode("sim/hitches/winch/rope/exist") == nil )
357 props.globals.getNode("sim/hitches/winch/rope/exist", 1).setBoolValue(0);
358 if (props.globals.getNode("sim/hitches/winch/rope/model_id") == nil )
359 props.globals.getNode("sim/hitches/winch/rope/model_id", 1).setIntValue(-1);
360 if (props.globals.getNode("sim/hitches/winch/rope/path_to_model") == nil )
361 props.globals.getNode("sim/hitches/winch/rope/path_to_model", 1).setValue("Models/Aircraft/towropes.xml");
362 if (props.globals.getNode("sim/hitches/winch/rope/rope-diameter-mm") == nil )
363 props.globals.getNode("sim/hitches/winch/rope/rope-diameter-mm", 1).setIntValue(20.);
365 # new properties for JSBSim winch
366 if ( getprop("sim/flight-model") == "jsb" ) {
367 if (props.globals.getNode("sim/hitches/winch/force_name_jsbsim") == nil )
368 props.globals.getNode("sim/hitches/winch/force_name_jsbsim", 1).setValue("hitch");
369 if (props.globals.getNode("sim/hitches/winch/automatic-release-angle-deg") == nil )
370 props.globals.getNode("sim/hitches/winch/automatic-release-angle-deg", 1).setValue(361.);
371 if (props.globals.getNode("sim/hitches/winch/winch/clutched") == nil )
372 props.globals.getNode("sim/hitches/winch/winch/clutched", 1).setBoolValue(0);
373 if (props.globals.getNode("sim/hitches/winch/winch/actual-spool-speed-m-s") == nil )
374 props.globals.getNode("sim/hitches/winch/winch/actual-spool-speed-m-s", 1).setValue(0.);
375 if (props.globals.getNode("sim/hitches/winch/winch/spool-acceleration-m-s-s") == nil )
376 props.globals.getNode("sim/hitches/winch/winch/spool-acceleration-m-s-s", 1).setValue(8.);
377 if (props.globals.getNode("sim/hitches/winch/winch/max-unspool-speed-m-s") == nil )
378 props.globals.getNode("sim/hitches/winch/winch/max-unspool-speed-m-s", 1).setValue(40.);
379 if (props.globals.getNode("sim/hitches/winch/winch/actual-force-N") == nil )
380 props.globals.getNode("sim/hitches/winch/winch/actual-force-N", 1).setValue(0.);
381 if (props.globals.getNode("sim/hitches/winch/winch/max-force-N") == nil )
382 props.globals.getNode("sim/hitches/winch/winch/max-force-N", 1).setValue(1000.);
383 if (props.globals.getNode("sim/hitches/winch/winch/max-power-kW") == nil )
384 props.globals.getNode("sim/hitches/winch/winch/max-power-kW", 1).setValue(123.);
385 if (props.globals.getNode("sim/hitches/winch/tow/break-force-N") == nil )
386 props.globals.getNode("sim/hitches/winch/tow/break-force-N", 1).setValue(12345.);
387 if (props.globals.getNode("sim/hitches/winch/winch/magic-constant") == nil )
388 props.globals.getNode("sim/hitches/winch/winch/magic-constant", 1).setValue(500.);
393 # ######################################################################################################################
395 # ######################################################################################################################
399 #print("function towing is running");
405 # ------------------------------- aerotow part -------------------------------
407 var open = getprop("sim/hitches/aerotow/open");
408 var oldOpen = getprop("sim/hitches/aerotow/oldOpen");
410 if ( open != oldOpen ) { # check if my hitch state has changed, if yes: message
411 #print("state has changed: open=",open," oldOpen=",oldOpen);
413 if ( !open ) { # my hitch was open and is closed now
414 if ( getprop("sim/flight-model") == "jsb" ) {
415 var distance = getprop("sim/hitches/aerotow/tow/dist");
416 var towlength_m = getprop("sim/hitches/aerotow/tow/length");
417 if ( distance > towlength_m * 1.0001 ) {
418 setprop("sim/messages/pilot", sprintf("Could not lock hitch (tow length is insufficient) on hitch %i!",
419 getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
420 props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(1); # open my hitch again
421 } # mp aircraft to far away
422 else { # my hitch is closed
423 setprop("sim/messages/pilot", sprintf("Locked hitch aerotow %i!",
424 getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
426 props.globals.getNode("sim/hitches/aerotow/broken").setBoolValue(0);
428 if ( !getprop("sim/hitches/aerotow/open") ) {
430 createTowrope("aerotow");
432 # set default hitch coordinates (needed for Ai- and non-interactive MP aircrafts)
433 setAIObjectDefaults() ;
435 } # end hitch is closed
437 if ( open ) { # my hitch is now open
438 if ( getprop("sim/flight-model") == "jsb" ) {
439 if ( getprop("sim/hitches/aerotow/broken") ) {
440 setprop("sim/messages/pilot", sprintf("Oh no, the tow is broken"));
443 setprop("sim/messages/pilot", sprintf("Opened hitch aerotow %i!",
444 getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
446 releaseHitch("aerotow"); # open=1 / forces=0
448 removeTowrope("aerotow"); # remove towrope model
449 } # end hitch is open
451 setprop("sim/hitches/aerotow/oldOpen",open);
452 } # end hitch state has changed
456 } # end hitch is closed (open == 0)
457 else { # my hitch is open
458 var mp_auto_connect_period = props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period").getValue();
459 if ( mp_auto_connect_period != 0 ) { # if auto-connect
460 if ( getprop("sim/flight-model") == "jsb" ) { # only for JSBSim aircraft
462 } # end JSBSim aircraft
463 dt = mp_auto_connect_period;
464 #print("towing: running as auto connect with period=",dt);
465 } # end if auto-connect
466 else { # my hitch is open and not auto-connect
472 # ------------------------------- winch part -------------------------------
474 var winchopen = getprop("sim/hitches/winch/open");
475 var wincholdOpen = getprop("sim/hitches/winch/oldOpen");
477 if ( winchopen != wincholdOpen ) { # check if my hitch state has changed, if yes: message
478 #print("winch state has changed: open=",winchopen," oldOpen=",wincholdOpen);
479 if ( !winchopen ) { # my hitch was open and is closed now
480 if ( getprop("sim/flight-model") == "jsb" ) {
481 var distance = getprop("sim/hitches/winch/tow/dist");
482 var towlength_m = getprop("sim/hitches/winch/tow/length");
483 if ( distance > towlength_m ) {
484 setprop("sim/messages/pilot", sprintf("Could not lock hitch (tow length is insufficient) on hitch %i!",
485 getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
486 props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(1); # open my hitch again
487 } # mp aircraft to far away
488 else { # my hitch is closed
489 setprop("sim/messages/pilot", sprintf("Locked hitch winch %i!",
490 getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
491 setprop("sim/hitches/winch/winch/clutched","false");
493 props.globals.getNode("sim/hitches/winch/broken").setBoolValue(0);
494 props.globals.getNode("sim/hitches/winch/winch/actual-spool-speed-m-s").setValue(0.);
496 if ( !getprop("sim/hitches/winch/open") ) {
498 createTowrope("winch");
500 # set default hitch coordinates (needed for Ai- and non-interactive MP aircrafts)
501 setAIObjectDefaults() ;
503 } # end hitch is closed
505 if ( winchopen ) { # my hitch is now open
506 if ( getprop("sim/flight-model") == "jsb" ) {
507 if ( getprop("sim/hitches/winch/broken") ) {
508 setprop("sim/messages/pilot", sprintf("Oh no, the tow is broken"));
510 releaseHitch("winch");
513 } # end hitch is open
515 setprop("sim/hitches/winch/oldOpen",winchopen);
516 } # end hitch state has changed
522 settimer( towing, dt );
527 # ######################################################################################################################
528 # find best AI object
529 # ######################################################################################################################
531 var findBestAIObject = func (){
533 # the nearest found plane, that is close enough will be used
534 # set some default variables, needed later to identify if the found object is
535 # an AI-Object, a "non-interactiv MP-Object or an interactive MP-Object
538 var aiobjects = []; # keeps the ai-planes from the property tree
539 var AI_id = 0; # id of towplane
540 var callsign = 0; # callsign of towplane
541 var aiPosition = geo.Coord.new(); # current processed ai-plane
542 var lat_deg = 0; # latitude of current processed aiobject
543 var lon_deg = 0; # longitude of current processed aiobject
544 var alt_m = 0; # altitude of current processed aiobject
545 var myPosition = geo.Coord.new(); # coordinates of glider
546 var distance_m = 0; # distance to ai-plane
550 var nodeIsAiAircraft = 0;
551 var nodeIsMpAircraft = 0;
552 var running_as_autoconnect = 0;
553 var mp_open_last_state = 0;
556 if ( getprop("sim/flight-model") == "yasim" ) return; # bypass this routine for Yasim-aircrafts
558 #print("findBestAIObject");
560 if (props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period").getValue() != 0 ) {
561 var running_as_autoconnect = 1;
562 #print("findBestAIObject: running as auto connect");
565 var towlength_m = props.globals.getNode("sim/hitches/aerotow/tow/length").getValue();
567 var bestdist_m = towlength_m; # initial value
569 myPosition = geo.aircraft_position();
570 # todo: calculate exact hitch position
572 if( running_as_autoconnect ) {
573 var mycallsign = props.globals.getNode("sim/multiplay/callsign").getValue();
574 #print('mycallsign=',mycallsign);
578 aiobjects = props.globals.getNode("ai/models").getChildren();
579 foreach (var aimember; aiobjects) {
580 if ( (var node = aimember.getName() ) != nil ) {
581 nodeIsAiAircraft = 0;
582 nodeIsMpAircraft = 0;
583 if ( sprintf("%8s",node) == "aircraft" ) nodeIsAiAircraft = 1;
584 if ( sprintf("%11s",node) == "multiplayer" ) nodeIsMpAircraft = 1;
585 #print("found NodeName=",node," nodeIsAiAircraft=",nodeIsAiAircraft," nodeIsMpAircraft=",nodeIsMpAircraft );
587 if( !nodeIsAiAircraft and !nodeIsMpAircraft ) continue;
589 if( running_as_autoconnect ) {
590 if ( !nodeIsMpAircraft ) continue;
591 if ( aimember.getValue("sim/hitches/aerotow/open") == 1 ) continue; # if mp hook open, auto-connect isn't possible
592 if (mycallsign != aimember.getValue("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign") ) continue ; # I'm the wrong one
595 var lat_deg = aimember.getNode("position/latitude-deg").getValue();
596 var lon_deg = aimember.getNode("position/longitude-deg").getValue();
597 var alt_m = aimember.getNode("position/altitude-ft").getValue() * FT2M;
599 var aiPosition = geo.Coord.set_latlon( lat_deg, lon_deg, alt_m );
600 distance_m = (myPosition.distance_to(aiPosition));
601 #print('distance_m=',distance_m,' bestdist_m=',bestdist_m);
602 if ( distance_m < bestdist_m ) {
603 bestdist_m = distance_m;
605 var towEndNode = node;
606 var nodeID = aimember.getNode("id").getValue();
607 var aicallsign = aimember.getNode("callsign").getValue();
608 #print('nodeId=',nodeID,' AiCallsign=',aicallsign);
611 props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(0);
612 props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-node").setBoolValue(nodeIsAiAircraft);
613 props.globals.getNode("sim/hitches/aerotow/tow/connected-to-mp-node").setBoolValue(nodeIsMpAircraft);
614 props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign").setValue(aicallsign);
615 props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id").setIntValue(nodeID);
616 props.globals.getNode("sim/hitches/aerotow/tow/connected-to-property-node").setBoolValue(1);
617 props.globals.getNode("sim/hitches/aerotow/tow/node").setValue(towEndNode);
618 props.globals.getNode("sim/hitches/aerotow/tow/dist").setValue(bestdist_m);
619 props.globals.getNode("sim/hitches/aerotow/tow/mp_last_reported_dist", 1).setValue(0.);
621 # Set some dummy values. In case of an "interactive"-MP plane
622 # the correct values will be transmitted in the following loop
623 aimember.getNode("sim/hitches/aerotow/local-pos-x",1).setValue(-5.);
624 aimember.getNode("sim/hitches/aerotow/local-pos-y",1).setValue(0.);
625 aimember.getNode("sim/hitches/aerotow/local-pos-z",1).setValue(0.);
626 aimember.getNode("sim/hitches/aerotow/tow/dist",1).setValue(-1.);
633 if ( !running_as_autoconnect) {
634 setprop("sim/messages/pilot", sprintf("%s, I am on your hook, distance %4.3f meter.",aicallsign,bestdist_m));
637 setprop("sim/messages/ai-plane", sprintf("%s: I am on your hook, distance %4.3f meter.",aicallsign,bestdist_m ));
639 if ( running_as_autoconnect ) {
641 props.globals.getNode("sim/hitches/aerotow/is-slave").setBoolValue(isSlave);
643 # set the dist value to some value below the tow length (if not, the hitch would open the next calc force run
644 distance_m = towlength_m * 0.5;
645 props.globals.getNode("sim/hitches/aerotow/mp_oldOpen").setBoolValue(1);
649 if (!running_as_autoconnect) {
650 setprop("sim/messages/atc", sprintf("Sorry, no aircraft for aerotow!"));
654 } # End function findBestAIObject
657 # ######################################################################################################################
660 # Start the towing animation ASAP
665 # ######################################################################################################################
667 # ######################################################################################################################
669 var aerotow = func (open){
671 #print("function aerotow is running");
675 ########################################### my hitch position ############################################
677 myPosition = geo.aircraft_position();
678 var my_head_deg = getprop("orientation/heading-deg");
679 var my_roll_deg = getprop("orientation/roll-deg");
680 var my_pitch_deg = getprop("orientation/pitch-deg");
682 # hook coordinates in Yasim-system (x-> nose / y -> left wing / z -> up)
683 var x = getprop("sim/hitches/aerotow/local-pos-x");
684 var y = getprop("sim/hitches/aerotow/local-pos-y");
685 var z = getprop("sim/hitches/aerotow/local-pos-z");
687 var alpha_deg = my_roll_deg * (1.); # roll clockwise (looking in x-direction) := +
688 var beta_deg = my_pitch_deg * (-1.); # pitch clockwise (looking in y-direction) := -
690 # transform hook coordinates
691 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.);
693 var install_distance_m = Xn[0]; # in front of ref-point of glider
694 var install_side_m = Xn[1];
695 var install_alt_m = Xn[2];
697 var myHitch_pos = myPosition.apply_course_distance( my_head_deg , install_distance_m );
698 var myHitch_pos = myPosition.apply_course_distance( my_head_deg - 90. , install_side_m );
699 myHitch_pos.set_alt(myPosition.alt() + install_alt_m);
701 ########################################### ai hitch position ############################################
703 var aiNodeID = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id"); # id of former found ai/mp aircraft
704 #print("aiNodeID=",aiNodeID);
706 aiobjects = props.globals.getNode("ai/models").getChildren();
707 foreach (var aimember; aiobjects) {
708 if ( (var c = aimember.getNode("id") ) != nil ) {
709 var testprop = c.getValue();
710 if ( testprop == aiNodeID) {
712 var ai_lat = aimember.getNode("position/latitude-deg").getValue();
713 var ai_lon = aimember.getNode("position/longitude-deg").getValue();
714 var ai_alt = (aimember.getNode("position/altitude-ft").getValue()) * FT2M;
715 #print("ai_lat,lon,alt",ai_lat,ai_lon,ai_alt);
717 var ai_pitch_deg = aimember.getNode("orientation/pitch-deg").getValue();
718 var ai_roll_deg = aimember.getNode("orientation/roll-deg").getValue();
719 var ai_head_deg = aimember.getNode("orientation/true-heading-deg").getValue();
721 var aiHitchX = aimember.getNode("sim/hitches/aerotow/local-pos-x").getValue();
722 var aiHitchY = aimember.getNode("sim/hitches/aerotow/local-pos-y").getValue();
723 var aiHitchZ = aimember.getNode("sim/hitches/aerotow/local-pos-z").getValue();
725 if ( getprop("sim/flight-model") == "jsb" ) {
726 # check if the multiplayer hitch state has changed
727 # this trick avoids immediate opening after locking because MP-aircraft has not yet reported a locked hitch
728 if ( (var d = aimember.getNode("sim/hitches/aerotow/open") ) != nil ) {
729 var mpOpen = aimember.getNode("sim/hitches/aerotow/open").getValue();
730 var mp_oldOpen = getprop("sim/hitches/aerotow/mp_oldOpen");
731 #print('mpOpen=',mpOpen,' mp_oldOpen=',mp_oldOpen);
732 if ( mpOpen != mp_oldOpen ) { # state has changed: was open and is now locked OR was locked and is now open
734 setprop("sim/messages/ai-plane", sprintf("%s: I have released the tow!",getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign")) );
735 releaseHitch("aerotow"); # my open=1 / forces=0 / remove towrope
737 props.globals.getNode("sim/hitches/aerotow/mp_oldOpen").setBoolValue(mpOpen);
738 } # end: state has changed
739 } # end: node is available
742 var aiPosition = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt );
744 var alpha_deg = ai_roll_deg * (1.);
745 var beta_deg = ai_pitch_deg * (-1.);
747 # transform hook coordinates
748 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.);
750 var install_distance_m = Xn[0]; # in front of ref-point of glider
751 var install_side_m = Xn[1];
752 var install_alt_m = Xn[2];
754 var aiHitch_pos = aiPosition.apply_course_distance( ai_head_deg , install_distance_m );
755 var aiHitch_pos = aiPosition.apply_course_distance( ai_head_deg - 90. , install_side_m );
756 aiHitch_pos.set_alt(aiPosition.alt() + install_alt_m);
758 ########################################### distance between hitches #####################################
760 var distance = (myHitch_pos.direct_distance_to(aiHitch_pos)); # distance to plane in meter
761 var aiHitchheadto = (myHitch_pos.course_to(aiHitch_pos));
762 var height = myHitch_pos.alt() - aiHitch_pos.alt();
764 var aiHitchpitchto = -math.asin((myHitch_pos.alt()-aiHitch_pos.alt())/distance) / 0.01745;
765 #print(" pitch: ", aiHitchpitchto);
767 # update position of rope
768 setprop("ai/models/aerotowrope/position/latitude-deg", myHitch_pos.lat());
769 setprop("ai/models/aerotowrope/position/longitude-deg", myHitch_pos.lon());
770 setprop("ai/models/aerotowrope/position/altitude-ft", myHitch_pos.alt() * M2FT);
771 #print("ai_lat,lon,alt",myHitch_pos.lat()," ",myHitch_pos.lon()," ",myHitch_pos.alt() );
773 # update pitch and heading of rope
774 setprop("ai/models/aerotowrope/orientation/true-heading-deg", aiHitchheadto);
775 setprop("ai/models/aerotowrope/orientation/pitch-deg", aiHitchpitchto);
777 # update length of rope
778 setprop("sim/hitches/aerotow/tow/dist", distance);
779 #print("distance=",distance);
782 ############################################# calc forces ##################################################
784 # calc forces only for JSBSim-aircrafts
786 # tow-end-forces must be reported in N to be consiststent to Yasim-aircrafts
787 # hitch-forces must be LBS to be consistent to the JSBSim "external_forces/.../magnitude" definition
789 if ( getprop("sim/flight-model") == "jsb" ) {
790 #print("Force-Routine");
792 # check if the MP-aircraft properties have been updated. If not (maybe due to time-lag) bypass force calculation (use previous forces instead)
793 var mp_reported_dist = aimember.getNode("sim/hitches/aerotow/tow/dist").getValue();
794 var mp_last_reported_dist = getprop("sim/hitches/aerotow/tow/mp_last_reported_dist");
795 var mp_delta_reported_dist = mp_reported_dist - mp_last_reported_dist ;
796 setprop("sim/hitches/aerotow/tow/mp_last_reported_dist",mp_reported_dist);
797 var mp_delta_reported_dist2 = mp_delta_reported_dist * mp_delta_reported_dist ; # we need the absolute value
798 if ( (mp_delta_reported_dist2 > 0.0000001) or (mp_reported_dist < 0. )){ # we have the updated MP coordinates (no time lag)
799 # or the MP-aircraft is a non-interactive mp plane (mp_reported_dist = -1)
800 # => update forces else use the old forces!
802 var breakforce_N = getprop("sim/hitches/aerotow/tow/brake-force"); # could be different in both aircrafts
804 var isSlave = getprop("sim/hitches/aerotow/is-slave");
805 if ( !isSlave ){ # if we are master, we have to calculate the forces
806 #print("master: calc forces");
807 var elastic_constant = getprop("sim/hitches/aerotow/tow/elastic-constant");
808 var towlength_m = getprop("sim/hitches/aerotow/tow/length");
810 var delta_towlength_m = distance - towlength_m;
811 #print("towlength_m= ", towlength_m , " elastic_constant= ", elastic_constant," delta_towlength_m= ", delta_towlength_m);
813 if ( delta_towlength_m < 0. ) {
817 var forcetow_N = elastic_constant * delta_towlength_m / towlength_m;
820 else { # we are slave and get the forces from master
821 #print("slave: get forces"," aimember=",aimember.getName());
823 var forcetowX_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-x").getValue() * 1;
824 var forcetowY_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-y").getValue() * 1;
825 var forcetowZ_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-z").getValue() * 1;
826 var forcetow_N = math.sqrt( forcetowX_N * forcetowX_N + forcetowY_N * forcetowY_N + forcetowZ_N * forcetowZ_N );
829 var forcetow_LBS = forcetow_N * 0.224809; # N -> LBF
830 #print(" forcetow_N ", forcetow_N , " distance ", distance," ", breakforce_N);
832 if ( forcetow_N < breakforce_N ) {
834 var distancepr = (myHitch_pos.distance_to(aiHitch_pos));
836 # correct a failure, if the projected length is larger than direct length
837 if (distancepr > distance) { distancepr = distance;}
839 var alpha = math.acos( (distancepr / distance) );
840 if ( aiHitch_pos.alt() > myHitch_pos.alt()) alpha = - alpha;
842 var beta = ( aiHitchheadto - my_head_deg ) * 0.01745;
843 var gamma = my_pitch_deg * 0.01745;
844 var delta = my_roll_deg * 0.01745;
846 var sina = math.sin(alpha);
847 var cosa = math.cos(alpha);
848 var sinb = math.sin(beta);
849 var cosb = math.cos(beta);
850 var sing = math.sin(gamma);
851 var cosg = math.cos(gamma);
852 var sind = math.sin(delta);
853 var cosd = math.cos(delta);
855 #var forcetow = forcetow_N; # we deliver N to JSBSim
856 var forcetow = forcetow_LBS; # we deliver LBS to JSBSim
858 # calculate unit vector of force direction in JSBSim-system
861 # global forces: alpha beta
862 var fglobalx = force * cosa * cosb;
863 var fglobaly = force * cosa * sinb;
864 var fglobalz = force * sina;
866 # local forces by pitch: gamma
867 var flpitchx = fglobalx * cosg - fglobalz * sing;
868 var flpitchy = fglobaly;
869 var flpitchz = fglobalx * sing + fglobalz * cosg;
871 # local forces by roll: delta
872 var flrollx = flpitchx;
873 var flrolly = flpitchy * cosd + flpitchz * sind;
874 var flrollz = - flpitchy * sind + flpitchz * cosd;
876 # asigning to LOCAL coord of plane
877 var forcex = flrollx;
878 var forcey = flrolly;
879 var forcez = flrollz;
880 #print("fx=",forcex," fy=",forcey," fz=",forcez);
882 # JSBSim-body-frame: x-> nose / y -> right wing / z -> down
883 # apply forces to hook (forces are in LBS or N see above)
884 var hitchname = getprop("sim/hitches/aerotow/force_name_jsbsim");
885 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", forcetow);
886 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", forcex);
887 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", forcey);
888 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", forcez);
890 } # end force < break force
891 else { # rope is broken
892 props.globals.getNode("sim/hitches/aerotow/broken").setBoolValue(1);
893 #setprop("sim/messages/atc", sprintf("Oh no, the tow is broken"));
894 releaseHitch("aerotow"); # open=1 / forces=0 / remove towrope
897 ############################################# report forces ##############################################
899 # if we are connected to an MP aircraft and master
900 var nodeIsMpAircraft = getprop("sim/hitches/aerotow/tow/connected-to-mp-node");
901 if ( nodeIsMpAircraft and !isSlave ){
902 #print("report Forces");
904 # transform my hitch coordinates to cartesian earth coordinates
905 var myHitchCartEarth = geodtocart(myHitch_pos.lat(),myHitch_pos.lon(),myHitch_pos.alt() );
906 var myHitchXearth_m = myHitchCartEarth[0];
907 var myHitchYearth_m = myHitchCartEarth[1];
908 var myHitchZearth_m = myHitchCartEarth[2];
910 # transform MP hitch coordinates to cartesian earth coordinates
911 var aiHitchCartEarth = geodtocart(aiHitch_pos.lat(),aiHitch_pos.lon(),aiHitch_pos.alt() );
912 var aiHitchXearth_m = aiHitchCartEarth[0];
913 var aiHitchYearth_m = aiHitchCartEarth[1];
914 var aiHitchZearth_m = aiHitchCartEarth[2];
916 # calculate normal vector in tow direction in cartesian earth coordinates
917 var dx = aiHitchXearth_m - myHitchXearth_m;
918 var dy = aiHitchYearth_m - myHitchYearth_m;
919 var dz = aiHitchZearth_m - myHitchZearth_m;
920 var dl = math.sqrt( dx * dx + dy * dy + dz * dz );
922 var forcetowX_N = forcetow_N * dx / dl;
923 var forcetowY_N = forcetow_N * dy / dl;
924 var forcetowZ_N = forcetow_N * dz / dl;
926 setprop("sim/hitches/aerotow/tow/dist", distance);
927 setprop("sim/hitches/aerotow/tow/end-force-x", -forcetowX_N); # force acts in
928 setprop("sim/hitches/aerotow/tow/end-force-y", -forcetowY_N); # opposite direction
929 setprop("sim/hitches/aerotow/tow/end-force-z", -forcetowZ_N); # at tow end
931 } # end report forces
935 #print("forces NOT updated!");
937 } # end forces/JSBSim
942 if ( !aimember.getNode("valid").getValue() ) { # MP-aircraft is accessable (node is valid)
943 #print("MP-aircraft isn't valid!");
944 props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(1); # open my hitch
945 setprop("sim/messages/atc", sprintf("MP-aircraft disappeared!" ));
948 } # end: check id != nil
949 } # end: loop over aiobjects
952 } # end function aerotow
956 # ######################################################################################################################
958 # ######################################################################################################################
960 var winch = func (open){
963 var M2FT = 1. / FT2M;
964 var RAD2DEG = 57.29578;
965 var DEG2RAD = 1. / RAD2DEG;
969 ########################################### my hitch position ############################################
971 myPosition = geo.aircraft_position();
972 var my_head_deg = getprop("orientation/heading-deg");
973 var my_roll_deg = getprop("orientation/roll-deg");
974 var my_pitch_deg = getprop("orientation/pitch-deg");
976 # hitch coordinates in YASim-system (x-> nose / y -> left wing / z -> up)
977 var x = getprop("sim/hitches/winch/local-pos-x");
978 var y = getprop("sim/hitches/winch/local-pos-y");
979 var z = getprop("sim/hitches/winch/local-pos-z");
981 var alpha_deg = my_roll_deg * (1.); # roll clockwise (looking in x-direction) := +
982 var beta_deg = my_pitch_deg * (-1.); # pitch clockwise (looking in y-direction) := -
984 # transform hook coordinates
985 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.);
987 var install_distance_m = Xn[0]; # in front of ref-point of glider
988 var install_side_m = Xn[1];
989 var install_alt_m = Xn[2];
991 var myHitch_pos = myPosition.apply_course_distance( my_head_deg , install_distance_m );
992 var myHitch_pos = myPosition.apply_course_distance( my_head_deg - 90. , install_side_m );
993 myHitch_pos.set_alt(myPosition.alt() + install_alt_m);
995 ########################################### winch hitch position ############################################
998 var winch_global_pos_x = getprop("sim/hitches/winch/winch/global-pos-x");
999 var winch_global_pos_y = getprop("sim/hitches/winch/winch/global-pos-y");
1000 var winch_global_pos_z = getprop("sim/hitches/winch/winch/global-pos-z");
1002 var winch_geod = carttogeod(winch_global_pos_x,winch_global_pos_y,winch_global_pos_z);
1004 var ai_lat = winch_geod[0];
1005 var ai_lon = winch_geod[1];
1006 #var ai_alt = winch_geod[2] * FT2M;
1007 var ai_alt = winch_geod[2];
1008 #print("ai_lat,lon,alt",ai_lat,ai_lon,ai_alt);
1010 var aiHitch_pos = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt );
1013 ########################################### distance between hitches #####################################
1015 var distance = (myHitch_pos.direct_distance_to(aiHitch_pos)); # distance to winch in meter
1016 var aiHitchheadto = (myHitch_pos.course_to(aiHitch_pos));
1017 var height = myHitch_pos.alt() - aiHitch_pos.alt();
1019 var aiHitchpitchto = -math.asin((myHitch_pos.alt()-aiHitch_pos.alt())/distance) / 0.01745;
1020 #print(" pitch: ", aiHitchpitchto);
1022 # update position of rope
1023 setprop("ai/models/winchrope/position/latitude-deg", myHitch_pos.lat());
1024 setprop("ai/models/winchrope/position/longitude-deg", myHitch_pos.lon());
1025 setprop("ai/models/winchrope/position/altitude-ft", myHitch_pos.alt() * M2FT);
1026 #print("ai_lat,lon,alt",myHitch_pos.lat()," ",myHitch_pos.lon()," ",myHitch_pos.alt() );
1028 # update pitch and heading of rope
1029 setprop("ai/models/winchrope/orientation/true-heading-deg", aiHitchheadto);
1030 setprop("ai/models/winchrope/orientation/pitch-deg", aiHitchpitchto);
1032 # update length of rope
1033 setprop("sim/hitches/winch/tow/dist", distance);
1034 #print("distance=",distance);
1037 ############################################# calc forces ##################################################
1039 # calc forces only for JSBSim-aircrafts
1041 # tow-end-forces must be reported in N to be consiststent to Yasim-aircrafts
1042 # hitch-forces must be LBS to be consistent to the JSBSim "external_forces/.../magnitude" definition
1044 if ( getprop("sim/flight-model") == "jsb" ) {
1046 var spool_max = getprop("sim/hitches/winch/winch/max-spool-speed-m-s");
1047 var unspool_max = getprop("sim/hitches/winch/winch/max-unspool-speed-m-s");
1048 var max_force_N = getprop("sim/hitches/winch/winch/max-force-N");
1049 var max_power_W = getprop("sim/hitches/winch/winch/max-power-kW") * 1000.;
1050 var breakforce_N = getprop("sim/hitches/winch/tow/break-force-N");
1051 var elastic_constant = getprop("sim/hitches/winch/tow/elastic-constant");
1052 var towlength_m = getprop("sim/hitches/winch/tow/length");
1053 var max_tow_length_m = getprop("sim/hitches/winch/winch/max-tow-length-m");
1054 var spoolspeed = getprop("sim/hitches/winch/winch/actual-spool-speed-m-s");
1055 var spool_acceleration = getprop("sim/hitches/winch/winch/spool-acceleration-m-s-s");
1056 var delta_t = getprop("sim/time/delta-sec");
1058 var towlength_new_m = towlength_m - spoolspeed * delta_t;
1059 var delta_towlength_m = distance - towlength_new_m;
1060 #print("towlength_m= ", towlength_m , " elastic_constant= ", elastic_constant," delta_towlength_m= ", delta_towlength_m);
1062 if ( getprop("sim/hitches/winch/winch/clutched") ) {
1063 var delta_spoolspeed = spool_acceleration * delta_t;
1064 spoolspeed = spoolspeed + delta_spoolspeed ;
1065 if ( spoolspeed > spool_max ) spoolspeed = spool_max;
1067 else { # un-clutched
1068 # --- experimental --- #
1070 # we assume that the the winch-operator avoids tow sagging ( => rigid rope; negativ forces allowed)
1071 var forcetow_N = elastic_constant * delta_towlength_m / towlength_new_m;
1073 # drag of tow-rope ( magic! )
1074 var magic_constant = getprop("sim/hitches/winch/winch/magic-constant");
1075 tow_drag_N = spoolspeed * spoolspeed * math.sqrt( math.sqrt( height * height ) * max_tow_length_m ) / magic_constant ;
1077 # mass = tow-mass only (drum-mass ignored)
1078 var mass_kg = max_tow_length_m * getprop("sim/hitches/winch/tow/weight-per-m-kg-m");
1080 var acceleration = ( forcetow_N - tow_drag_N ) / mass_kg;
1081 var delta_spoolspeed = acceleration * delta_t;
1082 spoolspeed = spoolspeed - delta_spoolspeed;
1083 if ( spoolspeed < - unspool_max ) spoolspeed = - unspool_max;
1084 #print("spoolspeed= ",spoolspeed," delta_spoolspeed= ",delta_spoolspeed," delta_towlength= ", delta_towlength_m);
1085 #print("forcetow_N= ",forcetow_N," tow_drag_N= ",tow_drag_N," acceleration= ", acceleration);
1088 if ( delta_towlength_m < 0. ) {
1089 var forcetow_N = 0.;
1092 var forcetow_N = elastic_constant * delta_towlength_m / towlength_new_m;
1095 if ( forcetow_N > max_force_N ) {
1096 forcetow_N = max_force_N;
1097 var towlength_new_m = distance / ( forcetow_N / elastic_constant + 1. );
1098 spoolspeed = (towlength_m - towlength_new_m ) / delta_t;
1101 var power = forcetow_N * spoolspeed;
1102 if ( power > max_power_W) {
1103 power = max_power_W;
1104 spoolspeed = power / forcetow_N;
1105 towlength_new_m = towlength_m - spoolspeed * delta_t;
1107 #print("power=",power," spoolspeed=",spoolspeed," force=",forcetow_N);
1109 setprop("sim/hitches/winch/tow/length",towlength_new_m);
1110 setprop("sim/hitches/winch/winch/actual-spool-speed-m-s",spoolspeed);
1111 setprop("sim/hitches/winch/winch/actual-force-N",forcetow_N);
1113 # force due to tow-weight (acts in tow direction at the heigher hitch)
1114 var force_due_to_weight_N = getprop("sim/hitches/winch/tow/weight-per-m-kg-m") * 9.81 * height;
1115 if (height < 0. ) force_due_to_weight_N = 0.;
1117 forcetow_N = forcetow_N + force_due_to_weight_N;
1118 var forcetow_LBS = forcetow_N * 0.224809; # N -> LBF
1119 #print(" forcetow_N ", forcetow_N , " distance ", distance," ", breakforce_N);
1120 #print(" forcetow_N=", forcetow_N , " force_due_to_weight_N=", force_due_to_weight_N," height=",height);
1122 if ( forcetow_N < breakforce_N ) {
1124 var distancepr = (myHitch_pos.distance_to(aiHitch_pos));
1126 # correct a failure, if the projected length is larger than direct length
1127 if (distancepr > distance) { distancepr = distance;}
1129 var alpha = math.acos( (distancepr / distance) );
1130 if ( aiHitch_pos.alt() > myHitch_pos.alt()) alpha = - alpha;
1131 var beta = ( aiHitchheadto - my_head_deg ) * DEG2RAD;
1132 var gamma = my_pitch_deg * DEG2RAD;
1133 var delta = my_roll_deg * DEG2RAD;
1135 var sina = math.sin(alpha);
1136 var cosa = math.cos(alpha);
1137 var sinb = math.sin(beta);
1138 var cosb = math.cos(beta);
1139 var sing = math.sin(gamma);
1140 var cosg = math.cos(gamma);
1141 var sind = math.sin(delta);
1142 var cosd = math.cos(delta);
1144 #var forcetow = forcetow_N; # we deliver N to JSBSim
1145 var forcetow = forcetow_LBS; # we deliver LBS to JSBSim
1147 # calculate unit vector of force direction in JSBSim-system
1150 # global forces: alpha beta
1151 var fglobalx = force * cosa * cosb;
1152 var fglobaly = force * cosa * sinb;
1153 var fglobalz = force * sina;
1155 # local forces by pitch: gamma
1156 var flpitchx = fglobalx * cosg - fglobalz * sing;
1157 var flpitchy = fglobaly;
1158 var flpitchz = fglobalx * sing + fglobalz * cosg;
1160 # local forces by roll: delta
1161 var flrollx = flpitchx;
1162 var flrolly = flpitchy * cosd + flpitchz * sind;
1163 var flrollz = - flpitchy * sind + flpitchz * cosd;
1165 # asigning to LOCAL coord of plane
1166 var forcex = flrollx;
1167 var forcey = flrolly;
1168 var forcez = flrollz;
1169 #print("fx=",forcex," fy=",forcey," fz=",forcez);
1171 # JSBSim-body-frame: x-> nose / y -> right wing / z -> down
1172 # apply forces to hook (forces are in LBS or N see above)
1173 var hitchname = getprop("sim/hitches/winch/force_name_jsbsim");
1174 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", forcetow);
1175 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", forcex );
1176 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", forcey );
1177 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", forcez );
1179 # check, if auto-release condition is reached
1180 var rope_angle_deg = math.atan2(forcez , forcex ) * RAD2DEG;
1181 #print("rope_angle_deg=",rope_angle_deg);
1182 if (rope_angle_deg > getprop("sim/hitches/winch/automatic-release-angle-deg") ) releaseWinch();
1184 } # end force < break force
1185 else { # rope is broken
1186 props.globals.getNode("sim/hitches/winch/broken").setBoolValue(1);
1190 if ( towlength_new_m > max_tow_length_m ) {
1191 setprop("sim/messages/atc", sprintf("tow length exceeded!"));
1195 } # end forces/JSBSim
1197 } # end hitch is closed (open == 0)
1199 } # end function winch
1202 # ######################################################################################################################
1204 # ######################################################################################################################
1206 var createTowrope = func (device){
1208 # create the towrope in the model property tree
1209 #print("createTowrope for ",device);
1211 if ( getprop("sim/hitches/" ~ device ~ "/rope/exist") == 0 ) { # does the towrope exist?
1213 # get the next free model id
1214 var freeModelid = getFreeModelID();
1216 props.globals.getNode("sim/hitches/" ~ device ~ "/rope/model_id").setIntValue(freeModelid);
1217 props.globals.getNode("sim/hitches/" ~ device ~ "/rope/exist").setBoolValue(1);
1219 var towrope_ai = props.globals.getNode("ai/models/" ~ device ~ "rope", 1);
1220 var towrope_mod = props.globals.getNode("models", 1);
1222 towrope_ai.getNode("id", 1).setIntValue(4711);
1223 towrope_ai.getNode("callsign", 1).setValue("towrope");
1224 towrope_ai.getNode("valid", 1).setBoolValue(1);
1225 towrope_ai.getNode("position/latitude-deg", 1).setValue(0.);
1226 towrope_ai.getNode("position/longitude-deg", 1).setValue(0.);
1227 towrope_ai.getNode("position/altitude-ft", 1).setValue(0.);
1228 towrope_ai.getNode("orientation/true-heading-deg", 1).setValue(0.);
1229 towrope_ai.getNode("orientation/pitch-deg", 1).setValue(0.);
1230 towrope_ai.getNode("orientation/roll-deg", 1).setValue(0.);
1232 towrope_mod.model = towrope_mod.getChild("model", freeModelid, 1);
1233 towrope_mod.model.getNode("path", 1).setValue(getprop("sim/hitches/" ~ device ~ "/rope/path_to_model") );
1234 towrope_mod.model.getNode("longitude-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/longitude-deg");
1235 towrope_mod.model.getNode("latitude-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/latitude-deg");
1236 towrope_mod.model.getNode("elevation-ft-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/altitude-ft");
1237 towrope_mod.model.getNode("heading-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/true-heading-deg");
1238 towrope_mod.model.getNode("roll-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/roll-deg");
1239 towrope_mod.model.getNode("pitch-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/pitch-deg");
1240 towrope_mod.model.getNode("load", 1).remove();
1241 } # end towrope exist
1245 # ######################################################################################################################
1246 # get the next free id of "models/model" members
1247 # ######################################################################################################################
1249 var getFreeModelID = func {
1250 #print("getFreeModelID");
1251 var modelid = 0; # next unused id
1252 modelobjects = props.globals.getNode("models", 1).getChildren();
1253 foreach ( var member; modelobjects ) {
1254 if ( (var c = member.getIndex()) != nil) {
1258 #print("modelid=",modelid);
1263 # ######################################################################################################################
1265 # ######################################################################################################################
1267 var closeHitch = func {
1269 #print("closeHitch");
1271 setprop("sim/hitches/aerotow/open", "false");
1272 setprop("sim/hitches/aerotow/mp_oldOpen", "true");
1274 } # End function closeHitch
1277 # ######################################################################################################################
1279 # ######################################################################################################################
1281 var releaseHitch = func (device){
1283 #print("releaseHitch");
1285 if ( getprop("sim/flight-model") == "yasim" ) return; # bypass this routine for Yasim-aircrafts
1287 setprop("sim/hitches/" ~ device ~ "/open", "true");
1289 var hitchname = getprop("sim/hitches/" ~ device ~ "/force_name_jsbsim");
1290 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/magnitude", 0.);
1291 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/x", 0.);
1292 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/y", 0.);
1293 setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "/z", 0.);
1295 if ( device == "aerotow" ) {
1296 setprop("sim/hitches/aerotow/tow/end-force-x", 0.); # MP tow-end forces
1297 setprop("sim/hitches/aerotow/tow/end-force-y", 0.); #
1298 setprop("sim/hitches/aerotow/tow/end-force-z", 0.); #
1301 } # End function releaseHitch
1304 # ######################################################################################################################
1305 # remove/delete towrope
1306 # ######################################################################################################################
1308 var removeTowrope = func (device){
1310 # remove the towrope from the property tree ai/models
1311 # remove the towrope from the property tree models/
1313 if ( getprop("sim/hitches/" ~ device ~ "/rope/exist") == 1 ) { # does the towrope exist?
1315 # remove 3d model from scenery
1316 # identification is /models/model[x] with x=id_model
1317 var id_model = getprop("sim/hitches/" ~ device ~ "/rope/model_id");
1318 var modelsNode = "models/model[" ~ id_model ~ "]";
1319 props.globals.getNode(modelsNode).remove();
1320 props.globals.getNode("ai/models/" ~ device ~ "rope").remove();
1321 #print("towrope removed");
1322 setprop("sim/hitches/" ~ device ~ "/rope/exist", 0);
1328 # ######################################################################################################################
1329 # pull in towrope after hitch has been opened
1330 # ######################################################################################################################
1332 var pull_in_rope = func {
1334 var deg2rad = math.pi / 180.;
1337 if ( getprop("sim/hitches/winch/open") ) {
1339 # get length of rope
1340 #var distance = getprop("sim/hitches/winch/tow/dist");
1342 var towlength_m = getprop("sim/hitches/winch/tow/length");
1343 var spoolspeed = getprop("sim/hitches/winch/winch/max-spool-speed-m-s");
1344 var delta_t = getprop("sim/time/delta-sec");
1346 var delta_length_m = spoolspeed * delta_t;
1347 var towlength_new_m = towlength_m - delta_length_m;
1348 var towlength_min_m = getprop("sim/hitches/winch/winch/min-tow-length-m");
1350 if ( towlength_new_m > towlength_min_m ) {
1351 #print("actual towlength= ",towlength_new_m);
1353 # get position of rope end (former myHitch_pos)
1354 var tow_lat = getprop("ai/models/winchrope/position/latitude-deg");
1355 var tow_lon = getprop("ai/models/winchrope/position/longitude-deg");
1356 var tow_alt_m = getprop("ai/models/winchrope/position/altitude-ft") * FT2M;
1357 # get pitch and heading of rope
1358 var tow_heading_deg = getprop("ai/models/winchrope/orientation/true-heading-deg");
1359 var tow_pitch_rad = getprop("ai/models/winchrope/orientation/pitch-deg") * deg2rad;
1361 var aiTow_pos = geo.Coord.set_latlon( tow_lat, tow_lon, tow_alt_m );
1363 var delta_distance_m = delta_length_m * math.cos(tow_pitch_rad);
1364 var delta_alt_m = delta_length_m * math.sin(tow_pitch_rad);
1365 # vertical sink rate not yet taken into account!
1366 aiTow_pos = aiTow_pos.apply_course_distance( tow_heading_deg , delta_distance_m );
1367 aiTow_pos.set_alt(tow_alt_m + delta_alt_m);
1368 #print("aiTow_pos.alt()= ",aiTow_pos.alt()," ",tow_alt_m + delta_alt_m);
1370 # update position of rope
1371 setprop("ai/models/winchrope/position/latitude-deg", aiTow_pos.lat());
1372 setprop("ai/models/winchrope/position/longitude-deg", aiTow_pos.lon());
1373 setprop("ai/models/winchrope/position/altitude-ft", aiTow_pos.alt() * M2FT);
1375 # update length of rope
1376 setprop("sim/hitches/winch/tow/length",towlength_new_m);
1378 settimer( pull_in_rope , 0 );
1379 } # end towlength > min
1381 #print("pull in finished!");
1382 setprop("sim/hitches/winch/winch/actual-spool-speed-m-s", 0. );
1383 removeTowrope("winch"); # remove towrope model
1391 # ######################################################################################################################
1392 # set some AI-object default values
1393 # ######################################################################################################################
1395 var setAIObjectDefaults = func (){
1397 # set some default variables, needed to identify, if the found object is an AI-object, a "non-interactiv MP-object or
1398 # an interactive MP-object
1400 var aiNodeID = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id"); # id of former found ai/mp aircraft
1402 aiobjects = props.globals.getNode("ai/models").getChildren();
1403 foreach (var aimember; aiobjects) {
1404 if ( (var c = aimember.getNode("id") ) != nil ) {
1405 var testprop = c.getValue();
1406 if ( testprop == aiNodeID) {
1407 # Set some dummy values. In case of an "interactive"-MP plane
1408 # the correct values will be transmitted in the following loop.
1409 # Create this variables if not present.
1410 aimember.getNode("sim/hitches/aerotow/local-pos-x",1).setValue(-5.);
1411 aimember.getNode("sim/hitches/aerotow/local-pos-y",1).setValue(0.);
1412 aimember.getNode("sim/hitches/aerotow/local-pos-z",1).setValue(0.);
1413 aimember.getNode("sim/hitches/aerotow/tow/dist",1).setValue(-1.);
1421 # ######################################################################################################################
1423 # ######################################################################################################################
1425 var setWinchPositionAuto = func {
1427 # remove already existing winch model
1428 if ( getprop("/sim/hitches/winch/winch/winch-model-index") != nil ) {
1429 var id_model = getprop("/sim/hitches/winch/winch/winch-model-index");
1430 var modelsNode = "models/model[" ~ id_model ~ "]";
1431 props.globals.getNode(modelsNode).remove();
1432 #print("winch model removed");
1435 var initial_length_m = getprop("sim/hitches/winch/winch/initial-tow-length-m");
1436 var ac_pos = geo.aircraft_position(); # get position of aircraft
1437 var ac_hd = getprop("orientation/heading-deg"); # get heading of aircraft
1440 # get initial runway position
1441 var ipos_lat_deg = getprop("sim/presets/latitude-deg");
1442 var ipos_lon_deg = getprop("sim/presets/longitude-deg");
1443 var ipos_hd_deg = getprop("sim/presets/heading-deg");
1444 var ipos_alt_m = geo.elevation(ipos_lat_deg,ipos_lon_deg);
1445 var ipos_geo = geo.Coord.new().set_latlon(ipos_lat_deg, ipos_lon_deg, ipos_alt_m);
1446 # offset to initial position
1447 var deviation = (ac_pos.distance_to(ipos_geo));
1448 # if deviation is too much, locate winch in front of glider, otherwise locate winch to end of runway
1449 if ( deviation > 200) {
1450 var w = ac_pos.apply_course_distance( ac_hd , initial_length_m -1. );
1453 var w = ipos_geo.apply_course_distance( ipos_hd_deg , initial_length_m - 1. );
1455 var wpalt = geo.elevation(w.lat(), w.lon());
1458 var winchModel = geo.put_model("Models/Airport/supacat_winch.xml", w.lat(), w.lon(), (w.alt()+0.81), (w.course_to(ac_pos) ));
1460 setprop("/sim/hitches/winch/winch/global-pos-x", w.x());
1461 setprop("/sim/hitches/winch/winch/global-pos-y", w.y());
1462 setprop("/sim/hitches/winch/winch/global-pos-z", w.z());
1464 setprop("sim/hitches/winch/tow/dist",initial_length_m - 1.);
1465 setprop("sim/hitches/winch/tow/length",initial_length_m);
1467 #print("name=",winchModel.getName()," Index=",winchModel.getIndex()," Type=",winchModel.getType() );
1468 #print("val=",winchModel.getValue()," children=",winchModel.getChildren()," size=",size(winchModel) );
1469 setprop("/sim/hitches/winch/winch/winch-model-index",winchModel.getIndex() );
1470 setprop("sim/messages/pilot", sprintf("Connected to winch!"));
1472 props.globals.getNode("sim/hitches/winch/open").setBoolValue(0);
1474 } # End function setWinchPositionAuto
1477 # ######################################################################################################################
1478 # clutch / un-clutch winch
1479 # ######################################################################################################################
1481 var runWinch = func {
1483 if ( !getprop("sim/hitches/winch/winch/clutched") ) {
1484 setprop("sim/hitches/winch/winch/clutched","true");
1485 setprop("sim/messages/pilot", sprintf("Winch clutched!"));
1488 setprop("sim/hitches/winch/winch/clutched","false");
1489 setprop("sim/messages/pilot", sprintf("Winch un-clutched!"));
1492 } # End function runWinch
1495 # ######################################################################################################################
1497 # ######################################################################################################################
1499 var releaseWinch = func {
1501 setprop("sim/hitches/winch/open","true");
1503 } # End function releaseWinch
1506 # ######################################################################################################################
1507 # point transformation
1508 # ######################################################################################################################
1510 var PointRotate3D = func (x,y,z,xr,yr,zr,alpha_deg,beta_deg,gamma_deg){
1512 # ---------------------------------------------------------------------------------
1513 # rotates point (x,y,z) about all 3 cartesian axis
1514 # center of rotation (xr,yr,zr)
1515 # angle of rotation about x-axis = alpha
1516 # angle of rotation about y-axis = beta
1517 # angle of rotation about z-axis = gamma
1518 # delivers new point coordinates (x_new,y_new,z_new)
1519 # ---------------------------------------------------------------------------------
1535 #----------------------------------------------------------------------------------
1537 # Transformation in rotation-system X_rel = X-Xr = (x-xr, y-yr, z-zr)
1543 var deg2rad = math.pi / 180.;
1545 var alpha_rad = deg2rad * alpha_deg;
1546 var beta_rad = deg2rad * beta_deg;
1547 var gamma_rad = deg2rad * gamma_deg;
1549 var sin_alpha = math.sin(alpha_rad);
1550 var cos_alpha = math.cos(alpha_rad);
1552 var sin_beta = math.sin(beta_rad);
1553 var cos_beta = math.cos(beta_rad);
1555 var sin_gamma = math.sin(gamma_rad);
1556 var cos_gamma = math.cos(gamma_rad);
1560 # Rotate about x-axis Rx(alpha)
1562 # Rx11 Rx12 Rx13 1 0 0
1563 # Rx(alpha)= Rx21 Rx22 Rx23 = 0 cos(alpha) -sin(alpha)
1564 # Rx31 Rx32 Rx33 0 sin(alpha) cos(alpha)
1570 var Rx22 = cos_alpha;
1571 var Rx23 = - sin_alpha;
1573 var Rx32 = sin_alpha;
1574 var Rx33 = cos_alpha;
1576 # Rotate about y-axis Ry(beta)
1578 # Ry11 Ry12 Ry13 cos(beta) 0 sin(beta)
1579 # Ry(beta)= Ry21 Ry22 Ry23 = 0 1 0
1580 # Ry31 Ry32 Ry33 -sin(beta) 0 cos(beta)
1582 var Ry11 = cos_beta;
1584 var Ry13 = sin_beta;
1588 var Ry31 = - sin_beta;
1590 var Ry33 = cos_beta;
1592 # Rotate about z-axis Rz(gamma)
1594 # Rz11 Rz12 Rz13 cos(gamma) -sin(gamma) 0
1595 # Rz(gamma)= Rz21 Rz22 Rz23 = sin(gamma) cos(gamma) 0
1596 # Rz31 Rz32 Rz33 0 0 1
1598 var Rz11 = cos_gamma;
1599 var Rz12 = - sin_gamma;
1601 var Rz21 = sin_gamma;
1602 var Rz22 = cos_gamma;
1608 # First rotation about x-axis
1610 var x_x = Rx11 * x_rel + Rx12 * y_rel + Rx13 * z_rel;
1611 var y_x = Rx21 * x_rel + Rx22 * y_rel + Rx23 * z_rel;
1612 var z_x = Rx31 * x_rel + Rx32 * y_rel + Rx33 * z_rel;
1614 # subsequent rotation about y-axis
1616 var x_xy = Ry11 * x_x + Ry12 * y_x + Ry13 * z_x;
1617 var y_xy = Ry21 * x_x + Ry22 * y_x + Ry23 * z_x;
1618 var z_xy = Ry31 * x_x + Ry32 * y_x + Ry33 * z_x;
1620 # subsequent rotation about z-axis:
1622 var x_xyz = Rz11 * x_xy + Rz12 * y_xy + Rz13 * z_xy;
1623 var y_xyz = Rz21 * x_xy + Rz22 * y_xy + Rz23 * z_xy;
1624 var z_xyz = Rz31 * x_xy + Rz32 * y_xy + Rz33 * z_xy;
1626 # Back transformation X_rel = X-Xr = (x-xr, y-yr, z-zr)
1627 var xn = xr + x_xyz;
1628 var yn = yr + y_xyz;
1629 var zn = zr + z_xyz;
1631 var Xn = [xn,yn,zn];
1637 ##################################################################################################################################
1643 # - animate rope slack
1644 # - pull in towrope: take sink rate of rope into account
1645 # - dynamic ID for ai-rope-model
1647 # Please contact D-NXKT at yahoo.de for bug-reports, suggestions, ...