winch/aerotowing support for JSBSim-aircrafts; visible tow- and winch-ropes for YASim...
[fg:fgdata.git] / Nasal / towing / hitch.nas
1 #
2 # Version: 10. March 2014
3 #
4 # Purpose of this routine:
5 # ------------------------
6 #
7 # - Create visible winch- and towropes for gliders and towplanes
8 # - Support of aerotowing and winch for JSBSim-aircrafts (glider and towplanes)
9 #
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:
14 #
15 #
16 # Setup of visible winch/towropes for Yasim-aircrafts:
17 # ----------------------------------------------------  
18 #
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":      
21 # </sim>
22 #  <hitches>
23 #   <aerotow>
24 #    <rope>
25 #     <rope-diameter-mm type ="float">10</rope-diameter-mm>
26 #    </rope>
27 #   </aerotow>
28 #   <winch>
29 #    <rope>
30 #     <rope-diameter-mm type ="float">20</rope-diameter-mm>
31 #    </rope>
32 #   </winch>
33 #  </hitches>
34 # </sim>
35 #
36 # That's all!
37 #
38 #
39 #
40 # Support of aerotowing and winch for JSBSim-aircrafts (glider and towplanes):
41 # ----------------------------------------------------------------------------  
42 #
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 but MUST end with "_x", "_y","_z", respectively.    
48 #
49 # <external_reactions>
50 #  <force name="hitch_x" frame="BODY" unit="LBS" >
51 #   <location unit="M">
52 #    <x>3.65</x>  
53 #    <y> 0.0</y>
54 #    <z>-0.12</z>
55 #   </location>
56 #   <direction>
57 #    <x> 1.0</x>
58 #    <y> 0.0</y>
59 #    <z> 0.0</z>
60 #   </direction>
61 #  </force>
62 #  
63 #  <force name="hitch_y" frame="BODY" unit="LBS" >
64 #   <location unit="M">
65 #    <x>3.65</x>
66 #    <y> 0.0</y>
67 #    <z>-0.12</z>
68 #   </location>
69 #   <direction>
70 #    <x> 0.0</x>
71 #    <y> 1.0</y>
72 #    <z> 0.0</z>
73 #   </direction>
74 #  </force>
75 #  
76 #  <force name="hitch_z" frame="BODY" unit="LBS" >
77 #   <location unit="M">
78 #    <x>3.65</x> 
79 #    <y> 0.0</y>
80 #    <z>-0.12</z>
81 #   </location>
82 #   <direction>
83 #    <x> 0.0</x>
84 #    <y> 0.0</y>
85 #    <z> 1.0</z>
86 #   </direction>
87 #  </force>
88 # </external_reactions>
89
90 # 2. Define controls for aerotowing and winch.
91 # Add the following key bindings in "yourAircraft-set.xml":
92 # <input> 
93 #  <keyboard> 
94 #
95 #   <key n="15">
96 #     <name>Ctrl-o</name>
97 #     <desc>Find aircraft for aerotow</desc>
98 #     <binding>
99 #       <command>nasal</command>
100 #       <script>towing.findBestAIObject()</script>
101 #     </binding>
102 #   </key>
103 #   
104 #   <key n="111">
105 #     <name>o</name>
106 #     <desc>Lock aerotow-hook</desc>
107 #     <binding>
108 #       <command>nasal</command>
109 #       <script>towing.closeHitch()</script>
110 #     </binding>
111 #   </key>
112 #   
113 #   <key n="79">
114 #     <name>O</name>
115 #     <desc>Open aerotow-hook</desc>
116 #     <binding>
117 #       <command>nasal</command>
118 #       <script>towing.releaseHitch("aerotow")</script>
119 #     </binding>
120 #   </key>
121 #
122 #   <key n="23">
123 #     <name>Ctrl-w</name>
124 #     <desc>Place Winch and hook in</desc>
125 #     <binding>
126 #       <command>nasal</command>
127 #       <script>towing.setWinchPositionAuto()</script>
128 #     </binding>
129 #   </key>
130 #   
131 #   <key n="119">
132 #     <name>w</name>
133 #     <desc>Start winch</desc>
134 #     <binding>
135 #       <command>nasal</command>
136 #       <script>towing.runWinch()</script>
137 #     </binding>
138 #   </key>
139 #   
140 #   <key n="87">
141 #     <name>W</name>
142 #     <desc>Open winch-hook</desc>
143 #     <binding>
144 #       <command>nasal</command>
145 #       <script>towing.releaseHitch("winch")</script>
146 #     </binding>
147 #   </key>
148 #
149 #  </keyboard> 
150 # </input> 
151 #
152 # For towplanes only "key n=79" (Open aerotow-hook) is required!
153 #
154 #
155 # 3. Set mandatory properties:
156 #<sim>
157 # <hitches>
158 #  <aerotow>
159 #   <basename_force_jsbsim type="string">hitch</basename_force_jsbsim>
160 #   <local-pos-x type="float">1.5</local-pos-x>
161 #   <local-pos-y type="float"> 0.00</local-pos-y>
162 #   <local-pos-z type="float">-0.3</local-pos-z>
163 #   <force-is-calculated-by-other type="bool">false</force-is-calculated-by-other>
164 #   <mp-auto-connect-period type="float">0.0</mp-auto-connect-period>
165 #  </aerotow>
166 #  <winch>
167 #   <basename_force_jsbsim type="string">hitch</basename_force_jsbsim>
168 #   <local-pos-x type="float">0.0</local-pos-x>
169 #   <local-pos-y type="float">0.0</local-pos-y>
170 #   <local-pos-z type="float">0.0</local-pos-z>
171 #  </winch>
172 # </hitches>
173 #</sim>
174 #
175 # "basename_force_jsbsim" must be the external force name in JSBSim but without the ending "_x", "_y", "_z".
176 #
177 # IMPORTANT:
178 # JSBSim doesn't provide the hitch coordinates in the property tree. Hence you must set them again to get a 
179 # visible towrope (local-pos-x/y/z). Unfortunately the coordinate systems are different. Here the coordinates 
180 # for the "YASim-System" are needed (x points to the nose, y points to the left wing, z points upwards).  
181 # If you see the rope at the expected position "local-pos-x/y/z" are correct.
182 #
183 # "force-is-calculated-by-other" should be "false" for gliders and "true" for towplanes.
184 # "mp-auto-connect-period" is only needed for towplanes and should be "1". 
185
186
187 # 4. Set optional properties:
188 #<sim>
189 # <hitches>
190 #  <aerotow>
191 #   <tow>
192 #    <brake-force type="float">6000</brake-force>
193 #    <elastic-constant type="float">9000</elastic-constant>
194 #   </tow>
195 #   <rope>
196 #    <rope-diameter-mm type="float">20</rope-diameter-mm>
197 #   </rope>
198 #  </aerotow>
199 #  <winch>
200 #   <automatic-release-angle-deg type="float">70.</automatic-release-angle-deg>
201 #   <winch>
202 #    <initial-tow-length-m type="float">1000.</initial-tow-length-m>
203 #    <max-tow-length-m type="float">1500.</max-tow-length-m> 
204 #    <max-force type="float">800.</max-force>
205 #    <max-power-kW type="float">100.</max-power-kW>
206 #    <max-spool-speed-m-s type="float">15.</max-spool-speed-m-s>
207 #    <max-unspool-speed-m-s type="float">20.</max-unspool-speed-m-s>
208 #    <rel-speed alias="/sim/hitches/winch/winch/actual-spool-speed-m-s"/>
209 #   </winch>
210 #   <tow>
211 #    <break-force type="float">10000</break-force>
212 #    <elastic-constant type="float">40000</elastic-constant>
213 #   </tow>
214 #   <rope>
215 #    <rope-diameter-mm type="float">40</rope-diameter-mm>
216 #   </rope>
217 #  </winch>
218 # </hitches>
219 #<sim>
220 #
221 # That's it!
222
223
224 ##################################################  general info  ############################################
225 #
226 # 3 different types of towplanes could exist: AI-plane, MP-plane without interaction, MP-plane with interaction.
227 # AI-planes are identified by the node "ai/models/aircraft/".
228 # MP-planes (interactice/non-interactive) are identified by the existence of node "ai/models/multiplayer".
229 # Interactive MP-plane: variables in node "ai/models/multiplayer/sim/hitches/" are updated.
230 # Non-interactive MP-plane: variables are not updated (values are either not defined or have "wrong" values 
231 # from a former owner of this node.
232 #  
233 # The following properties are transmitted in multiplayer:
234 # "sim/hitches/aerotow/tow/elastic-constant"
235 # "sim/hitches/aerotow/tow/weight-per-m-kg-m"
236 # "sim/hitches/aerotow/tow/dist"
237 # "sim/hitches/aerotow/tow/connected-to-property-node"
238 # "sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign"   
239 # "sim/hitches/aerotow/tow/brake-force"
240 # "sim/hitches/aerotow/tow/end-force-x"
241 # "sim/hitches/aerotow/tow/end-force-y"
242 # "sim/hitches/aerotow/tow/end-force-z"
243 # "sim/hitches/aerotow/is-slave"
244 # "sim/hitches/aerotow/speed-in-tow-direction"
245 # "sim/hitches/aerotow/open", open);               
246 # "sim/hitches/aerotow/local-pos-x"
247 # "sim/hitches/aerotow/local-pos-y"
248 # "sim/hitches/aerotow/local-pos-z"     
249 #
250 ##############################################################################################################
251
252
253
254
255 # ######################################################################################################################
256 #                                            check, if towing support makes sense     
257 # ######################################################################################################################
258
259 # Check if node "sim/hitches" is defined. If not, return!
260   if (props.globals.getNode("sim/hitches") == nil ) return;
261   print("towing is active!"); 
262
263
264 # ######################################################################################################################
265 #                                           set defaults / initialize at startup      
266 # ######################################################################################################################
267
268 # set defaults for properties that are NOT already defined 
269
270  # yasim properties for aerotow (should be already defined for yasim aircrafts but not for JSBSim aircrafts
271   if (props.globals.getNode("sim/hitches/aerotow/broken") == nil )
272       props.globals.getNode("sim/hitches/aerotow/broken", 1).setBoolValue(0);
273   if (props.globals.getNode("sim/hitches/aerotow/force") == nil )
274       props.globals.getNode("sim/hitches/aerotow/force", 1).setValue(0.);
275   if (props.globals.getNode("sim/hitches/aerotow/force-is-calculated-by-other") == nil ) 
276       props.globals.getNode("sim/hitches/aerotow/force-is-calculated-by-other", 1).setBoolValue(0);
277   if (props.globals.getNode("sim/hitches/aerotow/is-slave") == nil )
278       props.globals.getNode("sim/hitches/aerotow/is-slave", 1).setBoolValue(0);
279   if (props.globals.getNode("sim/hitches/aerotow/local-pos-x") == nil )
280       props.globals.getNode("sim/hitches/aerotow/local-pos-x", 1).setValue(0.); 
281   if (props.globals.getNode("sim/hitches/aerotow/local-pos-y") == nil )   
282       props.globals.getNode("sim/hitches/aerotow/local-pos-y", 1).setValue(0.);
283   if (props.globals.getNode("sim/hitches/aerotow/local-pos-z") == nil ) 
284       props.globals.getNode("sim/hitches/aerotow/local-pos-z", 1).setValue(0.);
285   if (props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period") == nil )  
286       props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period", 1).setValue(0.);
287   if (props.globals.getNode("sim/hitches/aerotow/mp-time-lag") == nil )  
288       props.globals.getNode("sim/hitches/aerotow/mp-time-lag", 1).setValue(0.);
289   #if (props.globals.getNode("sim/hitches/aerotow/open") == nil ) 
290       props.globals.getNode("sim/hitches/aerotow/open", 1).setBoolValue(1);
291   if (props.globals.getNode("sim/hitches/aerotow/speed-in-tow-direction") == nil )  
292       props.globals.getNode("sim/hitches/aerotow/speed-in-tow-direction", 1).setValue(0.);
293   if (props.globals.getNode("sim/hitches/aerotow/tow/brake-force") == nil )
294       props.globals.getNode("sim/hitches/aerotow/tow/brake-force", 1).setValue(12345.); 
295   if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-node") == nil )   
296       props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-node", 1).setBoolValue(0);
297   if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign") == nil ) 
298       props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign", 1).setValue("");
299   if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id") == nil )   
300       props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id", 1).setIntValue(0);  
301   if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-mp-node") == nil )   
302       props.globals.getNode("sim/hitches/aerotow/tow/connected-to-mp-node", 1).setBoolValue(0);
303   if (props.globals.getNode("sim/hitches/aerotow/tow/connected-to-property-node") == nil )   
304       props.globals.getNode("sim/hitches/aerotow/tow/connected-to-property-node", 1).setBoolValue(0);
305   if (props.globals.getNode("sim/hitches/aerotow/tow/dist") == nil )   
306       props.globals.getNode("sim/hitches/aerotow/tow/dist", 1).setValue(0.);
307   if (props.globals.getNode("sim/hitches/aerotow/tow/elastic-constant") == nil )
308       props.globals.getNode("sim/hitches/aerotow/tow/elastic-constant", 1).setValue(9111.);
309   if (props.globals.getNode("sim/hitches/aerotow/tow/end-force-x") == nil )   
310       props.globals.getNode("sim/hitches/aerotow/tow/end-force-x", 1).setValue(0.);
311   if (props.globals.getNode("sim/hitches/aerotow/tow/end-force-y") == nil )   
312       props.globals.getNode("sim/hitches/aerotow/tow/end-force-y", 1).setValue(0.);
313   if (props.globals.getNode("sim/hitches/aerotow/tow/end-force-z") == nil )   
314       props.globals.getNode("sim/hitches/aerotow/tow/end-force-z", 1).setValue(0.);
315   if (props.globals.getNode("sim/hitches/aerotow/tow/length") == nil )   
316       props.globals.getNode("sim/hitches/aerotow/tow/length", 1).setValue(60.);
317   if (props.globals.getNode("sim/hitches/aerotow/tow/node") == nil )   
318       props.globals.getNode("sim/hitches/aerotow/tow/node", 1).setValue("");
319   if (props.globals.getNode("sim/hitches/aerotow/tow/weight-per-m-kg-m") == nil )
320       props.globals.getNode("sim/hitches/aerotow/tow/weight-per-m-kg-m", 1).setValue(0.35);
321
322  # additional properties  
323   if (props.globals.getNode("sim/hitches/aerotow/oldOpen") == nil ) 
324       props.globals.getNode("sim/hitches/aerotow/oldOpen", 1).setBoolValue(1);
325  
326  # new properties for towrope
327   if (props.globals.getNode("sim/hitches/aerotow/rope/exist") == nil )
328       props.globals.getNode("sim/hitches/aerotow/rope/exist", 1).setBoolValue(0);
329   if (props.globals.getNode("sim/hitches/aerotow/rope/model_id") == nil )
330       props.globals.getNode("sim/hitches/aerotow/rope/model_id", 1).setIntValue(-1);
331   if (props.globals.getNode("sim/hitches/aerotow/rope/path_to_model") == nil )
332       props.globals.getNode("sim/hitches/aerotow/rope/path_to_model", 1).setValue("Models/Aircraft/towropes.xml");
333   if (props.globals.getNode("sim/hitches/aerotow/rope/rope-diameter-mm") == nil )
334       props.globals.getNode("sim/hitches/aerotow/rope/rope-diameter-mm", 1).setIntValue(20.);
335
336  # new properties for JSBSim aerotow
337  if ( getprop("sim/flight-model") == "jsb" ) {
338   if (props.globals.getNode("sim/hitches/aerotow/basename_force_jsbsim") == nil )
339       props.globals.getNode("sim/hitches/aerotow/basename_force_jsbsim", 1).setValue("hitch");      
340   if (props.globals.getNode("sim/hitches/aerotow/mp_oldOpen") == nil ) 
341       props.globals.getNode("sim/hitches/aerotow/mp_oldOpen", 1).setBoolValue(1);
342   if (props.globals.getNode("sim/hitches/aerotow/tow/mp_last_reporded_dist") == nil )   
343       props.globals.getNode("sim/hitches/aerotow/tow/mp_last_reported_dist", 1).setValue(0.);
344  }
345
346  # yasim properties for winch (should be already defined for yasim aircrafts but not for JSBSim aircrafts
347   #if (props.globals.getNode("sim/hitches/winch/open") == nil ) 
348       props.globals.getNode("sim/hitches/winch/open", 1).setBoolValue(1);
349   if (props.globals.getNode("sim/hitches/winch/broken") == nil )
350       props.globals.getNode("sim/hitches/winch/broken", 1).setBoolValue(0);
351   if (props.globals.getNode("sim/hitches/winch/winch/global-pos-x") == nil )
352       props.globals.getNode("sim/hitches/winch/winch/global-pos-x", 1).setValue(0.);
353   if (props.globals.getNode("sim/hitches/winch/winch/global-pos-y") == nil )   
354       props.globals.getNode("sim/hitches/winch/winch/global-pos-y", 1).setValue(0.);
355   if (props.globals.getNode("sim/hitches/winch/winch/global-pos-z") == nil ) 
356       props.globals.getNode("sim/hitches/winch/winch/global-pos-z", 1).setValue(0.);
357   if (props.globals.getNode("sim/hitches/winch/winch/initial-tow-length-m") == nil ) 
358       props.globals.getNode("sim/hitches/winch/winch/initial-tow-length-m", 1).setValue(1000.);      
359   if (props.globals.getNode("sim/hitches/winch/winch/max-tow-length-m") == nil ) 
360       props.globals.getNode("sim/hitches/winch/winch/max-tow-length-m", 1).setValue(1500.);
361   if (props.globals.getNode("sim/hitches/winch/winch/min-tow-length-m") == nil ) 
362       props.globals.getNode("sim/hitches/winch/winch/min-tow-length-m", 1).setValue(1.);
363
364   if (props.globals.getNode("sim/hitches/winch/tow/length") == nil )   
365       props.globals.getNode("sim/hitches/winch/tow/length", 1).setValue(0.);
366   if (props.globals.getNode("sim/hitches/winch/tow/dist") == nil )   
367       props.globals.getNode("sim/hitches/winch/tow/dist", 1).setValue(0.);
368   if (props.globals.getNode("sim/hitches/winch/tow/elastic-constant") == nil )
369       props.globals.getNode("sim/hitches/winch/tow/elastic-constant", 1).setValue(40001.);
370   if (props.globals.getNode("sim/hitches/winch/tow/weight-per-m-kg-m") == nil )
371       props.globals.getNode("sim/hitches/winch/tow/weight-per-m-kg-m", 1).setValue(0.1);
372
373  # additional properties  
374   if (props.globals.getNode("sim/hitches/winch/oldOpen") == nil ) 
375       props.globals.getNode("sim/hitches/winch/oldOpen", 1).setBoolValue(1);
376   if (props.globals.getNode("sim/hitches/winch/winch/max-spool-speed-m-s") == nil ) 
377       props.globals.getNode("sim/hitches/winch/winch/max-spool-speed-m-s", 1).setValue(40.);
378
379  # new properties for winch-rope       
380   if (props.globals.getNode("sim/hitches/winch/rope/exist") == nil )
381       props.globals.getNode("sim/hitches/winch/rope/exist", 1).setBoolValue(0);
382   if (props.globals.getNode("sim/hitches/winch/rope/model_id") == nil )
383       props.globals.getNode("sim/hitches/winch/rope/model_id", 1).setIntValue(-1);
384   if (props.globals.getNode("sim/hitches/winch/rope/path_to_model") == nil )
385       props.globals.getNode("sim/hitches/winch/rope/path_to_model", 1).setValue("Models/Aircraft/towropes.xml");
386   if (props.globals.getNode("sim/hitches/winch/rope/rope-diameter-mm") == nil )
387       props.globals.getNode("sim/hitches/winch/rope/rope-diameter-mm", 1).setIntValue(20.);
388
389  # new properties for JSBSim winch
390  if ( getprop("sim/flight-model") == "jsb" ) {
391   if (props.globals.getNode("sim/hitches/winch/basename_force_jsbsim") == nil )
392       props.globals.getNode("sim/hitches/winch/basename_force_jsbsim", 1).setValue("hitch");
393   if (props.globals.getNode("sim/hitches/winch/automatic-release-angle-deg") == nil ) 
394       props.globals.getNode("sim/hitches/winch/automatic-release-angle-deg", 1).setValue(361.);
395   if (props.globals.getNode("sim/hitches/winch/winch/clutched") == nil )
396       props.globals.getNode("sim/hitches/winch/winch/clutched", 1).setBoolValue(0);
397   if (props.globals.getNode("sim/hitches/winch/winch/actual-spool-speed-m-s") == nil ) 
398       props.globals.getNode("sim/hitches/winch/winch/actual-spool-speed-m-s", 1).setValue(0.);
399   if (props.globals.getNode("sim/hitches/winch/winch/spool-acceleration-m-s-s") == nil ) 
400       props.globals.getNode("sim/hitches/winch/winch/spool-acceleration-m-s-s", 1).setValue(8.);
401   if (props.globals.getNode("sim/hitches/winch/winch/max-unspool-speed-m-s") == nil ) 
402       props.globals.getNode("sim/hitches/winch/winch/max-unspool-speed-m-s", 1).setValue(40.);
403   if (props.globals.getNode("sim/hitches/winch/winch/actual-force-N") == nil ) 
404       props.globals.getNode("sim/hitches/winch/winch/actual-force-N", 1).setValue(0.);
405   if (props.globals.getNode("sim/hitches/winch/winch/max-force-N") == nil ) 
406       props.globals.getNode("sim/hitches/winch/winch/max-force-N", 1).setValue(1000.);
407   if (props.globals.getNode("sim/hitches/winch/winch/max-power-kW") == nil ) 
408       props.globals.getNode("sim/hitches/winch/winch/max-power-kW", 1).setValue(123.);            
409   if (props.globals.getNode("sim/hitches/winch/tow/break-force-N") == nil )
410       props.globals.getNode("sim/hitches/winch/tow/break-force-N", 1).setValue(12345.); 
411   if (props.globals.getNode("sim/hitches/winch/winch/magic-constant") == nil )
412       props.globals.getNode("sim/hitches/winch/winch/magic-constant", 1).setValue(500.); 
413  }
414
415   
416
417 # ######################################################################################################################
418 #                                                         main function 
419 # ######################################################################################################################
420
421 var towing = func {
422  
423   #print("function towing is running");
424   
425   var FT2M = 0.30480;
426   var M2FT = 1 / FT2M;
427   var dt = 0;
428
429   # -------------------------------  aerotow part -------------------------------
430
431   var open = getprop("sim/hitches/aerotow/open");
432   var oldOpen = getprop("sim/hitches/aerotow/oldOpen");
433     
434   if ( open != oldOpen ) {   # check if my hitch state has changed, if yes: message
435     #print("state has changed: open=",open,"  oldOpen=",oldOpen);
436
437     if ( !open ) {      # my hitch was open and is closed now
438       if ( getprop("sim/flight-model") == "jsb" ) {
439         var distance = getprop("sim/hitches/aerotow/tow/dist");           
440         var towlength_m = getprop("sim/hitches/aerotow/tow/length");
441         if ( distance > towlength_m * 1.0001 ) {
442           setprop("sim/messages/pilot", sprintf("Could not lock hitch (tow length is insufficient) on hitch %i!",
443                                                getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
444           props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(1);  # open my hitch again       
445         }  # mp aircraft to far away
446         else {  # my hitch is closed
447           setprop("sim/messages/pilot", sprintf("Locked hitch aerotow %i!",
448                                                  getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
449         }
450         props.globals.getNode("sim/hitches/aerotow/broken").setBoolValue(0);
451       }  # end: JSBSim
452       if ( !getprop("sim/hitches/aerotow/open") ) {
453         # setup ai-towrope
454         createTowrope("aerotow");  
455
456         # set default hitch coordinates (needed for Ai- and non-interactive MP aircrafts)
457         setAIObjectDefaults() ;      
458       } 
459     }  # end hitch is closed
460     
461     if ( open ) {   # my hitch is now open
462       if ( getprop("sim/flight-model") == "jsb" ) {
463         if ( getprop("sim/hitches/aerotow/broken") ) {
464           setprop("sim/messages/pilot", sprintf("Oh no, the tow is broken"));
465         } 
466         else {
467           setprop("sim/messages/pilot", sprintf("Opened hitch aerotow %i!",
468                                                 getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
469         }
470       releaseHitch("aerotow"); # open=1 / forces=0
471       }  # end: JSBSim
472       removeTowrope("aerotow");   # remove towrope model            
473     }  # end hitch is open 
474        
475     setprop("sim/hitches/aerotow/oldOpen",open); 
476   }  # end hitch state has changed
477
478   if (!open ) {
479     aerotow(open);
480   }  # end hitch is closed (open == 0)
481   else {   # my hitch is open
482     var mp_auto_connect_period = props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period").getValue();
483     if ( mp_auto_connect_period != 0 ) {   # if auto-connect
484       if ( getprop("sim/flight-model") == "jsb" ) {  # only for JSBSim aircraft
485         findBestAIObject();
486       }   # end JSBSim  aircraft
487       dt = mp_auto_connect_period;
488       #print("towing: running as auto connect with period=",dt);
489     }   # end if auto-connect
490     else {  # my hitch is open and not auto-connect
491       dt = 0;
492     }
493   }
494
495
496   # -------------------------------  winch part -------------------------------
497
498   var winchopen = getprop("sim/hitches/winch/open");
499   var wincholdOpen = getprop("sim/hitches/winch/oldOpen");
500     
501   if ( winchopen != wincholdOpen ) {   # check if my hitch state has changed, if yes: message
502     #print("winch state has changed: open=",winchopen,"  oldOpen=",wincholdOpen);
503     if ( !winchopen ) {      # my hitch was open and is closed now
504       if ( getprop("sim/flight-model") == "jsb" ) {
505         var distance = getprop("sim/hitches/winch/tow/dist");           
506         var towlength_m = getprop("sim/hitches/winch/tow/length");
507         if ( distance > towlength_m ) {
508           setprop("sim/messages/pilot", sprintf("Could not lock hitch (tow length is insufficient) on hitch %i!",
509                                                getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
510           props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(1);  # open my hitch again       
511         }  # mp aircraft to far away
512         else {  # my hitch is closed
513           setprop("sim/messages/pilot", sprintf("Locked hitch winch %i!",
514                                                  getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
515           setprop("sim/hitches/winch/winch/clutched","false");
516         }
517         props.globals.getNode("sim/hitches/winch/broken").setBoolValue(0);
518         props.globals.getNode("sim/hitches/winch/winch/actual-spool-speed-m-s").setValue(0.);
519       }  # end: JSBSim
520       if ( !getprop("sim/hitches/winch/open") ) {
521         # setup ai-towrope
522         createTowrope("winch");  
523
524         # set default hitch coordinates (needed for Ai- and non-interactive MP aircrafts)
525         setAIObjectDefaults() ;      
526       } 
527     }  # end hitch is closed
528     
529     if ( winchopen ) {   # my hitch is now open
530       if ( getprop("sim/flight-model") == "jsb" ) {
531         if ( getprop("sim/hitches/winch/broken") ) {
532           setprop("sim/messages/pilot", sprintf("Oh no, the tow is broken"));
533         } 
534       releaseHitch("winch"); 
535       }  # end: JSBSim
536       pull_in_rope();
537     }  # end hitch is open
538     
539     setprop("sim/hitches/winch/oldOpen",winchopen); 
540   } # end hitch state has changed
541
542   if (!winchopen ) {
543     winch(winchopen);
544   }
545
546   settimer( towing, dt );
547    
548 }   # end towing
549
550
551 # ######################################################################################################################
552 #                                                   find best AI object
553 # ######################################################################################################################
554
555 var findBestAIObject = func (){
556
557   # the nearest found plane, that is close enough will be used
558   # set some default variables, needed later to identify if the found object is   
559   # an AI-Object, a "non-interactiv MP-Object or an interactive MP-Object
560   
561   # local variables
562   var aiobjects = [];                    # keeps the ai-planes from the property tree
563   var AI_id = 0;                         # id of towplane
564   var callsign = 0;                      # callsign of towplane
565   var aiPosition = geo.Coord.new();      # current processed ai-plane
566   var lat_deg = 0;                       # latitude of current processed aiobject
567   var lon_deg = 0;                       # longitude of current processed aiobject
568   var alt_m = 0;                         # altitude of current processed aiobject
569   var myPosition = geo.Coord.new();      # coordinates of glider
570   var distance_m = 0;                    # distance to ai-plane
571
572   var FT2M = 0.30480;
573   
574   var nodeIsAiAircraft = 0;
575   var nodeIsMpAircraft = 0;
576   var running_as_autoconnect = 0;
577   var mp_open_last_state = 0;
578   var isSlave = 0;
579   
580   if ( getprop("sim/flight-model") == "yasim" ) return; # bypass this routine for Yasim-aircrafts
581   
582   #print("findBestAIObject");
583   
584   if (props.globals.getNode("sim/hitches/aerotow/mp-auto-connect-period").getValue() != 0 ) {
585     var running_as_autoconnect = 1;
586     #print("findBestAIObject: running as auto connect");
587   }
588   
589   var towlength_m = props.globals.getNode("sim/hitches/aerotow/tow/length").getValue(); 
590
591   var bestdist_m = towlength_m; # initial value
592   
593   myPosition = geo.aircraft_position();
594   # todo: calculate exact hitch position
595
596   if( running_as_autoconnect ) {
597     var mycallsign = props.globals.getNode("sim/multiplay/callsign").getValue();
598     #print('mycallsign=',mycallsign);   
599   }
600
601   var found = 0;
602   aiobjects = props.globals.getNode("ai/models").getChildren();    
603   foreach (var aimember; aiobjects) {   
604     if ( (var node = aimember.getName() ) != nil ) {
605       nodeIsAiAircraft = 0;
606       nodeIsMpAircraft = 0;
607       if ( sprintf("%8s",node)  == "aircraft" ) nodeIsAiAircraft = 1;
608       if ( sprintf("%11s",node) == "multiplayer" ) nodeIsMpAircraft = 1;
609       #print("found NodeName=",node,"  nodeIsAiAircraft=",nodeIsAiAircraft,"  nodeIsMpAircraft=",nodeIsMpAircraft  );
610       
611       if( !nodeIsAiAircraft and !nodeIsMpAircraft ) continue;
612     
613       if( running_as_autoconnect ) {
614         if ( !nodeIsMpAircraft ) continue;
615         if ( aimember.getValue("sim/hitches/aerotow/open") == 1 ) continue;       # if mp hook open, auto-connect isn't possible
616         if (mycallsign != aimember.getValue("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign") ) continue ;  # I'm the wrong one      
617       }
618
619       var lat_deg = aimember.getNode("position/latitude-deg").getValue(); 
620       var lon_deg = aimember.getNode("position/longitude-deg").getValue(); 
621       var alt_m = aimember.getNode("position/altitude-ft").getValue() * FT2M; 
622         
623       var aiPosition = geo.Coord.set_latlon( lat_deg, lon_deg, alt_m ); 
624       distance_m = (myPosition.distance_to(aiPosition)); 
625       #print('distance_m=',distance_m,'  bestdist_m=',bestdist_m); 
626       if ( distance_m < bestdist_m ) { 
627         bestdist_m = distance_m;
628
629         var towEndNode = node;   
630         var nodeID = aimember.getNode("id").getValue();
631         var aicallsign = aimember.getNode("callsign").getValue();
632         #print('nodeId=',nodeID,'   AiCallsign=',aicallsign);
633
634         #set properties       
635         props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(0);
636         props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-node").setBoolValue(nodeIsAiAircraft);
637         props.globals.getNode("sim/hitches/aerotow/tow/connected-to-mp-node").setBoolValue(nodeIsMpAircraft);
638         props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign").setValue(aicallsign);
639         props.globals.getNode("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id").setIntValue(nodeID); 
640         props.globals.getNode("sim/hitches/aerotow/tow/connected-to-property-node").setBoolValue(1);
641         props.globals.getNode("sim/hitches/aerotow/tow/node").setValue(towEndNode);
642         props.globals.getNode("sim/hitches/aerotow/tow/dist").setValue(bestdist_m);
643         props.globals.getNode("sim/hitches/aerotow/tow/mp_last_reported_dist", 1).setValue(0.);
644
645         # Set some dummy values. In case of an "interactive"-MP plane 
646         # the correct values will be transmitted in the following loop
647         aimember.getNode("sim/hitches/aerotow/local-pos-x",1).setValue(-5.);
648         aimember.getNode("sim/hitches/aerotow/local-pos-y",1).setValue(0.);
649         aimember.getNode("sim/hitches/aerotow/local-pos-z",1).setValue(0.);
650         aimember.getNode("sim/hitches/aerotow/tow/dist",1).setValue(-1.);
651                 
652         found = 1;
653       }
654     } 
655   }
656   if (found) {
657     if ( !running_as_autoconnect) {
658       setprop("sim/messages/pilot", sprintf("%s, I am on your hook, distance %4.3f meter.",aicallsign,bestdist_m));
659     }
660     else {
661       setprop("sim/messages/ai-plane", sprintf("%s: I am on your hook, distance %4.3f meter.",aicallsign,bestdist_m ));
662     }
663     if ( running_as_autoconnect ) {
664       isSlave = 1;
665       props.globals.getNode("sim/hitches/aerotow/is-slave").setBoolValue(isSlave);
666     }
667     # set the dist value to some value below the tow length (if not, the hitch would open the next calc force run
668     distance_m = towlength_m * 0.5;
669     props.globals.getNode("sim/hitches/aerotow/mp_oldOpen").setBoolValue(1);
670     
671   }  # end: if found
672   else {
673     if (!running_as_autoconnect) {
674       setprop("sim/messages/atc", sprintf("Sorry, no aircraft for aerotow!"));
675     }
676   } 
677   
678 } # End function findBestAIObject
679
680
681 # ######################################################################################################################
682
683
684 # Start the towing animation ASAP
685 towing();
686
687
688
689 # ######################################################################################################################
690 #                                                         aerotow function 
691 # ######################################################################################################################
692
693 var aerotow = func (open){
694
695    #print("function aerotow is running");
696
697 #  if (!open ) {
698   
699   ###########################################  my hitch position  ############################################
700   
701   myPosition = geo.aircraft_position();
702   var my_head_deg  = getprop("orientation/heading-deg");
703   var my_roll_deg  = getprop("orientation/roll-deg");
704   var my_pitch_deg = getprop("orientation/pitch-deg");
705   
706   # hook coordinates in Yasim-system (x-> nose / y -> left wing / z -> up)
707   var x = getprop("sim/hitches/aerotow/local-pos-x");
708   var y = getprop("sim/hitches/aerotow/local-pos-y");
709   var z = getprop("sim/hitches/aerotow/local-pos-z");
710
711   var alpha_deg = my_roll_deg * (1.);   # roll clockwise (looking in x-direction) := +
712   var beta_deg  = my_pitch_deg * (-1.); # pitch clockwise (looking in y-direction) := -
713   
714   # transform hook coordinates
715   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.);
716
717   var install_distance_m = Xn[0]; # in front of ref-point of glider
718   var install_side_m     = Xn[1];
719   var install_alt_m      =  Xn[2];  
720     
721   var myHitch_pos    = myPosition.apply_course_distance( my_head_deg , install_distance_m ); 
722   var myHitch_pos    = myPosition.apply_course_distance( my_head_deg - 90. , install_side_m );   
723   myHitch_pos.set_alt(myPosition.alt() + install_alt_m); 
724   
725   ###########################################  ai hitch position  ############################################
726   
727   var aiNodeID = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id");   # id of former found ai/mp aircraft
728   #print("aiNodeID=",aiNodeID);
729   
730   aiobjects = props.globals.getNode("ai/models").getChildren(); 
731   foreach (var aimember; aiobjects) { 
732     if ( (var c = aimember.getNode("id") ) != nil ) { 
733       var testprop = c.getValue();
734       if ( testprop ==  aiNodeID) {
735         # get coordinates
736         var ai_lat = aimember.getNode("position/latitude-deg").getValue(); 
737         var ai_lon = aimember.getNode("position/longitude-deg").getValue(); 
738         var ai_alt = (aimember.getNode("position/altitude-ft").getValue()) * FT2M; 
739         #print("ai_lat,lon,alt",ai_lat,ai_lon,ai_alt);
740         
741         var ai_pitch_deg = aimember.getNode("orientation/pitch-deg").getValue();
742         var ai_roll_deg = aimember.getNode("orientation/roll-deg").getValue();
743         var ai_head_deg = aimember.getNode("orientation/true-heading-deg").getValue();
744
745         var aiHitchX = aimember.getNode("sim/hitches/aerotow/local-pos-x").getValue();
746         var aiHitchY = aimember.getNode("sim/hitches/aerotow/local-pos-y").getValue();
747         var aiHitchZ = aimember.getNode("sim/hitches/aerotow/local-pos-z").getValue();        
748
749         if ( getprop("sim/flight-model") == "jsb" ) {
750           # check if the multiplayer hitch state has changed 
751           # this trick avoids immediate opening after locking because MP-aircraft has not yet reported a locked hitch                            
752           if ( (var d = aimember.getNode("sim/hitches/aerotow/open") ) != nil ) {
753             var mpOpen = aimember.getNode("sim/hitches/aerotow/open").getValue(); 
754             var mp_oldOpen = getprop("sim/hitches/aerotow/mp_oldOpen");
755             #print('mpOpen=',mpOpen,'  mp_oldOpen=',mp_oldOpen);           
756             if ( mpOpen != mp_oldOpen ) { # state has changed: was open and is now locked OR was locked and is now open
757               if ( mpOpen ) {   
758                 setprop("sim/messages/ai-plane", sprintf("%s: I have released the tow!",getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-callsign")) );
759                 releaseHitch("aerotow"); # my open=1 / forces=0 / remove towrope 
760               }  # end: open
761               props.globals.getNode("sim/hitches/aerotow/mp_oldOpen").setBoolValue(mpOpen);
762             }  # end: state has changed
763           }  # end: node is available
764         }  #end : JSBSim
765   
766         var aiPosition = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt );
767   
768         var alpha_deg = ai_roll_deg * (1.);
769         var beta_deg  = ai_pitch_deg * (-1.);    
770
771         # transform hook coordinates
772         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.);
773
774         var install_distance_m =  Xn[0]; # in front of ref-point of glider  
775         var install_side_m     =  Xn[1];
776         var install_alt_m      =  Xn[2];  
777
778         var aiHitch_pos    = aiPosition.apply_course_distance( ai_head_deg , install_distance_m ); 
779         var aiHitch_pos    = aiPosition.apply_course_distance( ai_head_deg - 90. , install_side_m );   
780         aiHitch_pos.set_alt(aiPosition.alt() + install_alt_m); 
781
782         ###########################################  distance between hitches  #####################################
783     
784         var distance = (myHitch_pos.direct_distance_to(aiHitch_pos));      # distance to plane in meter
785         var aiHitchheadto = (myHitch_pos.course_to(aiHitch_pos));
786         var height = myHitch_pos.alt() - aiHitch_pos.alt();
787
788         var aiHitchpitchto = -math.asin((myHitch_pos.alt()-aiHitch_pos.alt())/distance) / 0.01745;
789         #print("  pitch: ", aiHitchpitchto);
790   
791         # update position of rope
792         setprop("ai/models/aerotowrope/position/latitude-deg", myHitch_pos.lat());
793         setprop("ai/models/aerotowrope/position/longitude-deg", myHitch_pos.lon());
794         setprop("ai/models/aerotowrope/position/altitude-ft", myHitch_pos.alt() * M2FT);
795         #print("ai_lat,lon,alt",myHitch_pos.lat(),"   ",myHitch_pos.lon(),"   ",myHitch_pos.alt() );
796     
797         # update pitch and heading of rope
798         setprop("ai/models/aerotowrope/orientation/true-heading-deg", aiHitchheadto);
799         setprop("ai/models/aerotowrope/orientation/pitch-deg", aiHitchpitchto);
800
801         # update length of rope
802         setprop("sim/hitches/aerotow/tow/dist", distance);
803         #print("distance=",distance);
804
805  
806         #############################################  calc forces  ##################################################
807
808         # calc forces only for JSBSim-aircrafts
809   
810         # tow-end-forces must be reported in N to be consiststent to Yasim-aircrafts
811         # hitch-forces must be LBS to be consistent to the JSBSim "external_forces/.../magnitude" definition 
812
813         if ( getprop("sim/flight-model") == "jsb" ) { 
814           #print("Force-Routine");
815
816           # check if the MP-aircraft properties have been updated. If not (maybe due to time-lag) bypass force calculation (use previous forces instead)
817           var mp_reported_dist = aimember.getNode("sim/hitches/aerotow/tow/dist").getValue();
818           var mp_last_reported_dist = getprop("sim/hitches/aerotow/tow/mp_last_reported_dist");
819           var mp_delta_reported_dist = mp_reported_dist - mp_last_reported_dist ;           
820           setprop("sim/hitches/aerotow/tow/mp_last_reported_dist",mp_reported_dist);
821           var mp_delta_reported_dist2 = mp_delta_reported_dist  * mp_delta_reported_dist ;   # we need the absolute value
822           if ( (mp_delta_reported_dist2 > 0.0000001) or (mp_reported_dist < 0. )){     # we have the updated MP coordinates (no time lag) 
823                                                                                        # or the MP-aircraft is a non-interactive mp plane (mp_reported_dist = -1)
824                                                                                        # => update forces else use the old forces!      
825  
826           var breakforce_N = getprop("sim/hitches/aerotow/tow/brake-force");  # could be different in both aircrafts
827
828           var isSlave = getprop("sim/hitches/aerotow/is-slave");
829           if ( !isSlave ){  # if we are master, we have to calculate the forces 
830             #print("master: calc forces");
831             var elastic_constant = getprop("sim/hitches/aerotow/tow/elastic-constant");
832             var towlength_m = getprop("sim/hitches/aerotow/tow/length");
833
834             var delta_towlength_m = distance - towlength_m;
835             #print("towlength_m= ", towlength_m , "  elastic_constant= ", elastic_constant,"  delta_towlength_m= ", delta_towlength_m);
836
837             if ( delta_towlength_m < 0. ) {
838               var forcetow_N = 0.;
839             }
840             else{
841               var forcetow_N = elastic_constant * delta_towlength_m / towlength_m;
842             }
843           }  # end !isSlave
844           else {   # we are slave and get the forces from master
845             #print("slave: get forces","    aimember=",aimember.getName());
846             # get forces
847             var forcetowX_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-x").getValue() * 1;
848             var forcetowY_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-y").getValue() * 1;
849             var forcetowZ_N = aimember.getNode("sim/hitches/aerotow/tow/end-force-z").getValue() * 1;
850             var forcetow_N = math.sqrt( forcetowX_N * forcetowX_N + forcetowY_N * forcetowY_N + forcetowZ_N * forcetowZ_N ); 
851           }  # end isSlave
852   
853           var forcetow_LBS = forcetow_N * 0.224809;   # N -> LBF     
854           #print(" forcetow_N ", forcetow_N , "  distance ", distance,"  ", breakforce_N);
855
856           if ( forcetow_N < breakforce_N ) {
857       
858             var distancepr = (myHitch_pos.distance_to(aiHitch_pos)); 
859       
860             # correct a failure, if the projected length is larger than direct length
861             if (distancepr > distance) { distancepr = distance;} 
862            
863             var alpha = math.acos( (distancepr / distance) );
864             if ( aiHitch_pos.alt() > myHitch_pos.alt()) alpha = - alpha;
865
866             var beta = ( aiHitchheadto - my_head_deg ) * 0.01745; 
867             var gamma = my_pitch_deg * 0.01745;
868             var delta = my_roll_deg * 0.01745;
869       
870             var sina = math.sin(alpha);
871             var cosa = math.cos(alpha);
872             var sinb = math.sin(beta);
873             var cosb = math.cos(beta);
874             var sing = math.sin(gamma);
875             var cosg = math.cos(gamma);
876             var sind = math.sin(delta);
877             var cosd = math.cos(delta);
878       
879             #var forcetow = forcetow_N;   # we deliver N to JSBSim
880             var forcetow = forcetow_LBS;   # we deliver LBS to JSBSim
881       
882             # global forces: alpha beta
883             var fglobalx = forcetow * cosa * cosb;
884             var fglobaly = forcetow * cosa * sinb;
885             var fglobalz = forcetow * sina;
886                         
887             # local forces by pitch: gamma
888             var flpitchx = fglobalx * cosg - fglobalz * sing;
889             var flpitchy = fglobaly;
890             var flpitchz = fglobalx * sing + fglobalz * cosg;
891             
892             # local forces by roll: delta
893             var flrollx  =   flpitchx;
894             var flrolly  =   flpitchy * cosd + flpitchz * sind;
895             var flrollz  = - flpitchy * sind + flpitchz * cosd;
896       
897             # asigning to LOCAL coord of plane
898             var forcex = flrollx;
899             var forcey = flrolly;
900             var forcez = flrollz;
901             #print("fx=",forcex,"  fy=",forcey,"  fz=",forcez);
902       
903             # JSBSim-body-frame:  x-> nose / y -> right wing / z -> down
904             # apply forces to hook (forces are in LBS or N see above)
905             var hitchname = getprop("sim/hitches/aerotow/basename_force_jsbsim");      
906             setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "_x/magnitude", forcex);
907             setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "_y/magnitude", forcey);
908             setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "_z/magnitude", forcez);
909
910           }  # end force < break force
911           else {  # rope is broken
912             props.globals.getNode("sim/hitches/aerotow/broken").setBoolValue(1);
913             #setprop("sim/messages/atc", sprintf("Oh no, the tow is broken"));
914             releaseHitch("aerotow"); # open=1 / forces=0 / remove towrope 
915           }
916      
917           #############################################  report forces  ##############################################
918
919           # if we are connected to an MP aircraft and master
920           var nodeIsMpAircraft = getprop("sim/hitches/aerotow/tow/connected-to-mp-node");
921           if ( nodeIsMpAircraft and !isSlave ){
922             #print("report Forces");
923
924             # transform my hitch coordinates to cartesian earth coordinates    
925             var myHitchCartEarth = geodtocart(myHitch_pos.lat(),myHitch_pos.lon(),myHitch_pos.alt() );
926             var myHitchXearth_m = myHitchCartEarth[0];
927             var myHitchYearth_m = myHitchCartEarth[1];
928             var myHitchZearth_m = myHitchCartEarth[2];
929
930             # transform MP hitch coordinates to cartesian earth coordinates    
931             var aiHitchCartEarth = geodtocart(aiHitch_pos.lat(),aiHitch_pos.lon(),aiHitch_pos.alt() );
932             var aiHitchXearth_m = aiHitchCartEarth[0];
933             var aiHitchYearth_m = aiHitchCartEarth[1];
934             var aiHitchZearth_m = aiHitchCartEarth[2];
935
936             # calculate normal vector in tow direction in cartesian earth coordinates
937             var dx = aiHitchXearth_m - myHitchXearth_m;
938             var dy = aiHitchYearth_m - myHitchYearth_m;
939             var dz = aiHitchZearth_m - myHitchZearth_m;
940             var dl = math.sqrt( dx * dx + dy * dy + dz * dz );
941       
942             var forcetowX_N = forcetow_N * dx / dl;
943             var forcetowY_N = forcetow_N * dy / dl;
944             var forcetowZ_N = forcetow_N * dz / dl;
945       
946             setprop("sim/hitches/aerotow/tow/dist", distance);
947             setprop("sim/hitches/aerotow/tow/end-force-x", -forcetowX_N); # force acts in 
948             setprop("sim/hitches/aerotow/tow/end-force-y", -forcetowY_N); # opposite direction  
949             setprop("sim/hitches/aerotow/tow/end-force-z", -forcetowZ_N); # at tow end
950     
951           } # end report forces
952     
953           }  # end: timelag
954           else{
955             #print("forces NOT updated!");
956           }
957         }  # end forces/JSBSim
958
959
960       }  # end: aiNodeID
961       else{  
962         if ( !aimember.getNode("valid").getValue() ) {  # MP-aircraft is accessable (node is valid)
963           #print("MP-aircraft isn't valid!");
964           props.globals.getNode("sim/hitches/aerotow/open").setBoolValue(1);  # open my hitch
965           setprop("sim/messages/atc", sprintf("MP-aircraft disappeared!" )); 
966         }
967       }
968     }  # end: check id != nil
969   }  # end: loop over aiobjects
970
971
972 }   # end function aerotow
973       
974
975
976 # ######################################################################################################################
977 #                                                         winch function 
978 # ######################################################################################################################
979
980 var winch = func (open){
981  
982   var FT2M = 0.30480;
983   var M2FT = 1. / FT2M;
984   var RAD2DEG = 57.29578;
985   var DEG2RAD = 1. / RAD2DEG;
986      
987   if (!open ) {
988   
989   ###########################################  my hitch position  ############################################
990   
991   myPosition = geo.aircraft_position();
992   var my_head_deg  = getprop("orientation/heading-deg");
993   var my_roll_deg  = getprop("orientation/roll-deg");
994   var my_pitch_deg = getprop("orientation/pitch-deg");
995   
996   # hitch coordinates in YASim-system (x-> nose / y -> left wing / z -> up)
997   var x = getprop("sim/hitches/winch/local-pos-x");
998   var y = getprop("sim/hitches/winch/local-pos-y");
999   var z = getprop("sim/hitches/winch/local-pos-z");
1000
1001   var alpha_deg = my_roll_deg * (1.);   # roll clockwise (looking in x-direction) := +
1002   var beta_deg  = my_pitch_deg * (-1.); # pitch clockwise (looking in y-direction) := -
1003   
1004   # transform hook coordinates
1005   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.);
1006
1007   var install_distance_m = Xn[0]; # in front of ref-point of glider
1008   var install_side_m     = Xn[1];
1009   var install_alt_m      = Xn[2];  
1010     
1011   var myHitch_pos    = myPosition.apply_course_distance( my_head_deg , install_distance_m ); 
1012   var myHitch_pos    = myPosition.apply_course_distance( my_head_deg - 90. , install_side_m );   
1013   myHitch_pos.set_alt(myPosition.alt() + install_alt_m); 
1014   
1015   ###########################################  winch hitch position  ############################################
1016
1017   # get coordinates  
1018   var winch_global_pos_x = getprop("sim/hitches/winch/winch/global-pos-x"); 
1019   var winch_global_pos_y = getprop("sim/hitches/winch/winch/global-pos-y");
1020   var winch_global_pos_z = getprop("sim/hitches/winch/winch/global-pos-z");
1021
1022   var winch_geod = carttogeod(winch_global_pos_x,winch_global_pos_y,winch_global_pos_z);
1023
1024   var ai_lat = winch_geod[0];
1025   var ai_lon = winch_geod[1]; 
1026   #var ai_alt = winch_geod[2] * FT2M; 
1027   var ai_alt = winch_geod[2];
1028   #print("ai_lat,lon,alt",ai_lat,ai_lon,ai_alt);
1029
1030   var aiHitch_pos = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt );
1031   
1032
1033   ###########################################  distance between hitches  #####################################
1034     
1035   var distance = (myHitch_pos.direct_distance_to(aiHitch_pos));    # distance to winch in meter
1036   var aiHitchheadto = (myHitch_pos.course_to(aiHitch_pos));
1037   var height = myHitch_pos.alt() - aiHitch_pos.alt();
1038
1039   var aiHitchpitchto = -math.asin((myHitch_pos.alt()-aiHitch_pos.alt())/distance) / 0.01745;
1040   #print("  pitch: ", aiHitchpitchto);
1041   
1042   # update position of rope
1043   setprop("ai/models/winchrope/position/latitude-deg", myHitch_pos.lat());
1044   setprop("ai/models/winchrope/position/longitude-deg", myHitch_pos.lon());
1045   setprop("ai/models/winchrope/position/altitude-ft", myHitch_pos.alt() * M2FT);
1046   #print("ai_lat,lon,alt",myHitch_pos.lat(),"   ",myHitch_pos.lon(),"   ",myHitch_pos.alt() );
1047     
1048   # update pitch and heading of rope
1049   setprop("ai/models/winchrope/orientation/true-heading-deg", aiHitchheadto);
1050   setprop("ai/models/winchrope/orientation/pitch-deg", aiHitchpitchto);
1051
1052   # update length of rope
1053   setprop("sim/hitches/winch/tow/dist", distance);
1054   #print("distance=",distance);
1055
1056  
1057   #############################################  calc forces  ##################################################
1058
1059   # calc forces only for JSBSim-aircrafts
1060   
1061   # tow-end-forces must be reported in N to be consiststent to Yasim-aircrafts
1062   # hitch-forces must be LBS to be consistent to the JSBSim "external_forces/.../magnitude" definition 
1063
1064   if ( getprop("sim/flight-model") == "jsb"  ) {
1065    
1066     var spool_max = getprop("sim/hitches/winch/winch/max-spool-speed-m-s");
1067     var unspool_max = getprop("sim/hitches/winch/winch/max-unspool-speed-m-s"); 
1068     var max_force_N = getprop("sim/hitches/winch/winch/max-force-N");
1069     var max_power_W = getprop("sim/hitches/winch/winch/max-power-kW") * 1000.;   
1070     var breakforce_N = getprop("sim/hitches/winch/tow/break-force-N"); 
1071     var elastic_constant = getprop("sim/hitches/winch/tow/elastic-constant");
1072     var towlength_m = getprop("sim/hitches/winch/tow/length");
1073     var max_tow_length_m = getprop("sim/hitches/winch/winch/max-tow-length-m");
1074     var spoolspeed = getprop("sim/hitches/winch/winch/actual-spool-speed-m-s");
1075     var spool_acceleration = getprop("sim/hitches/winch/winch/spool-acceleration-m-s-s");
1076     var delta_t = getprop("sim/time/delta-sec"); 
1077
1078     var towlength_new_m = towlength_m - spoolspeed * delta_t;
1079     var delta_towlength_m = distance - towlength_new_m;
1080     #print("towlength_m= ", towlength_m , "  elastic_constant= ", elastic_constant,"  delta_towlength_m= ", delta_towlength_m);
1081
1082     if ( getprop("sim/hitches/winch/winch/clutched") ) {
1083       var delta_spoolspeed =  spool_acceleration * delta_t; 
1084       spoolspeed = spoolspeed + delta_spoolspeed ;
1085       if ( spoolspeed > spool_max ) spoolspeed = spool_max;
1086     }
1087     else {   # un-clutched 
1088       # --- experimental --- #
1089
1090       # we assume that the the winch-operator avoids tow sagging ( => rigid rope; negativ forces allowed)
1091       var forcetow_N = elastic_constant * delta_towlength_m / towlength_new_m;
1092       
1093       # drag of tow-rope ( magic! )
1094       var magic_constant = getprop("sim/hitches/winch/winch/magic-constant");
1095       tow_drag_N = spoolspeed * spoolspeed * math.sqrt( math.sqrt( height * height ) * max_tow_length_m ) / magic_constant ;
1096       
1097       # mass = tow-mass only (drum-mass ignored)
1098       var mass_kg = max_tow_length_m * getprop("sim/hitches/winch/tow/weight-per-m-kg-m");
1099
1100       var acceleration = ( forcetow_N - tow_drag_N ) / mass_kg;
1101       var delta_spoolspeed = acceleration * delta_t; 
1102       spoolspeed = spoolspeed - delta_spoolspeed;
1103       if ( spoolspeed < - unspool_max ) spoolspeed = - unspool_max;    
1104       #print("spoolspeed= ",spoolspeed,"  delta_spoolspeed= ",delta_spoolspeed,"  delta_towlength= ", delta_towlength_m);
1105       #print("forcetow_N= ",forcetow_N,"  tow_drag_N= ",tow_drag_N,"  acceleration= ", acceleration);
1106     }
1107
1108     if ( delta_towlength_m < 0. ) {
1109       var forcetow_N = 0.;
1110     }
1111     else{
1112       var forcetow_N = elastic_constant * delta_towlength_m / towlength_new_m;
1113     }
1114     
1115     if ( forcetow_N > max_force_N ) {
1116       forcetow_N = max_force_N;
1117       var towlength_new_m = distance / ( forcetow_N / elastic_constant + 1. );
1118       spoolspeed = (towlength_m - towlength_new_m ) / delta_t;
1119     }  
1120     
1121     var power = forcetow_N * spoolspeed;
1122     if ( power > max_power_W) {
1123       power = max_power_W;
1124       spoolspeed = power / forcetow_N;
1125       towlength_new_m = towlength_m - spoolspeed * delta_t;
1126     }
1127     #print("power=",power,"  spoolspeed=",spoolspeed,"  force=",forcetow_N);
1128     
1129     setprop("sim/hitches/winch/tow/length",towlength_new_m);
1130     setprop("sim/hitches/winch/winch/actual-spool-speed-m-s",spoolspeed);
1131     setprop("sim/hitches/winch/winch/actual-force-N",forcetow_N);
1132     
1133     # force due to tow-weight (acts in tow direction at the heigher hitch)
1134     var force_due_to_weight_N = getprop("sim/hitches/winch/tow/weight-per-m-kg-m") * 9.81 * height;
1135     if (height < 0. ) force_due_to_weight_N = 0.; 
1136     
1137     forcetow_N = forcetow_N + force_due_to_weight_N;
1138     var forcetow_LBS = forcetow_N * 0.224809;   # N -> LBF     
1139     #print(" forcetow_N ", forcetow_N , "  distance ", distance,"  ", breakforce_N);
1140     #print(" forcetow_N=", forcetow_N , "  force_due_to_weight_N=", force_due_to_weight_N,"  height=",height);
1141
1142     if ( forcetow_N < breakforce_N ) {
1143       
1144       var distancepr = (myHitch_pos.distance_to(aiHitch_pos)); 
1145       
1146       # correct a failure, if the projected length is larger than direct length
1147       if (distancepr > distance) { distancepr = distance;} 
1148            
1149       var alpha = math.acos( (distancepr / distance) );
1150       if ( aiHitch_pos.alt() > myHitch_pos.alt()) alpha = - alpha;
1151       var beta = ( aiHitchheadto - my_head_deg ) * DEG2RAD; 
1152       var gamma = my_pitch_deg * DEG2RAD;
1153       var delta = my_roll_deg * DEG2RAD;
1154            
1155       var sina = math.sin(alpha);
1156       var cosa = math.cos(alpha);
1157       var sinb = math.sin(beta);
1158       var cosb = math.cos(beta);
1159       var sing = math.sin(gamma);
1160       var cosg = math.cos(gamma);
1161       var sind = math.sin(delta);
1162       var cosd = math.cos(delta);
1163       
1164       #var forcetow = forcetow_N;   # we deliver N to JSBSim
1165       var forcetow = forcetow_LBS;   # we deliver LBS to JSBSim
1166       
1167       # global forces: alpha beta
1168       var fglobalx = forcetow * cosa * cosb;
1169       var fglobaly = forcetow * cosa * sinb;
1170       var fglobalz = forcetow * sina;
1171
1172       # local forces by pitch: gamma
1173       var flpitchx = fglobalx * cosg - fglobalz * sing;
1174       var flpitchy = fglobaly;
1175       var flpitchz = fglobalx * sing + fglobalz * cosg;
1176             
1177       # local forces by roll: delta
1178       var flrollx  =   flpitchx;
1179       var flrolly  =   flpitchy * cosd + flpitchz * sind;
1180       var flrollz  = - flpitchy * sind + flpitchz * cosd;
1181
1182       # asigning to LOCAL coord of plane
1183       var forcex = flrollx;
1184       var forcey = flrolly;
1185       var forcez = flrollz;
1186       #print("fx=",forcex,"  fy=",forcey,"  fz=",forcez);
1187             
1188       # JSBSim-body-frame:  x-> nose / y -> right wing / z -> down
1189       # apply forces to hook (forces are in LBS or N see above)
1190       var hitchname = getprop("sim/hitches/winch/basename_force_jsbsim");      
1191       setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "_x/magnitude", forcex);
1192       setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "_y/magnitude", forcey);
1193       setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "_z/magnitude", forcez);
1194
1195       # check, if auto-release condition is reached
1196       var rope_angle_deg = math.atan2(forcez , forcex ) * RAD2DEG;
1197       #print("rope_angle_deg=",rope_angle_deg);
1198       if (rope_angle_deg > getprop("sim/hitches/winch/automatic-release-angle-deg") ) releaseWinch();
1199
1200     }  # end force < break force
1201     else {  # rope is broken
1202       props.globals.getNode("sim/hitches/winch/broken").setBoolValue(1);
1203       releaseWinch(); 
1204     }
1205
1206     if ( towlength_new_m > max_tow_length_m ) {
1207       setprop("sim/messages/atc", sprintf("tow length exceeded!"));
1208       releaseWinch(); 
1209     }
1210
1211   }  # end forces/JSBSim
1212
1213   }  # end hitch is closed (open == 0)
1214
1215 }  # end function winch
1216
1217
1218 # ######################################################################################################################
1219 #                                                      create towrope
1220 # ######################################################################################################################
1221
1222 var createTowrope = func (device){
1223
1224   # create the towrope in the model property tree
1225   #print("createTowrope for ",device);
1226
1227   if ( getprop("sim/hitches/" ~ device ~ "/rope/exist") == 0 ) {   # does the towrope exist?
1228
1229     # get the next free model id
1230     var freeModelid = getFreeModelID();
1231
1232     props.globals.getNode("sim/hitches/" ~ device ~ "/rope/model_id").setIntValue(freeModelid);
1233     props.globals.getNode("sim/hitches/" ~ device ~ "/rope/exist").setBoolValue(1);
1234     
1235     var towrope_ai  = props.globals.getNode("ai/models/" ~ device ~ "rope", 1);
1236     var towrope_mod  = props.globals.getNode("models", 1);
1237         
1238     towrope_ai.getNode("id", 1).setIntValue(4711);
1239     towrope_ai.getNode("callsign", 1).setValue("towrope");
1240     towrope_ai.getNode("valid", 1).setBoolValue(1);
1241     towrope_ai.getNode("position/latitude-deg", 1).setValue(0.);
1242     towrope_ai.getNode("position/longitude-deg", 1).setValue(0.);
1243     towrope_ai.getNode("position/altitude-ft", 1).setValue(0.);
1244     towrope_ai.getNode("orientation/true-heading-deg", 1).setValue(0.);
1245     towrope_ai.getNode("orientation/pitch-deg", 1).setValue(0.);
1246     towrope_ai.getNode("orientation/roll-deg", 1).setValue(0.);
1247
1248     towrope_mod.model = towrope_mod.getChild("model", freeModelid, 1);
1249     towrope_mod.model.getNode("path", 1).setValue(getprop("sim/hitches/" ~ device ~ "/rope/path_to_model") );
1250     towrope_mod.model.getNode("longitude-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/longitude-deg");
1251     towrope_mod.model.getNode("latitude-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/latitude-deg");
1252     towrope_mod.model.getNode("elevation-ft-prop", 1).setValue("ai/models/" ~ device ~ "rope/position/altitude-ft");
1253     towrope_mod.model.getNode("heading-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/true-heading-deg");
1254     towrope_mod.model.getNode("roll-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/roll-deg");
1255     towrope_mod.model.getNode("pitch-deg-prop", 1).setValue("ai/models/" ~ device ~ "rope/orientation/pitch-deg");
1256     towrope_mod.model.getNode("load", 1).remove();
1257   }  # end towrope exist
1258 }
1259
1260
1261 # ######################################################################################################################
1262 #                                     get the next free id of "models/model" members        
1263 # ######################################################################################################################
1264
1265 var getFreeModelID = func {
1266   #print("getFreeModelID");
1267   var modelid = 0;   # next unused id  
1268   modelobjects = props.globals.getNode("models", 1).getChildren(); 
1269   foreach ( var member; modelobjects ) { 
1270     if ( (var c = member.getIndex()) != nil) { 
1271       modelid = c + 1;
1272     }
1273   }
1274   #print("modelid=",modelid);    
1275   return(modelid);
1276 }
1277
1278
1279 # ######################################################################################################################
1280 #                                                       close hitch       
1281 # ######################################################################################################################
1282
1283 var closeHitch = func {
1284   
1285   #print("closeHitch");
1286
1287   setprop("sim/hitches/aerotow/open", "false");
1288   setprop("sim/hitches/aerotow/mp_oldOpen", "true");
1289   
1290 } # End function closeHitch
1291
1292
1293 # ######################################################################################################################
1294 #                                                     release hitch       
1295 # ######################################################################################################################
1296
1297 var releaseHitch = func (device){
1298   
1299   #print("releaseHitch");
1300   
1301   if ( getprop("sim/flight-model") == "yasim" ) return; # bypass this routine for Yasim-aircrafts
1302
1303   setprop("sim/hitches/" ~ device ~ "/open", "true");
1304   
1305   var hitchname = getprop("sim/hitches/" ~ device ~ "/basename_force_jsbsim");
1306   setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "_x/magnitude", 0.);
1307   setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "_y/magnitude", 0.);
1308   setprop("fdm/jsbsim/external_reactions/" ~ hitchname ~ "_z/magnitude", 0.);
1309  
1310   if ( device == "aerotow" ) {
1311     setprop("sim/hitches/aerotow/tow/end-force-x", 0.);          # MP tow-end forces 
1312     setprop("sim/hitches/aerotow/tow/end-force-y", 0.);          # 
1313     setprop("sim/hitches/aerotow/tow/end-force-z", 0.);          # 
1314   }
1315   
1316 } # End function releaseHitch
1317
1318
1319 # ######################################################################################################################
1320 #                                                  remove/delete towrope       
1321 # ######################################################################################################################
1322
1323 var removeTowrope = func (device){
1324   
1325   # remove the towrope from the property tree ai/models
1326   # remove the towrope from the property tree models/
1327      
1328   if ( getprop("sim/hitches/" ~ device ~ "/rope/exist") == 1 ) {   # does the towrope exist?
1329
1330     # remove 3d model from scenery
1331     # identification is /models/model[x] with x=id_model
1332     var id_model = getprop("sim/hitches/" ~ device ~ "/rope/model_id");
1333     var modelsNode = "models/model[" ~ id_model ~ "]";
1334     props.globals.getNode(modelsNode).remove();
1335     props.globals.getNode("ai/models/" ~ device ~ "rope").remove();
1336     #print("towrope removed");
1337     setprop("sim/hitches/" ~ device ~ "/rope/exist", 0);
1338   }
1339   
1340 }
1341
1342
1343 # ######################################################################################################################
1344 #                                           pull in towrope after hitch has been opened       
1345 # ######################################################################################################################
1346
1347 var pull_in_rope = func {
1348
1349   var deg2rad = math.pi / 180.;   
1350   var FT2M = 0.30480;
1351
1352   if ( getprop("sim/hitches/winch/open") ) {
1353
1354     # get length of rope
1355     #var distance = getprop("sim/hitches/winch/tow/dist");
1356
1357     var towlength_m = getprop("sim/hitches/winch/tow/length");
1358     var spoolspeed = getprop("sim/hitches/winch/winch/max-spool-speed-m-s");  
1359     var delta_t = getprop("sim/time/delta-sec"); 
1360
1361     var delta_length_m = spoolspeed * delta_t;
1362     var towlength_new_m = towlength_m - delta_length_m;
1363     var towlength_min_m = getprop("sim/hitches/winch/winch/min-tow-length-m");
1364   
1365     if ( towlength_new_m > towlength_min_m ) {
1366       #print("actual towlength= ",towlength_new_m);
1367
1368       # get position of rope end (former myHitch_pos)
1369       var tow_lat = getprop("ai/models/winchrope/position/latitude-deg");
1370       var tow_lon = getprop("ai/models/winchrope/position/longitude-deg");
1371       var tow_alt_m = getprop("ai/models/winchrope/position/altitude-ft") * FT2M;
1372       # get pitch and heading of rope
1373       var tow_heading_deg = getprop("ai/models/winchrope/orientation/true-heading-deg");
1374       var tow_pitch_rad = getprop("ai/models/winchrope/orientation/pitch-deg") * deg2rad;
1375
1376       var aiTow_pos = geo.Coord.set_latlon( tow_lat, tow_lon, tow_alt_m ); 
1377                
1378       var delta_distance_m = delta_length_m * math.cos(tow_pitch_rad);  
1379       var delta_alt_m      = delta_length_m * math.sin(tow_pitch_rad);
1380       # vertical sink rate not yet taken into account!    
1381       aiTow_pos    = aiTow_pos.apply_course_distance( tow_heading_deg , delta_distance_m ); 
1382       aiTow_pos.set_alt(tow_alt_m + delta_alt_m); 
1383       #print("aiTow_pos.alt()= ",aiTow_pos.alt(),"  ",tow_alt_m + delta_alt_m);
1384    
1385       # update position of rope
1386       setprop("ai/models/winchrope/position/latitude-deg", aiTow_pos.lat());
1387       setprop("ai/models/winchrope/position/longitude-deg", aiTow_pos.lon());
1388       setprop("ai/models/winchrope/position/altitude-ft", aiTow_pos.alt() * M2FT);
1389
1390       # update length of rope   
1391       setprop("sim/hitches/winch/tow/length",towlength_new_m);
1392     
1393       settimer( pull_in_rope , 0 );
1394     }  # end towlength > min
1395     else {
1396       #print("pull in finished!");
1397       setprop("sim/hitches/winch/winch/actual-spool-speed-m-s", 0. );
1398       removeTowrope("winch");   # remove towrope model
1399     }
1400   
1401   }  # end if open
1402   
1403 }
1404
1405
1406 # ######################################################################################################################
1407 #                                              set some AI-object default values
1408 # ######################################################################################################################
1409
1410 var setAIObjectDefaults = func (){
1411
1412   # set some default variables, needed to identify, if the found object is an AI-object, a "non-interactiv MP-object or
1413   # an interactive MP-object
1414
1415   var aiNodeID = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id");   # id of former found ai/mp aircraft
1416
1417   aiobjects = props.globals.getNode("ai/models").getChildren();    
1418   foreach (var aimember; aiobjects) {   
1419     if ( (var c = aimember.getNode("id") ) != nil ) { 
1420       var testprop = c.getValue();
1421       if ( testprop ==  aiNodeID) {
1422          # Set some dummy values. In case of an "interactive"-MP plane 
1423          # the correct values will be transmitted in the following loop.
1424          # Create this variables if not present. 
1425          aimember.getNode("sim/hitches/aerotow/local-pos-x",1).setValue(-5.);
1426          aimember.getNode("sim/hitches/aerotow/local-pos-y",1).setValue(0.);
1427          aimember.getNode("sim/hitches/aerotow/local-pos-z",1).setValue(0.);
1428          aimember.getNode("sim/hitches/aerotow/tow/dist",1).setValue(-1.);             
1429       }
1430     } 
1431   }
1432   
1433 }
1434
1435
1436 # ######################################################################################################################
1437 #                                                  place winch model
1438 # ######################################################################################################################
1439
1440 var setWinchPositionAuto = func {
1441   
1442   # remove already existing winch model
1443   if ( getprop("/sim/hitches/winch/winch/winch-model-index") != nil ) {
1444     var id_model = getprop("/sim/hitches/winch/winch/winch-model-index");
1445     var modelsNode = "models/model[" ~ id_model ~ "]";
1446     props.globals.getNode(modelsNode).remove();
1447     #print("winch model removed");
1448   }  
1449
1450   var initial_length_m = getprop("sim/hitches/winch/winch/initial-tow-length-m");
1451   var ac_pos = geo.aircraft_position();              # get position of aircraft
1452   var ac_hd  = getprop("orientation/heading-deg");   # get heading of aircraft
1453    
1454   # setup winch
1455   # get initial runway position
1456   var ipos_lat_deg = getprop("sim/presets/latitude-deg");
1457   var ipos_lon_deg = getprop("sim/presets/longitude-deg");
1458   var ipos_hd_deg  = getprop("sim/presets/heading-deg");
1459   var ipos_alt_m = geo.elevation(ipos_lat_deg,ipos_lon_deg);
1460   var ipos_geo = geo.Coord.new().set_latlon(ipos_lat_deg, ipos_lon_deg, ipos_alt_m); 
1461   # offset to initial position
1462   var deviation = (ac_pos.distance_to(ipos_geo)); 
1463   # if deviation is too much, locate winch in front of glider, otherwise locate winch to end of runway
1464   if ( deviation > 200) { 
1465     var w = ac_pos.apply_course_distance( ac_hd , initial_length_m -1. ); 
1466   }
1467   else {
1468     var w = ipos_geo.apply_course_distance( ipos_hd_deg , initial_length_m - 1. ); 
1469   }
1470   var wpalt = geo.elevation(w.lat(), w.lon()); 
1471   w.set_alt(wpalt);
1472
1473   var winchModel = geo.put_model("Models/Airport/supacat_winch.xml", w.lat(), w.lon(), (w.alt()+0.81), (w.course_to(ac_pos) ));
1474             
1475   setprop("/sim/hitches/winch/winch/global-pos-x", w.x());
1476   setprop("/sim/hitches/winch/winch/global-pos-y", w.y());
1477   setprop("/sim/hitches/winch/winch/global-pos-z", w.z());
1478
1479   setprop("sim/hitches/winch/tow/dist",initial_length_m - 1.);
1480   setprop("sim/hitches/winch/tow/length",initial_length_m);
1481  
1482   #print("name=",winchModel.getName(),"  Index=",winchModel.getIndex(),"  Type=",winchModel.getType() );
1483   #print("val=",winchModel.getValue(),"  children=",winchModel.getChildren(),"  size=",size(winchModel) );  
1484   setprop("/sim/hitches/winch/winch/winch-model-index",winchModel.getIndex() );
1485   setprop("sim/messages/pilot", sprintf("Connected to winch!"));
1486   
1487   props.globals.getNode("sim/hitches/winch/open").setBoolValue(0);     
1488
1489 } # End function setWinchPositionAuto
1490
1491
1492 # ######################################################################################################################
1493 #                                                  clutch / un-clutch winch
1494 # ######################################################################################################################
1495
1496 var runWinch = func {
1497
1498   if ( !getprop("sim/hitches/winch/winch/clutched") ) {  
1499     setprop("sim/hitches/winch/winch/clutched","true");
1500     setprop("sim/messages/pilot", sprintf("Winch clutched!"));
1501   } 
1502   else { 
1503     setprop("sim/hitches/winch/winch/clutched","false");
1504     setprop("sim/messages/pilot", sprintf("Winch un-clutched!"));
1505   }
1506  
1507 } # End function runWinch
1508
1509
1510 # ######################################################################################################################
1511 #                                                     release winch
1512 # ######################################################################################################################
1513
1514 var releaseWinch = func {
1515   
1516   setprop("sim/hitches/winch/open","true");
1517  
1518 } # End function releaseWinch
1519
1520
1521 # ######################################################################################################################
1522 #                                                      point transformation  
1523 # ######################################################################################################################
1524
1525 var PointRotate3D = func (x,y,z,xr,yr,zr,alpha_deg,beta_deg,gamma_deg){
1526
1527   # ---------------------------------------------------------------------------------
1528   #   rotates point (x,y,z) about all 3 cartesian axis
1529   #   center of rotation (xr,yr,zr)
1530   #   angle of rotation about x-axis = alpha
1531   #   angle of rotation about y-axis = beta
1532   #   angle of rotation about z-axis = gamma
1533   #   delivers new point coordinates (x_new,y_new,z_new)
1534   # ---------------------------------------------------------------------------------
1535   #
1536   #
1537   # Definitions:
1538   # ---------------- 
1539   #
1540   # x        y           z     
1541   # alpha    beta        gamma   
1542   #                       
1543   #                        
1544   #       z                
1545   #       |  y             
1546   #       | /            
1547   #       |/             
1548   #       ----->x                
1549   #                      
1550   #----------------------------------------------------------------------------------  
1551
1552   # Transformation in rotation-system X_rel = X-Xr = (x-xr, y-yr, z-zr)
1553   var x_rel = x-xr;
1554   var y_rel = y-yr;  
1555   var z_rel = z-zr;  
1556
1557   # Trigonometry
1558   var deg2rad = math.pi / 180.;   
1559
1560   var alpha_rad    = deg2rad * alpha_deg;
1561   var beta_rad     = deg2rad * beta_deg;
1562   var gamma_rad    = deg2rad * gamma_deg;
1563
1564   var sin_alpha = math.sin(alpha_rad);
1565   var cos_alpha = math.cos(alpha_rad);
1566
1567   var sin_beta  = math.sin(beta_rad);
1568   var cos_beta  = math.cos(beta_rad);
1569
1570   var sin_gamma = math.sin(gamma_rad);
1571   var cos_gamma = math.cos(gamma_rad);
1572
1573   # Matrices 
1574   #
1575   # Rotate about x-axis Rx(alpha) 
1576   #
1577   #             Rx11 Rx12 Rx13      1     0            0
1578   # Rx(alpha)=  Rx21 Rx22 Rx23   =  0  cos(alpha)  -sin(alpha)
1579   #             Rx31 Rx32 Rx33      0  sin(alpha)   cos(alpha)
1580   #
1581   var Rx11 = 1.;
1582   var Rx12 = 0.;
1583   var Rx13 = 0.;
1584   var Rx21 = 0.;
1585   var Rx22 = cos_alpha;
1586   var Rx23 = - sin_alpha;
1587   var Rx31 = 0.;
1588   var Rx32 = sin_alpha;
1589   var Rx33 = cos_alpha;
1590   #
1591   # Rotate about y-axis Ry(beta) 
1592   #
1593   #            Ry11 Ry12 Ry13      cos(beta)  0   sin(beta)   
1594   # Ry(beta)=  Ry21 Ry22 Ry23   =      0      1      0 
1595   #            Ry31 Ry32 Ry33     -sin(beta)  0   cos(beta)
1596   #
1597   var Ry11 = cos_beta;
1598   var Ry12 = 0.;
1599   var Ry13 = sin_beta;
1600   var Ry21 = 0.;
1601   var Ry22 = 1.;
1602   var Ry23 = 0.;
1603   var Ry31 = - sin_beta;
1604   var Ry32 = 0.;
1605   var Ry33 = cos_beta;
1606   #
1607   # Rotate about z-axis Rz(gamma) 
1608   #
1609   #            Rz11 Rz12 Rz13      cos(gamma)  -sin(gamma)  0  
1610   # Rz(gamma)= Rz21 Rz22 Rz23   =  sin(gamma)   cos(gamma)  0   
1611   #            Rz31 Rz32 Rz33          0            0       1
1612   #
1613   var Rz11 = cos_gamma;
1614   var Rz12 = - sin_gamma;
1615   var Rz13 = 0.;
1616   var Rz21 = sin_gamma;
1617   var Rz22 = cos_gamma;
1618   var Rz23 = 0.;
1619   var Rz31 = 0.;
1620   var Rz32 = 0.;
1621   var Rz33 = 1.; 
1622   #
1623   # First rotation about x-axis
1624   # X_x = Rx*X_rel
1625   var x_x = Rx11 * x_rel + Rx12 * y_rel + Rx13 * z_rel;
1626   var y_x = Rx21 * x_rel + Rx22 * y_rel + Rx23 * z_rel;
1627   var z_x = Rx31 * x_rel + Rx32 * y_rel + Rx33 * z_rel;  
1628   #
1629   # subsequent rotation about y-axis
1630   # X_xy = Ry*X_x
1631   var x_xy = Ry11 * x_x + Ry12 * y_x + Ry13 * z_x;
1632   var y_xy = Ry21 * x_x + Ry22 * y_x + Ry23 * z_x;
1633   var z_xy = Ry31 * x_x + Ry32 * y_x + Ry33 * z_x; 
1634   #
1635   # subsequent rotation about z-axis:
1636   # X_xyz = Rz*X_xy
1637   var x_xyz = Rz11 * x_xy + Rz12 * y_xy + Rz13 * z_xy;
1638   var y_xyz = Rz21 * x_xy + Rz22 * y_xy + Rz23 * z_xy;
1639   var z_xyz = Rz31 * x_xy + Rz32 * y_xy + Rz33 * z_xy;
1640   
1641   # Back transformation  X_rel = X-Xr = (x-xr, y-yr, z-zr)
1642   var xn = xr + x_xyz;
1643   var yn = yr + y_xyz;
1644   var zn = zr + z_xyz;
1645   
1646   var Xn = [xn,yn,zn];
1647   
1648   return Xn;
1649   
1650 }  
1651   
1652 ##################################################################################################################################
1653
1654
1655 # todo:
1656 # ------
1657 #
1658 # - animate rope slack
1659 # - pull in towrope: take sink rate of rope into account
1660 # - dynamic ID for ai-rope-model
1661 #
1662 # Please contact D-NXKT at yahoo.de for bug-reports, suggestions, ...
1663