towing: replace 3 JSBSim-forces with one direction with 1 force and 3 directions
[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.    
48 #
49 # <external_reactions>
50 #  <force name="hitch" 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>0.0</x>
58 #    <y>0.0</y>
59 #    <z>0.0</z>
60 #   </direction>
61 #  </force>
62 # </external_reactions>
63
64 # 2. Define controls for aerotowing and winch.
65 # Add the following key bindings in "yourAircraft-set.xml":
66 # <input> 
67 #  <keyboard> 
68 #
69 #   <key n="15">
70 #     <name>Ctrl-o</name>
71 #     <desc>Find aircraft for aerotow</desc>
72 #     <binding>
73 #       <command>nasal</command>
74 #       <script>towing.findBestAIObject()</script>
75 #     </binding>
76 #   </key>
77 #   
78 #   <key n="111">
79 #     <name>o</name>
80 #     <desc>Lock aerotow-hook</desc>
81 #     <binding>
82 #       <command>nasal</command>
83 #       <script>towing.closeHitch()</script>
84 #     </binding>
85 #   </key>
86 #   
87 #   <key n="79">
88 #     <name>O</name>
89 #     <desc>Open aerotow-hook</desc>
90 #     <binding>
91 #       <command>nasal</command>
92 #       <script>towing.releaseHitch("aerotow")</script>
93 #     </binding>
94 #   </key>
95 #
96 #   <key n="23">
97 #     <name>Ctrl-w</name>
98 #     <desc>Place Winch and hook in</desc>
99 #     <binding>
100 #       <command>nasal</command>
101 #       <script>towing.setWinchPositionAuto()</script>
102 #     </binding>
103 #   </key>
104 #   
105 #   <key n="119">
106 #     <name>w</name>
107 #     <desc>Start winch</desc>
108 #     <binding>
109 #       <command>nasal</command>
110 #       <script>towing.runWinch()</script>
111 #     </binding>
112 #   </key>
113 #   
114 #   <key n="87">
115 #     <name>W</name>
116 #     <desc>Open winch-hook</desc>
117 #     <binding>
118 #       <command>nasal</command>
119 #       <script>towing.releaseHitch("winch")</script>
120 #     </binding>
121 #   </key>
122 #
123 #  </keyboard> 
124 # </input> 
125 #
126 # For towplanes only "key n=79" (Open aerotow-hook) is required!
127 #
128 #
129 # 3. Set mandatory properties:
130 #<sim>
131 # <hitches>
132 #  <aerotow>
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>
139 #  </aerotow>
140 #  <winch>
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>
145 #  </winch>
146 # </hitches>
147 #</sim>
148 #
149 # "force_name_jsbsim" must be the external force name in JSBSim.
150 #
151 # IMPORTANT:
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.
156 #
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". 
159
160
161 # 4. Set optional properties:
162 #<sim>
163 # <hitches>
164 #  <aerotow>
165 #   <tow>
166 #    <brake-force type="float">6000</brake-force>
167 #    <elastic-constant type="float">9000</elastic-constant>
168 #   </tow>
169 #   <rope>
170 #    <rope-diameter-mm type="float">20</rope-diameter-mm>
171 #   </rope>
172 #  </aerotow>
173 #  <winch>
174 #   <automatic-release-angle-deg type="float">70.</automatic-release-angle-deg>
175 #   <winch>
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"/>
184 #   </winch>
185 #   <tow>
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>
189 #   </tow>
190 #   <rope>
191 #    <rope-diameter-mm type="float">40</rope-diameter-mm>
192 #   </rope>
193 #  </winch>
194 # </hitches>
195 #<sim>
196 #
197 # That's it!
198
199
200 ##################################################  general info  ############################################
201 #
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.
208 #  
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"     
225 #
226 ##############################################################################################################
227
228
229
230
231 # ######################################################################################################################
232 #                                            check, if towing support makes sense     
233 # ######################################################################################################################
234
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!"); 
238
239
240 # ######################################################################################################################
241 #                                           set defaults / initialize at startup      
242 # ######################################################################################################################
243
244 # set defaults for properties that are NOT already defined 
245
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);
297
298  # additional properties  
299   if (props.globals.getNode("sim/hitches/aerotow/oldOpen") == nil ) 
300       props.globals.getNode("sim/hitches/aerotow/oldOpen", 1).setBoolValue(1);
301  
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.);
311
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.);
320  }
321
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.);
339
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);
348
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.);
354
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.);
364
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.); 
389  }
390
391   
392
393 # ######################################################################################################################
394 #                                                         main function 
395 # ######################################################################################################################
396
397 var towing = func {
398  
399   #print("function towing is running");
400   
401   var FT2M = 0.30480;
402   var M2FT = 1 / FT2M;
403   var dt = 0;
404
405   # -------------------------------  aerotow part -------------------------------
406
407   var open = getprop("sim/hitches/aerotow/open");
408   var oldOpen = getprop("sim/hitches/aerotow/oldOpen");
409     
410   if ( open != oldOpen ) {   # check if my hitch state has changed, if yes: message
411     #print("state has changed: open=",open,"  oldOpen=",oldOpen);
412
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")));
425         }
426         props.globals.getNode("sim/hitches/aerotow/broken").setBoolValue(0);
427       }  # end: JSBSim
428       if ( !getprop("sim/hitches/aerotow/open") ) {
429         # setup ai-towrope
430         createTowrope("aerotow");  
431
432         # set default hitch coordinates (needed for Ai- and non-interactive MP aircrafts)
433         setAIObjectDefaults() ;      
434       } 
435     }  # end hitch is closed
436     
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"));
441         } 
442         else {
443           setprop("sim/messages/pilot", sprintf("Opened hitch aerotow %i!",
444                                                 getprop("sim/hitches/aerotow/tow/connected-to-mp-node")));
445         }
446       releaseHitch("aerotow"); # open=1 / forces=0
447       }  # end: JSBSim
448       removeTowrope("aerotow");   # remove towrope model            
449     }  # end hitch is open 
450        
451     setprop("sim/hitches/aerotow/oldOpen",open); 
452   }  # end hitch state has changed
453
454   if (!open ) {
455     aerotow(open);
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
461         findBestAIObject();
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
467       dt = 0;
468     }
469   }
470
471
472   # -------------------------------  winch part -------------------------------
473
474   var winchopen = getprop("sim/hitches/winch/open");
475   var wincholdOpen = getprop("sim/hitches/winch/oldOpen");
476     
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");
492         }
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.);
495       }  # end: JSBSim
496       if ( !getprop("sim/hitches/winch/open") ) {
497         # setup ai-towrope
498         createTowrope("winch");  
499
500         # set default hitch coordinates (needed for Ai- and non-interactive MP aircrafts)
501         setAIObjectDefaults() ;      
502       } 
503     }  # end hitch is closed
504     
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"));
509         } 
510       releaseHitch("winch"); 
511       }  # end: JSBSim
512       pull_in_rope();
513     }  # end hitch is open
514     
515     setprop("sim/hitches/winch/oldOpen",winchopen); 
516   } # end hitch state has changed
517
518   if (!winchopen ) {
519     winch(winchopen);
520   }
521
522   settimer( towing, dt );
523    
524 }   # end towing
525
526
527 # ######################################################################################################################
528 #                                                   find best AI object
529 # ######################################################################################################################
530
531 var findBestAIObject = func (){
532
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
536   
537   # local variables
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
547
548   var FT2M = 0.30480;
549   
550   var nodeIsAiAircraft = 0;
551   var nodeIsMpAircraft = 0;
552   var running_as_autoconnect = 0;
553   var mp_open_last_state = 0;
554   var isSlave = 0;
555   
556   if ( getprop("sim/flight-model") == "yasim" ) return; # bypass this routine for Yasim-aircrafts
557   
558   #print("findBestAIObject");
559   
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");
563   }
564   
565   var towlength_m = props.globals.getNode("sim/hitches/aerotow/tow/length").getValue(); 
566
567   var bestdist_m = towlength_m; # initial value
568   
569   myPosition = geo.aircraft_position();
570   # todo: calculate exact hitch position
571
572   if( running_as_autoconnect ) {
573     var mycallsign = props.globals.getNode("sim/multiplay/callsign").getValue();
574     #print('mycallsign=',mycallsign);   
575   }
576
577   var found = 0;
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  );
586       
587       if( !nodeIsAiAircraft and !nodeIsMpAircraft ) continue;
588     
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      
593       }
594
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; 
598         
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;
604
605         var towEndNode = node;   
606         var nodeID = aimember.getNode("id").getValue();
607         var aicallsign = aimember.getNode("callsign").getValue();
608         #print('nodeId=',nodeID,'   AiCallsign=',aicallsign);
609
610         #set properties       
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.);
620
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.);
627                 
628         found = 1;
629       }
630     } 
631   }
632   if (found) {
633     if ( !running_as_autoconnect) {
634       setprop("sim/messages/pilot", sprintf("%s, I am on your hook, distance %4.3f meter.",aicallsign,bestdist_m));
635     }
636     else {
637       setprop("sim/messages/ai-plane", sprintf("%s: I am on your hook, distance %4.3f meter.",aicallsign,bestdist_m ));
638     }
639     if ( running_as_autoconnect ) {
640       isSlave = 1;
641       props.globals.getNode("sim/hitches/aerotow/is-slave").setBoolValue(isSlave);
642     }
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);
646     
647   }  # end: if found
648   else {
649     if (!running_as_autoconnect) {
650       setprop("sim/messages/atc", sprintf("Sorry, no aircraft for aerotow!"));
651     }
652   } 
653   
654 } # End function findBestAIObject
655
656
657 # ######################################################################################################################
658
659
660 # Start the towing animation ASAP
661 towing();
662
663
664
665 # ######################################################################################################################
666 #                                                         aerotow function 
667 # ######################################################################################################################
668
669 var aerotow = func (open){
670
671    #print("function aerotow is running");
672
673 #  if (!open ) {
674   
675   ###########################################  my hitch position  ############################################
676   
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");
681   
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");
686
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) := -
689   
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.);
692
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];  
696     
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); 
700   
701   ###########################################  ai hitch position  ############################################
702   
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);
705   
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) {
711         # get coordinates
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);
716         
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();
720
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();        
724
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
733               if ( mpOpen ) {   
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 
736               }  # end: open
737               props.globals.getNode("sim/hitches/aerotow/mp_oldOpen").setBoolValue(mpOpen);
738             }  # end: state has changed
739           }  # end: node is available
740         }  #end : JSBSim
741   
742         var aiPosition = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt );
743   
744         var alpha_deg = ai_roll_deg * (1.);
745         var beta_deg  = ai_pitch_deg * (-1.);    
746
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.);
749
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];  
753
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); 
757
758         ###########################################  distance between hitches  #####################################
759     
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();
763
764         var aiHitchpitchto = -math.asin((myHitch_pos.alt()-aiHitch_pos.alt())/distance) / 0.01745;
765         #print("  pitch: ", aiHitchpitchto);
766   
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() );
772     
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);
776
777         # update length of rope
778         setprop("sim/hitches/aerotow/tow/dist", distance);
779         #print("distance=",distance);
780
781  
782         #############################################  calc forces  ##################################################
783
784         # calc forces only for JSBSim-aircrafts
785   
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 
788
789         if ( getprop("sim/flight-model") == "jsb" ) { 
790           #print("Force-Routine");
791
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!      
801  
802           var breakforce_N = getprop("sim/hitches/aerotow/tow/brake-force");  # could be different in both aircrafts
803
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");
809
810             var delta_towlength_m = distance - towlength_m;
811             #print("towlength_m= ", towlength_m , "  elastic_constant= ", elastic_constant,"  delta_towlength_m= ", delta_towlength_m);
812
813             if ( delta_towlength_m < 0. ) {
814               var forcetow_N = 0.;
815             }
816             else{
817               var forcetow_N = elastic_constant * delta_towlength_m / towlength_m;
818             }
819           }  # end !isSlave
820           else {   # we are slave and get the forces from master
821             #print("slave: get forces","    aimember=",aimember.getName());
822             # get forces
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 ); 
827           }  # end isSlave
828   
829           var forcetow_LBS = forcetow_N * 0.224809;   # N -> LBF     
830           #print(" forcetow_N ", forcetow_N , "  distance ", distance,"  ", breakforce_N);
831
832           if ( forcetow_N < breakforce_N ) {
833       
834             var distancepr = (myHitch_pos.distance_to(aiHitch_pos)); 
835       
836             # correct a failure, if the projected length is larger than direct length
837             if (distancepr > distance) { distancepr = distance;} 
838            
839             var alpha = math.acos( (distancepr / distance) );
840             if ( aiHitch_pos.alt() > myHitch_pos.alt()) alpha = - alpha;
841
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;
845       
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);
854       
855             #var forcetow = forcetow_N;   # we deliver N to JSBSim
856             var forcetow = forcetow_LBS;   # we deliver LBS to JSBSim
857       
858             # calculate unit vector of force direction in JSBSim-system      
859             var force = 1;
860  
861             # global forces: alpha beta
862             var fglobalx = force * cosa * cosb;
863             var fglobaly = force * cosa * sinb;
864             var fglobalz = force * sina;
865                         
866             # local forces by pitch: gamma
867             var flpitchx = fglobalx * cosg - fglobalz * sing;
868             var flpitchy = fglobaly;
869             var flpitchz = fglobalx * sing + fglobalz * cosg;
870             
871             # local forces by roll: delta
872             var flrollx  =   flpitchx;
873             var flrolly  =   flpitchy * cosd + flpitchz * sind;
874             var flrollz  = - flpitchy * sind + flpitchz * cosd;
875       
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);
881       
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);
889
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 
895           }
896      
897           #############################################  report forces  ##############################################
898
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");
903
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];
909
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];
915
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 );
921       
922             var forcetowX_N = forcetow_N * dx / dl;
923             var forcetowY_N = forcetow_N * dy / dl;
924             var forcetowZ_N = forcetow_N * dz / dl;
925       
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
930     
931           } # end report forces
932     
933           }  # end: timelag
934           else{
935             #print("forces NOT updated!");
936           }
937         }  # end forces/JSBSim
938
939
940       }  # end: aiNodeID
941       else{  
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!" )); 
946         }
947       }
948     }  # end: check id != nil
949   }  # end: loop over aiobjects
950
951
952 }   # end function aerotow
953       
954
955
956 # ######################################################################################################################
957 #                                                         winch function 
958 # ######################################################################################################################
959
960 var winch = func (open){
961  
962   var FT2M = 0.30480;
963   var M2FT = 1. / FT2M;
964   var RAD2DEG = 57.29578;
965   var DEG2RAD = 1. / RAD2DEG;
966      
967   if (!open ) {
968   
969   ###########################################  my hitch position  ############################################
970   
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");
975   
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");
980
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) := -
983   
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.);
986
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];  
990     
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); 
994   
995   ###########################################  winch hitch position  ############################################
996
997   # get coordinates  
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");
1001
1002   var winch_geod = carttogeod(winch_global_pos_x,winch_global_pos_y,winch_global_pos_z);
1003
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);
1009
1010   var aiHitch_pos = geo.Coord.set_latlon( ai_lat, ai_lon, ai_alt );
1011   
1012
1013   ###########################################  distance between hitches  #####################################
1014     
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();
1018
1019   var aiHitchpitchto = -math.asin((myHitch_pos.alt()-aiHitch_pos.alt())/distance) / 0.01745;
1020   #print("  pitch: ", aiHitchpitchto);
1021   
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() );
1027     
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);
1031
1032   # update length of rope
1033   setprop("sim/hitches/winch/tow/dist", distance);
1034   #print("distance=",distance);
1035
1036  
1037   #############################################  calc forces  ##################################################
1038
1039   # calc forces only for JSBSim-aircrafts
1040   
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 
1043
1044   if ( getprop("sim/flight-model") == "jsb"  ) {
1045    
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"); 
1057
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);
1061
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;
1066     }
1067     else {   # un-clutched 
1068       # --- experimental --- #
1069
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;
1072       
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 ;
1076       
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");
1079
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);
1086     }
1087
1088     if ( delta_towlength_m < 0. ) {
1089       var forcetow_N = 0.;
1090     }
1091     else{
1092       var forcetow_N = elastic_constant * delta_towlength_m / towlength_new_m;
1093     }
1094     
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;
1099     }  
1100     
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;
1106     }
1107     #print("power=",power,"  spoolspeed=",spoolspeed,"  force=",forcetow_N);
1108     
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);
1112     
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.; 
1116     
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);
1121
1122     if ( forcetow_N < breakforce_N ) {
1123       
1124       var distancepr = (myHitch_pos.distance_to(aiHitch_pos)); 
1125       
1126       # correct a failure, if the projected length is larger than direct length
1127       if (distancepr > distance) { distancepr = distance;} 
1128            
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;
1134            
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);
1143       
1144       #var forcetow = forcetow_N;   # we deliver N to JSBSim
1145       var forcetow = forcetow_LBS;   # we deliver LBS to JSBSim
1146
1147       # calculate unit vector of force direction in JSBSim-system      
1148       var force = 1;
1149
1150       # global forces: alpha beta
1151       var fglobalx = force * cosa * cosb;
1152       var fglobaly = force * cosa * sinb;
1153       var fglobalz = force * sina;
1154
1155       # local forces by pitch: gamma
1156       var flpitchx = fglobalx * cosg - fglobalz * sing;
1157       var flpitchy = fglobaly;
1158       var flpitchz = fglobalx * sing + fglobalz * cosg;
1159             
1160       # local forces by roll: delta
1161       var flrollx  =   flpitchx;
1162       var flrolly  =   flpitchy * cosd + flpitchz * sind;
1163       var flrollz  = - flpitchy * sind + flpitchz * cosd;
1164
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);
1170             
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 );
1178
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();
1183
1184     }  # end force < break force
1185     else {  # rope is broken
1186       props.globals.getNode("sim/hitches/winch/broken").setBoolValue(1);
1187       releaseWinch(); 
1188     }
1189
1190     if ( towlength_new_m > max_tow_length_m ) {
1191       setprop("sim/messages/atc", sprintf("tow length exceeded!"));
1192       releaseWinch(); 
1193     }
1194
1195   }  # end forces/JSBSim
1196
1197   }  # end hitch is closed (open == 0)
1198
1199 }  # end function winch
1200
1201
1202 # ######################################################################################################################
1203 #                                                      create towrope
1204 # ######################################################################################################################
1205
1206 var createTowrope = func (device){
1207
1208   # create the towrope in the model property tree
1209   #print("createTowrope for ",device);
1210
1211   if ( getprop("sim/hitches/" ~ device ~ "/rope/exist") == 0 ) {   # does the towrope exist?
1212
1213     # get the next free model id
1214     var freeModelid = getFreeModelID();
1215
1216     props.globals.getNode("sim/hitches/" ~ device ~ "/rope/model_id").setIntValue(freeModelid);
1217     props.globals.getNode("sim/hitches/" ~ device ~ "/rope/exist").setBoolValue(1);
1218     
1219     var towrope_ai  = props.globals.getNode("ai/models/" ~ device ~ "rope", 1);
1220     var towrope_mod  = props.globals.getNode("models", 1);
1221         
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.);
1231
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
1242 }
1243
1244
1245 # ######################################################################################################################
1246 #                                     get the next free id of "models/model" members        
1247 # ######################################################################################################################
1248
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) { 
1255       modelid = c + 1;
1256     }
1257   }
1258   #print("modelid=",modelid);    
1259   return(modelid);
1260 }
1261
1262
1263 # ######################################################################################################################
1264 #                                                       close hitch       
1265 # ######################################################################################################################
1266
1267 var closeHitch = func {
1268   
1269   #print("closeHitch");
1270
1271   setprop("sim/hitches/aerotow/open", "false");
1272   setprop("sim/hitches/aerotow/mp_oldOpen", "true");
1273   
1274 } # End function closeHitch
1275
1276
1277 # ######################################################################################################################
1278 #                                                     release hitch       
1279 # ######################################################################################################################
1280
1281 var releaseHitch = func (device){
1282   
1283   #print("releaseHitch");
1284   
1285   if ( getprop("sim/flight-model") == "yasim" ) return; # bypass this routine for Yasim-aircrafts
1286
1287   setprop("sim/hitches/" ~ device ~ "/open", "true");
1288   
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.);
1294
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.);          # 
1299   }
1300   
1301 } # End function releaseHitch
1302
1303
1304 # ######################################################################################################################
1305 #                                                  remove/delete towrope       
1306 # ######################################################################################################################
1307
1308 var removeTowrope = func (device){
1309   
1310   # remove the towrope from the property tree ai/models
1311   # remove the towrope from the property tree models/
1312      
1313   if ( getprop("sim/hitches/" ~ device ~ "/rope/exist") == 1 ) {   # does the towrope exist?
1314
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);
1323   }
1324   
1325 }
1326
1327
1328 # ######################################################################################################################
1329 #                                           pull in towrope after hitch has been opened       
1330 # ######################################################################################################################
1331
1332 var pull_in_rope = func {
1333
1334   var deg2rad = math.pi / 180.;   
1335   var FT2M = 0.30480;
1336
1337   if ( getprop("sim/hitches/winch/open") ) {
1338
1339     # get length of rope
1340     #var distance = getprop("sim/hitches/winch/tow/dist");
1341
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"); 
1345
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");
1349   
1350     if ( towlength_new_m > towlength_min_m ) {
1351       #print("actual towlength= ",towlength_new_m);
1352
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;
1360
1361       var aiTow_pos = geo.Coord.set_latlon( tow_lat, tow_lon, tow_alt_m ); 
1362                
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);
1369    
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);
1374
1375       # update length of rope   
1376       setprop("sim/hitches/winch/tow/length",towlength_new_m);
1377     
1378       settimer( pull_in_rope , 0 );
1379     }  # end towlength > min
1380     else {
1381       #print("pull in finished!");
1382       setprop("sim/hitches/winch/winch/actual-spool-speed-m-s", 0. );
1383       removeTowrope("winch");   # remove towrope model
1384     }
1385   
1386   }  # end if open
1387   
1388 }
1389
1390
1391 # ######################################################################################################################
1392 #                                              set some AI-object default values
1393 # ######################################################################################################################
1394
1395 var setAIObjectDefaults = func (){
1396
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
1399
1400   var aiNodeID = getprop("sim/hitches/aerotow/tow/connected-to-ai-or-mp-id");   # id of former found ai/mp aircraft
1401
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.);             
1414       }
1415     } 
1416   }
1417   
1418 }
1419
1420
1421 # ######################################################################################################################
1422 #                                                  place winch model
1423 # ######################################################################################################################
1424
1425 var setWinchPositionAuto = func {
1426   
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");
1433   }  
1434
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
1438    
1439   # setup winch
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. ); 
1451   }
1452   else {
1453     var w = ipos_geo.apply_course_distance( ipos_hd_deg , initial_length_m - 1. ); 
1454   }
1455   var wpalt = geo.elevation(w.lat(), w.lon()); 
1456   w.set_alt(wpalt);
1457
1458   var winchModel = geo.put_model("Models/Airport/supacat_winch.xml", w.lat(), w.lon(), (w.alt()+0.81), (w.course_to(ac_pos) ));
1459             
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());
1463
1464   setprop("sim/hitches/winch/tow/dist",initial_length_m - 1.);
1465   setprop("sim/hitches/winch/tow/length",initial_length_m);
1466  
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!"));
1471   
1472   props.globals.getNode("sim/hitches/winch/open").setBoolValue(0);     
1473
1474 } # End function setWinchPositionAuto
1475
1476
1477 # ######################################################################################################################
1478 #                                                  clutch / un-clutch winch
1479 # ######################################################################################################################
1480
1481 var runWinch = func {
1482
1483   if ( !getprop("sim/hitches/winch/winch/clutched") ) {  
1484     setprop("sim/hitches/winch/winch/clutched","true");
1485     setprop("sim/messages/pilot", sprintf("Winch clutched!"));
1486   } 
1487   else { 
1488     setprop("sim/hitches/winch/winch/clutched","false");
1489     setprop("sim/messages/pilot", sprintf("Winch un-clutched!"));
1490   }
1491  
1492 } # End function runWinch
1493
1494
1495 # ######################################################################################################################
1496 #                                                     release winch
1497 # ######################################################################################################################
1498
1499 var releaseWinch = func {
1500   
1501   setprop("sim/hitches/winch/open","true");
1502  
1503 } # End function releaseWinch
1504
1505
1506 # ######################################################################################################################
1507 #                                                      point transformation  
1508 # ######################################################################################################################
1509
1510 var PointRotate3D = func (x,y,z,xr,yr,zr,alpha_deg,beta_deg,gamma_deg){
1511
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   # ---------------------------------------------------------------------------------
1520   #
1521   #
1522   # Definitions:
1523   # ---------------- 
1524   #
1525   # x        y           z     
1526   # alpha    beta        gamma   
1527   #                       
1528   #                        
1529   #       z                
1530   #       |  y             
1531   #       | /            
1532   #       |/             
1533   #       ----->x                
1534   #                      
1535   #----------------------------------------------------------------------------------  
1536
1537   # Transformation in rotation-system X_rel = X-Xr = (x-xr, y-yr, z-zr)
1538   var x_rel = x-xr;
1539   var y_rel = y-yr;  
1540   var z_rel = z-zr;  
1541
1542   # Trigonometry
1543   var deg2rad = math.pi / 180.;   
1544
1545   var alpha_rad    = deg2rad * alpha_deg;
1546   var beta_rad     = deg2rad * beta_deg;
1547   var gamma_rad    = deg2rad * gamma_deg;
1548
1549   var sin_alpha = math.sin(alpha_rad);
1550   var cos_alpha = math.cos(alpha_rad);
1551
1552   var sin_beta  = math.sin(beta_rad);
1553   var cos_beta  = math.cos(beta_rad);
1554
1555   var sin_gamma = math.sin(gamma_rad);
1556   var cos_gamma = math.cos(gamma_rad);
1557
1558   # Matrices 
1559   #
1560   # Rotate about x-axis Rx(alpha) 
1561   #
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)
1565   #
1566   var Rx11 = 1.;
1567   var Rx12 = 0.;
1568   var Rx13 = 0.;
1569   var Rx21 = 0.;
1570   var Rx22 = cos_alpha;
1571   var Rx23 = - sin_alpha;
1572   var Rx31 = 0.;
1573   var Rx32 = sin_alpha;
1574   var Rx33 = cos_alpha;
1575   #
1576   # Rotate about y-axis Ry(beta) 
1577   #
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)
1581   #
1582   var Ry11 = cos_beta;
1583   var Ry12 = 0.;
1584   var Ry13 = sin_beta;
1585   var Ry21 = 0.;
1586   var Ry22 = 1.;
1587   var Ry23 = 0.;
1588   var Ry31 = - sin_beta;
1589   var Ry32 = 0.;
1590   var Ry33 = cos_beta;
1591   #
1592   # Rotate about z-axis Rz(gamma) 
1593   #
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
1597   #
1598   var Rz11 = cos_gamma;
1599   var Rz12 = - sin_gamma;
1600   var Rz13 = 0.;
1601   var Rz21 = sin_gamma;
1602   var Rz22 = cos_gamma;
1603   var Rz23 = 0.;
1604   var Rz31 = 0.;
1605   var Rz32 = 0.;
1606   var Rz33 = 1.; 
1607   #
1608   # First rotation about x-axis
1609   # X_x = Rx*X_rel
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;  
1613   #
1614   # subsequent rotation about y-axis
1615   # X_xy = Ry*X_x
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; 
1619   #
1620   # subsequent rotation about z-axis:
1621   # X_xyz = Rz*X_xy
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;
1625   
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;
1630   
1631   var Xn = [xn,yn,zn];
1632   
1633   return Xn;
1634   
1635 }  
1636   
1637 ##################################################################################################################################
1638
1639
1640 # todo:
1641 # ------
1642 #
1643 # - animate rope slack
1644 # - pull in towrope: take sink rate of rope into account
1645 # - dynamic ID for ai-rope-model
1646 #
1647 # Please contact D-NXKT at yahoo.de for bug-reports, suggestions, ...
1648