Support for creating high altitude noctilucent clouds in Advanced Weather/ALS
[fg:toms-fgdata.git] / Nasal / local_weather / weather_tile_management.nas
1 ########################################################
2 # routines to set up, transform and manage weather tiles
3 # Thorsten Renk, March 2011
4 ########################################################
5
6 # function                      purpose
7 #
8 # tile_management_loop          to decide if a tile is created, removed or considered current
9 # generate_tile                 to decide on orientation and type and set up all information for tile creation
10 # remove_tile                   to delete a tile by index
11 # change_active_tile            to change the tile the aircraft is currently in and to generate neighbour info
12 # copy_entry                    to copy tile information from one node to another
13 # create_neighbour              to set up information for a new neighbouring tile
14 # create_neighbours             to initialize the 8 neighbours of the initial tile
15 # buffer_loop                   to manage the buffering of faraway clouds in an array
16 # housekeeping_loop             to shift clouds from the scenery into the buffer
17 # watchdog loop                 (debug helping structure)
18 # calc_geo                      to get local Cartesian geometry for latitude conversion
19 # get_lat                       to get latitude from Cartesian coordinates
20 # get_lon                       to get longitude from Cartesian coordinates
21 # relangle                      to compute the relative angle between two directions, normalized to [0:180]
22 # norm_relangle                 to compute the relative angle between two directions, normalized to [0:360]
23 # delete_from_vector            to delete an element 'n' from a vector
24
25 # object                        purpose
26 #
27 # cloud                         to provide the data hash for the new cloud rendering system
28 # cloudBuffer                   to store a cloud in a Nasal buffer, to provide methods to move it
29 # cloudScenery                  to store info for clouds in scenery, to provide methods to move and evolve them
30
31
32 ###################################
33 # tile management loop
34 ###################################
35
36 var tile_management_loop = func {
37
38
39 if (local_weather.local_weather_running_flag == 0) {return;}
40
41 var tNode = props.globals.getNode(lw~"tiles", 1).getChildren("tile");
42 var viewpos = geo.aircraft_position(); # using viewpos here triggers massive tile ops for tower view...
43 var code = getprop(lw~"tiles/tile[4]/code");
44 var i = 0;
45 var d_min = 100000.0;
46 var i_min = 0;
47 var current_visibility = local_weather.interpolated_conditions.visibility_m;
48 var current_heading = getprop("orientation/heading-deg");
49 var loading_flag = getprop(lw~"tmp/asymmetric-tile-loading-flag");
50 var this_frame_action_flag = 0; # use this flag to avoid overlapping tile operations
51
52 setsize(active_tile_list,0);
53 #append(active_tile_list,0); # tile zero formally containing static objects is always active 
54
55 var distance_to_load = current_visibility + 10000.0;
56
57 if (distance_to_load > 65000.0) {distance_to_load = 65000.0;}
58 if (distance_to_load < 29000.0) {distance_to_load = 29000.0;}
59
60
61
62
63 var distance_to_remove = distance_to_load + 20000.0;
64 if (distance_to_remove > 65500.0) {distance_to_remove = 65500.0;}
65
66
67 # check here if we have a new weather station if METAR is running
68
69 if ((local_weather.metar_flag == 1) and (getprop(lw~"METAR/station-id") != getprop("/environment/metar/station-id"))) 
70         {
71         weather_tiles.set_METAR_weather_station();
72         }
73
74 # compute the averaged framerate and see if cloud visibility needs to be adjusted
75
76 if (local_weather.fps_control_flag == 1) 
77         {
78         local_weather.fps_average = local_weather.fps_sum/local_weather.fps_samples;
79
80         # print("Average framerate: ", local_weather.fps_average);
81         
82         local_weather.fps_sum = 0.0;
83         local_weather.fps_samples = 0;
84
85         if (local_weather.fps_average > 1.1 * local_weather.target_framerate)
86                 {
87                 var target_cloud_view_distance = cloud_view_distance * 1.1;
88                 if (target_cloud_view_distance > 45000.0)
89                         {target_cloud_view_distance = 45000.0;} 
90                 setprop(lw~"config/clouds-visible-range-m", target_cloud_view_distance);        
91                 }
92         if (local_weather.fps_average < 0.9 * local_weather.target_framerate)
93                 {
94                 var target_cloud_view_distance = cloud_view_distance * 0.9;
95                 if (target_cloud_view_distance < 15000.0)
96                         {target_cloud_view_distance = 15000.0;} 
97                 setprop(lw~"config/clouds-visible-range-m", target_cloud_view_distance);        
98                 }
99
100         }
101
102
103
104
105 foreach (var t; tNode) {
106
107         var tpos = geo.Coord.new();
108         tpos.set_latlon(t.getNode("latitude-deg").getValue(),t.getNode("longitude-deg").getValue(),0.0);
109         var d = viewpos.distance_to(tpos);
110         if (d < d_min) {d_min = d; i_min = i;}
111         var flag = t.getNode("generated-flag").getValue();
112         
113         if ((flag ==2) or (flag ==1)) {append(active_tile_list,t.getNode("tile-index").getValue());}
114
115         var dir = viewpos.course_to(tpos);
116         var d_load = distance_to_load;
117         var d_remove = distance_to_remove;
118         if (loading_flag == 1)
119                 {
120                 var angle = abs(dir-current_heading);
121                 #if (i==7) {print(angle);}
122                 if ((angle > 135.0) and (angle < 225.0))
123                         {               
124                         d_load = 0.7 * d_load;
125                         d_remove = 0.7 * d_remove;
126                         } 
127                 }
128
129         # the tile needs to be generated, unless it already has been
130         # and if no other tile has been generated in this loop cycle
131         # and the thread and convective system are idle
132         # (we want to avoid overlapping tile generation)
133
134         if ((d < d_load) and (flag==0) and (this_frame_action_flag == 0) and (getprop(lw~"tmp/thread-status") == "idle") and (getprop(lw~"tmp/convective-status") == "idle") and (getprop(lw~"tmp/presampling-status") == "idle")) 
135                 {
136                 this_frame_action_flag = 1;
137                 setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
138                 if (local_weather.debug_output_flag == 1) 
139                         {print("Building tile unique index ",getprop(lw~"tiles/tile-counter"), " in direction ",i);}
140                 append(active_tile_list,getprop(lw~"tiles/tile-counter"));
141
142                 if (local_weather.dynamics_flag == 1) 
143                         {
144                         var quadtree = [];
145                         weather_dynamics.generate_quadtree_structure(0, quadtree);
146                         append(weather_dynamics.cloudQuadtrees,quadtree);
147                         }
148
149                 t.getNode("generated-flag").setValue(1);
150                 t.getNode("timestamp-sec").setValue(weather_dynamics.time_lw);
151                 t.getNode("tile-index",1).setValue(getprop(lw~"tiles/tile-counter"));
152                 generate_tile(code, tpos.lat(), tpos.lon(),i);
153
154                 } 
155
156         if ((d > d_remove) and (flag == 2) and (this_frame_action_flag == 0)) # the tile needs to be deleted if it exists
157                 {
158                 if (local_weather.debug_output_flag == 1) 
159                         {print("Removing tile, unique index ", t.getNode("tile-index").getValue()," direction ",i);}
160                 remove_tile(t.getNode("tile-index").getValue());
161                 t.getNode("generated-flag").setValue(0);
162                 this_frame_action_flag = 1;
163                 }
164         i = i + 1;
165         } # end foreach
166
167         #print("Minimum distance to: ",i_min);
168
169         var presampling_status = getprop(lw~"tmp/presampling-status");
170         var convective_status = getprop(lw~"tmp/convective-status");
171         var thread_status = getprop(lw~"tmp/thread-status");
172
173         if ((presampling_status == "idle") and (convective_status == "idle") and (thread_status == "idle"))
174                 {
175                 var system_status = "idle";
176                 }       
177         else
178                 {system_status = "computing";}
179         
180         #  and (this_frame_action_flag == 0) and (presampling_status == "idle") and (convective_status=="idle")) 
181
182         # check if we've entered a different tile and if no operation is in progress
183
184         # var gen_flag = tNode[i_min].getNode("generated-flag").getValue();
185         if ((i_min != 4) and (system_status == "idle"))
186                 {
187
188                 var gen_flag = tNode[i_min].getNode("generated-flag").getValue();
189                 if (gen_flag != 2){     
190                         print("Tile direction ",i_min, " not generated!");
191                         print("Flag: ",gen_flag);
192                         }
193
194                 if (local_weather.debug_output_flag == 1) 
195                         {print("Changing active tile to direction ", i_min);}
196                 change_active_tile(i_min);
197                         
198                 }    
199
200         
201
202 if (getprop(lw~"tile-loop-flag") ==1) {settimer(tile_management_loop, 4.0);}
203
204 }
205
206
207 ###################################
208 # tile generation call
209 ###################################
210
211 var generate_tile = func (code, lat, lon, dir_index) {
212
213
214 # the code should never be NIL, but this appears to happen under certain conditions
215 # so just to be on the safe side make sure it is set to current tile code if
216 # it actually is NIL
217
218 if (code == "")
219         { 
220         print("No tile code - falling back on default!");
221         code = getprop(lw~"tiles/code");
222         }
223
224 setprop(lw~"tiles/tmp/latitude-deg", lat);
225 setprop(lw~"tiles/tmp/longitude-deg",lon);
226 setprop(lw~"tiles/tmp/code",code);
227 setprop(lw~"tiles/tmp/dir-index",dir_index);
228
229
230 # do windspeed and orientation before presampling check, but test not to do it again
231
232 if (((local_weather.presampling_flag == 1) and (getprop(lw~"tmp/presampling-status") == "idle")) or (local_weather.presampling_flag == 0))
233         {
234
235         var alpha = getprop(lw~"tmp/tile-orientation-deg");
236
237
238         if ((local_weather.wind_model_flag == 2) or (local_weather.wind_model_flag ==4))
239                 {
240
241                 if (local_weather.metar_flag == 0)
242                         {
243                         alpha = alpha + 2.0 * (rand()-0.5) * 10.0;
244                         # account for the systematic spin of weather systems around a low pressure 
245                         # core dependent on hemisphere
246                         if (lat >0.0) {alpha = alpha -3.0;}
247                         else {alpha = alpha +3.0;} 
248                         }
249                 else
250                         {
251                         
252                         var step = 20.0;
253                         
254                         var alpha_test = getprop("/environment/metar/base-wind-dir-deg");
255
256
257                         if (local_weather.debug_output_flag == 1)
258                                 {print("alpha: ", alpha, " alpha_test: ", alpha_test, " relangle: ", relangle(alpha, alpha_test));}
259
260
261                         var coordinate_rotation_angle = norm_relangle(alpha, alpha_test);
262
263                 
264                         if (coordinate_rotation_angle < 45.0)
265                                 {
266                                 var system_rotation_angle = 0;
267                                 var displacement_angle = coordinate_rotation_angle;
268                                 }
269                         else if (coordinate_rotation_angle < 135.0)
270                                 {
271                                 var system_rotation_angle = 90.0;
272                                 var displacement_angle = coordinate_rotation_angle - 90.0;
273                                 }
274                         else if (coordinate_rotation_angle < 225.0)
275                                 {
276                                 var system_rotation_angle = 180.0;
277                                 var displacement_angle = coordinate_rotation_angle - 180.0;
278                                 }
279                         else if (coordinate_rotation_angle < 315.0)
280                                 {
281                                 var system_rotation_angle = 270.0;
282                                 var displacement_angle = coordinate_rotation_angle - 270.0;
283                                 }
284                         else
285                                 {
286                                 var system_rotation_angle = 0;
287                                 var displacement_angle = coordinate_rotation_angle - 360.0;
288                                 }
289
290                 
291                         if (displacement_angle < -step)
292                                 {
293                                 print("Coordinate rotation by more than ",step," deg... compensating");
294                                 displacement_angle = -step;
295                                 }
296                         else if (displacement_angle > step)
297                                 {
298                                 print("Coordinate rotation by more than ",step," deg... compensating");
299                                 displacement_angle = step;
300                                 }
301
302                         alpha = alpha + system_rotation_angle + displacement_angle;
303                                 
304
305
306                         #if (relangle(alpha, alpha_test) > step)
307                         #       {
308                         #       print("Coordinate rotation by more than ",step," deg... compensating");
309                         #       if (relangle(alpha + step, alpha_test) < relangle(alpha-step, alpha_test))
310                         #               {
311                         #               alpha = alpha + step;
312                         #               }
313                         #       else 
314                         #               {
315                         #               alpha = alpha - step;
316                         #               }
317                         #       }
318                         #else
319                         #       {
320                         #       alpha = alpha_test;
321                         #       }
322                         }
323
324         
325                         
326
327                 setprop(lw~"tmp/tile-orientation-deg",alpha);
328                 
329                 # compute the new windspeed
330
331                 var windspeed = 0;
332                 if (local_weather.metar_flag == 0)
333                         {
334                         windspeed = getprop(lw~"tmp/windspeed-kt");
335                         windspeed = windspeed + 2.0 * (rand()-0.5) * 2.0;
336                         if (windspeed < 0) {windspeed = rand();}
337                         }
338                 else
339                         {
340                         var boundary_correction = 1.0/local_weather.get_slowdown_fraction();
341                         windspeed = boundary_correction * getprop("/environment/metar/base-wind-speed-kt");
342                         }
343         
344                 setprop(lw~"tmp/windspeed-kt",windspeed);
345
346                 # store the tile orientation and wind strength in an array for fast processing
347
348                 append(weather_dynamics.tile_wind_direction, alpha);
349                 append(weather_dynamics.tile_wind_speed, windspeed);
350
351                 }
352         else if (local_weather.wind_model_flag ==5) # alpha and windspeed are calculated
353                 {
354                 var res = local_weather.wind_interpolation(lat,lon,0.0);
355                 
356                 var step = 20.0;                
357                 var alpha_test = res[0];
358
359
360                 if (local_weather.debug_output_flag == 1)
361                                 {print("alpha: ", alpha, " alpha_test: ", alpha_test, " relangle: ", relangle(alpha, alpha_test));}
362
363
364                 var coordinate_rotation_angle = norm_relangle(alpha, alpha_test);
365
366                 #print("Norm_relangle : ", norm_relangle(alpha, alpha_test));
367
368                 
369                 if (coordinate_rotation_angle < 45.0)
370                         {
371                         var system_rotation_angle = 0;
372                         var displacement_angle = coordinate_rotation_angle;
373                         }
374                 else if (coordinate_rotation_angle < 135.0)
375                         {
376                         var system_rotation_angle = 90.0;
377                         var displacement_angle = coordinate_rotation_angle - 90.0;
378                         }
379                 else if (coordinate_rotation_angle < 225.0)
380                         {
381                         var system_rotation_angle = 180.0;
382                         var displacement_angle = coordinate_rotation_angle - 180.0;
383                         }
384                 else if (coordinate_rotation_angle < 315.0)
385                         {
386                         var system_rotation_angle = 270.0;
387                         var displacement_angle = coordinate_rotation_angle - 270.0;
388                         }
389                 else
390                         {
391                         var system_rotation_angle = 0;
392                         var displacement_angle = coordinate_rotation_angle - 360.0;
393                         }
394
395                 #print("Displacement angle: ", displacement_angle);
396                 
397                 if (displacement_angle < -step)
398                         {
399                         print("Coordinate rotation by more than ",step," deg... compensating");
400                         displacement_angle = -step;
401                         }
402                 else if (displacement_angle > step)
403                         {
404                         print("Coordinate rotation by more than ",step," deg... compensating");
405                         displacement_angle = step;
406                         }
407                 
408                 #print("Normalized displacement angle: ", displacement_angle);
409                 
410                 alpha = alpha + system_rotation_angle + displacement_angle;
411                 
412                 #print("alpha_out: ", alpha);
413
414                 #if (relangle(alpha, alpha_test) > step)
415                 #       {
416                 #       print("Coordinate rotation by more than ",step," deg... compensating");
417                 #       if (relangle(alpha + step, alpha_test) < relangle(alpha-step, alpha_test))
418                 #               {
419                 #               alpha = alpha + step;
420                 #               }
421                 #       else 
422                 #               {
423                 #               alpha = alpha - step;
424                 #               }
425                 #       }
426                 #else
427                 #       {
428                 #       alpha = alpha_test;
429                 #       }
430                         
431                 #alpha = alpha_test;
432
433
434
435                 setprop(lw~"tmp/tile-orientation-deg",alpha);                           
436                 var windspeed = res[1];
437                 setprop(lw~"tmp/windspeed-kt",windspeed);
438
439                 append(weather_dynamics.tile_wind_direction,res[0]);
440                 append(weather_dynamics.tile_wind_speed,res[1]);
441
442                 }
443
444
445         props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("orientation-deg").setValue(alpha);
446         }
447
448
449
450 # now see if we need to presample the terrain
451
452 if ((local_weather.presampling_flag == 1) and (getprop(lw~"tmp/presampling-status") == "idle") and (compat_layer.features.terrain_presampling_active == 0)) 
453         {
454         local_weather.terrain_presampling_start(lat, lon, 1000, 40000, getprop(lw~"tmp/tile-orientation-deg")); 
455         return;
456         }
457 else if (compat_layer.features.terrain_presampling_active == 1)# we have hard-coded values available and use those
458         {local_weather.terrain_presampling_analysis();}
459
460
461 if (local_weather.debug_output_flag == 1) 
462         {print("Current tile type: ", code);}
463
464 if (getprop(lw~"tmp/tile-management") == "repeat tile")
465         {
466         if (code == "altocumulus_sky"){weather_tiles.set_altocumulus_tile();}
467         else if (code == "broken_layers") {weather_tiles.set_broken_layers_tile();}
468         else if (code == "stratus") {weather_tiles.set_overcast_stratus_tile();}
469         else if (code == "cumulus_sky") {weather_tiles.set_fair_weather_tile();}
470         else if (code == "gliders_sky") {weather_tiles.set_gliders_sky_tile();}
471         else if (code == "blue_thermals") {weather_tiles.set_blue_thermals_tile();}
472         else if (code == "summer_rain") {weather_tiles.set_summer_rain_tile();}
473         else if (code == "high_pressure_core") {weather_tiles.set_high_pressure_core_tile();}
474         else if (code == "high_pressure") {weather_tiles.set_high_pressure_tile();}
475         else if (code == "high_pressure_border") {weather_tiles.set_high_pressure_border_tile();}
476         else if (code == "low_pressure_border") {weather_tiles.set_low_pressure_border_tile();}
477         else if (code == "low_pressure") {weather_tiles.set_low_pressure_tile();}
478         else if (code == "low_pressure_core") {weather_tiles.set_low_pressure_core_tile();}
479         else if (code == "cold_sector") {weather_tiles.set_cold_sector_tile();}
480         else if (code == "warm_sector") {weather_tiles.set_warm_sector_tile();}
481         else if (code == "tropical_weather") {weather_tiles.set_tropical_weather_tile();}
482         else if (code == "thunderstorms") {weather_tiles.set_thunderstorms_tile();}
483         else if (code == "test") {weather_tiles.set_4_8_stratus_tile();}
484         else 
485                 {
486                 print("Repeat tile not implemented with this tile type!");
487                 setprop("/sim/messages/pilot", "Local weather: Repeat tile not implemented with this tile type!");
488                 }
489         }
490 else if (getprop(lw~"tmp/tile-management") == "realistic weather")
491         {
492         var rn = rand() * getprop(lw~"config/large-scale-persistence");
493         
494         if (code == "low_pressure_core") 
495                 {
496                 if (rn > 0.1) {weather_tiles.set_low_pressure_core_tile();}
497                 else {weather_tiles.set_low_pressure_tile();}
498                 }
499         else if (code == "low_pressure") 
500                 {
501                 if (rn > 0.1) {weather_tiles.set_low_pressure_tile();}
502                 else if (rn > 0.05) {weather_tiles.set_low_pressure_core_tile();}
503                 else {weather_tiles.set_low_pressure_border_tile();}
504                 }
505         else if (code == "low_pressure_border") 
506                 {
507                 if (rn > 0.2) {weather_tiles.set_low_pressure_border_tile();}
508                 else if (rn > 0.15) {weather_tiles.set_cold_sector_tile();}
509                 else if (rn > 0.1) {weather_tiles.set_warm_sector_tile();}
510                 else if (rn > 0.05) {weather_tiles.set_low_pressure_tile();}
511                 else {weather_tiles.set_high_pressure_border_tile();}
512                 }
513         else if (code == "high_pressure_border") 
514                 {
515                 if (rn > 0.2) {weather_tiles.set_high_pressure_border_tile();}
516                 else if (rn > 0.15) {weather_tiles.set_cold_sector_tile();}
517                 else if (rn > 0.1) {weather_tiles.set_warm_sector_tile();}
518                 else if (rn > 0.05) {weather_tiles.set_high_pressure_tile();}
519                 else {weather_tiles.set_low_pressure_border_tile();}
520                 }
521         else if (code == "high_pressure") 
522                 {
523                 if (rn > 0.1) {weather_tiles.set_high_pressure_tile();}
524                 else if (rn > 0.05) {weather_tiles.set_high_pressure_border_tile();}
525                 else {weather_tiles.set_high_pressure_core_tile();}
526                 }
527         else if (code == "high_pressure_core") 
528                 {
529                 if (rn > 0.1) {weather_tiles.set_high_pressure_core_tile();}
530                 else {weather_tiles.set_high_pressure_tile();}
531                 }
532         else if (code == "cold_sector") 
533                 {
534                 if (rn > 0.15) {weather_tiles.set_cold_sector_tile();}
535                 else if (rn > 0.1) 
536                         {
537                         if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
538                                 {weather_tiles.set_warmfront1_tile();}
539                         else if ((dir_index ==3) or (dir_index ==5))
540                                 {weather_tiles.set_cold_sector_tile();}
541                         else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
542                                 {weather_tiles.set_coldfront_tile();}
543                         }
544                 else if (rn > 0.05) {weather_tiles.set_low_pressure_border_tile();}
545                 else {weather_tiles.set_high_pressure_border_tile();}
546                 }
547         else if (code == "warm_sector") 
548                 {
549                 if (rn > 0.15) {weather_tiles.set_warm_sector_tile();}
550                 else if (rn > 0.1) 
551                         {
552                         if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
553                                 {weather_tiles.set_coldfront_tile();}
554                         else if ((dir_index ==3) or (dir_index ==5))
555                                 {weather_tiles.set_warm_sector_tile();}
556                         else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
557                                 {weather_tiles.set_warmfront4_tile();}
558                         }
559                 else if (rn > 0.05) {weather_tiles.set_low_pressure_border_tile();}
560                 else {weather_tiles.set_high_pressure_border_tile();}
561                 }
562         else if (code == "warmfront1")
563                 {
564                 if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
565                         {weather_tiles.set_warmfront2_tile();}
566                 else if ((dir_index ==3) or (dir_index ==5))
567                         {
568                         if (rand() > 0.15)
569                                 {weather_tiles.set_warmfront1_tile();}
570                         else
571                                 {weather_tiles.set_high_pressure_border_tile();}
572                         }
573                 else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
574                         {weather_tiles.set_cold_sector_tile();}
575                 }
576         else if (code == "warmfront2")
577                 {
578                 if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
579                         {weather_tiles.set_warmfront3_tile();}
580                 if ((dir_index ==3) or (dir_index ==5))
581                         {
582                         if (rand() > 0.15)                      
583                                 {weather_tiles.set_warmfront2_tile();}
584                         else
585                                 {weather_tiles.set_high_pressure_border_tile();}
586                         }
587                 if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
588                         {weather_tiles.set_warmfront1_tile();}
589                 }
590         else if (code == "warmfront3")
591                 {
592                 if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
593                         {weather_tiles.set_warmfront4_tile();}
594                 if ((dir_index ==3) or (dir_index ==5))
595                         {
596                         if (rand() > 0.15)
597                                 {weather_tiles.set_warmfront3_tile();}
598                         else
599                                 {weather_tiles.set_low_pressure_border_tile();}
600                         }
601                 if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
602                         {weather_tiles.set_warmfront2_tile();}
603                 }
604         else if (code == "warmfront4")
605                 {
606                 if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
607                         {weather_tiles.set_warm_sector_tile();}
608                 if ((dir_index ==3) or (dir_index ==5))
609                         {
610                         if (rand() > 0.15)                      
611                                 {weather_tiles.set_warmfront4_tile();}
612                         else
613                                 {weather_tiles.set_low_pressure_tile();}
614                         }
615                 if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
616                         {weather_tiles.set_warmfront3_tile();}
617                 }
618         else if (code == "coldfront")
619                 {
620                 if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
621                         {weather_tiles.set_cold_sector_tile();}
622                 else if ((dir_index ==3) or (dir_index ==5))
623                         {       
624                         if (rand() > 0.15)
625                                 {weather_tiles.set_coldfront_tile();}
626                         else
627                                 {weather_tiles.set_high_pressure_border_tile();}
628                         }
629                 else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
630                         {weather_tiles.set_warm_sector_tile();}
631                 }
632         else
633                 {
634                 print("Realistic weather not implemented with this tile type!");
635                 setprop("/sim/messages/pilot", "Local weather: Realistic weather not implemented with this tile type!");
636                 }
637
638         } # end if mode == realistic weather
639 else if (getprop(lw~"tmp/tile-management") == "METAR")
640         {
641         weather_tiles.set_METAR_tile();
642         }
643 }
644         
645
646 ###################################
647 # tile removal call
648 ###################################
649
650 var remove_tile = func (index) {
651
652 # remove tile from active list
653
654 var s = size(active_tile_list);
655
656 for (var j = 0; j < s; j=j+1)
657         {
658         if (index == active_tile_list[j])
659                 {       
660                 active_tile_list = delete_from_vector(active_tile_list,j);
661                 break;
662                 }
663         }
664
665 settimer( func { props.globals.getNode("local-weather/clouds", 1).removeChild("tile",index) },100);
666
667
668 var effectNode = props.globals.getNode("local-weather/effect-volumes").getChildren("effect-volume");
669
670 var ecount = 0;
671
672 for (var i = 0; i < local_weather.n_effectVolumeArray; i = i + 1)
673         {
674         ev = local_weather.effectVolumeArray[i];
675         if (ev.index == index)
676                 {       
677                 local_weather.effectVolumeArray = delete_from_vector(local_weather.effectVolumeArray,i);
678                 local_weather.n_effectVolumeArray = local_weather.n_effectVolumeArray - 1;
679                 i = i - 1;
680                 ecount = ecount + 1;
681                 }
682         else if (ev.index == 0) # use the opportunity to check if static effects should also be removed
683                 {
684                 if (ev.get_distance() > 80000.0)
685                         {
686                         local_weather.effectVolumeArray = delete_from_vector(local_weather.effectVolumeArray,i);
687                         local_weather.n_effectVolumeArray = local_weather.n_effectVolumeArray - 1;
688                         i = i - 1;
689                         ecount = ecount + 1;
690                         }
691                 }
692         }
693
694
695 setprop(lw~"effect-volumes/number",getprop(lw~"effect-volumes/number")- ecount);
696
697 # set placement indices to zero to reinitiate search for free positions
698
699 setprop(lw~"clouds/placement-index",0);
700 setprop(lw~"clouds/model-placement-index",0);
701 setprop(lw~"effect-volumes/effect-placement-index",0);
702
703 # remove quadtree structures 
704
705 if (local_weather.dynamics_flag ==1)
706         {
707         settimer( func {setsize(weather_dynamics.cloudQuadtrees[index-1],0);},1.0);
708         }
709
710 # rebuild effect volume vector
711
712 local_weather.assemble_effect_array(); 
713
714 if (local_weather.wxradar_support_flag == 1)
715         {
716         local_weather.remove_wxradar_echos();
717         }
718
719 }
720
721
722
723 ###################################
724 # active tile change and neighbour 
725 # recomputation
726 ###################################
727
728 var change_active_tile = func (index) {
729
730 var t = props.globals.getNode(lw~"tiles").getChild("tile",index,0);
731
732 var lat = t.getNode("latitude-deg").getValue();
733 var lon = t.getNode("longitude-deg").getValue();
734 # var alpha = getprop(lw~"tmp/tile-orientation-deg");
735
736 var alpha_old = getprop(lw~"tiles/tile[4]/orientation-deg");
737 var alpha = t.getNode("orientation-deg").getValue();
738
739 var coordinate_rotation_angle = norm_relangle(alpha_old, alpha);
740
741 if (coordinate_rotation_angle < 45.0)
742         {
743         var system_rotation_angle = 0;
744         var displacement_angle = coordinate_rotation_angle;
745         }
746 else if (coordinate_rotation_angle < 135.0)
747         {
748         var system_rotation_angle = 90.0;
749         var displacement_angle = coordinate_rotation_angle - 90.0;
750         }
751 else if (coordinate_rotation_angle < 225.0)
752         {
753         var system_rotation_angle = 180.0;
754         var displacement_angle = coordinate_rotation_angle - 180.0;
755         }
756 else if (coordinate_rotation_angle < 315.0)
757         {
758         var system_rotation_angle = 270.0;
759         var displacement_angle = coordinate_rotation_angle - 270.0;
760         }
761 else
762         {
763         var system_rotation_angle = 0;
764         var displacement_angle = coordinate_rotation_angle - 360.0;
765         }
766
767 alpha = alpha_old + displacement_angle;
768
769 if (index == 0)
770         {
771         copy_entry(4,8);
772         copy_entry(3,7);
773         copy_entry(1,5);
774         copy_entry(0,4);
775         create_neighbour(lat,lon,0,alpha);
776         create_neighbour(lat,lon,1,alpha);
777         create_neighbour(lat,lon,2,alpha);
778         create_neighbour(lat,lon,3,alpha);
779         create_neighbour(lat,lon,6,alpha);
780         }
781 else if (index == 1)
782         {
783         copy_entry(3,6);
784         copy_entry(4,7);
785         copy_entry(5,8);
786         copy_entry(0,3);
787         copy_entry(1,4); 
788         copy_entry(2,5);
789         create_neighbour(lat,lon,0,alpha);
790         create_neighbour(lat,lon,1,alpha);
791         create_neighbour(lat,lon,2,alpha);
792         }
793 else if (index == 2)
794         {
795         copy_entry(4,6);
796         copy_entry(1,3);
797         copy_entry(2,4);
798         copy_entry(5,7);
799         create_neighbour(lat,lon,0,alpha);
800         create_neighbour(lat,lon,1,alpha);
801         create_neighbour(lat,lon,2,alpha);
802         create_neighbour(lat,lon,5,alpha);
803         create_neighbour(lat,lon,8,alpha);
804         }
805 else if (index == 3)
806         {
807         copy_entry(1,2);
808         copy_entry(4,5);
809         copy_entry(7,8);
810         copy_entry(0,1);
811         copy_entry(3,4); 
812         copy_entry(6,7);
813         create_neighbour(lat,lon,0,alpha);
814         create_neighbour(lat,lon,3,alpha);
815         create_neighbour(lat,lon,6,alpha);
816         }
817 else if (index == 5)
818         {
819         copy_entry(1,0);
820         copy_entry(4,3);
821         copy_entry(7,6);
822         copy_entry(2,1);
823         copy_entry(5,4); 
824         copy_entry(8,7);
825         create_neighbour(lat,lon,2,alpha);
826         create_neighbour(lat,lon,5,alpha);
827         create_neighbour(lat,lon,8,alpha);
828         }
829 else if (index == 6)
830         {
831         copy_entry(4,2);
832         copy_entry(3,1);
833         copy_entry(6,4);
834         copy_entry(7,5);
835         create_neighbour(lat,lon,0,alpha);
836         create_neighbour(lat,lon,3,alpha);
837         create_neighbour(lat,lon,6,alpha);
838         create_neighbour(lat,lon,7,alpha);
839         create_neighbour(lat,lon,8,alpha);
840         }
841 else if (index == 7)
842         {
843         copy_entry(3,0);
844         copy_entry(4,1);
845         copy_entry(5,2);
846         copy_entry(6,3);
847         copy_entry(7,4); 
848         copy_entry(8,5);
849         create_neighbour(lat,lon,6,alpha);
850         create_neighbour(lat,lon,7,alpha);
851         create_neighbour(lat,lon,8,alpha);
852         }
853 else if (index == 8)
854         {
855         copy_entry(4,0);
856         copy_entry(7,3);
857         copy_entry(8,4);
858         copy_entry(5,1);
859         create_neighbour(lat,lon,2,alpha);
860         create_neighbour(lat,lon,5,alpha);
861         create_neighbour(lat,lon,6,alpha);
862         create_neighbour(lat,lon,7,alpha);
863         create_neighbour(lat,lon,8,alpha);
864         }
865
866
867
868
869 if (system_rotation_angle > 0.0)
870         {
871         if (local_weather.debug_output_flag == 1)
872                 {print("Rotating coordinate system by ", system_rotation_angle, " degrees");}
873         
874         # create a buffer entry for rotation, this is deleted in the rotation routine
875
876         create_neighbour(lat, lon, 9, alpha);
877         rotate_tile_scheme(system_rotation_angle);
878         }
879 }
880
881
882 ###################################
883 # rotate tile scheme  
884 ###################################
885
886 var rotate_tile_scheme = func (angle) {
887
888 if (angle < 45.0)
889         {
890         return;
891         }
892 else if (angle < 135)
893         {
894
895
896         copy_entry(2,9);
897         copy_entry(8,2);
898         copy_entry(6,8);
899         copy_entry(0,6);
900         copy_entry(9,0);
901         copy_entry(5,9);
902         copy_entry(7,5);
903         copy_entry(3,7);
904         copy_entry(1,3);
905         copy_entry(9,1);
906
907         props.globals.getNode(lw~"tiles").removeChild("tile",9);
908         }
909 else if (angle < 225)
910         {
911         copy_entry(8,9);
912         copy_entry(0,8);
913         copy_entry(9,0);
914
915         copy_entry(7,9);
916         copy_entry(1,7);
917         copy_entry(9,1);
918
919         copy_entry(6,9);
920         copy_entry(2,6);
921         copy_entry(9,2);
922
923         copy_entry(5,9);
924         copy_entry(3,5);
925         copy_entry(9,3);
926         
927         props.globals.getNode(lw~"tiles").removeChild("tile",9);
928         }
929 else if (angle < 315)
930         {
931         copy_entry(0,9);
932         copy_entry(6,0);        
933         copy_entry(8,6);
934         copy_entry(2,8);
935         copy_entry(9,2);
936         copy_entry(3,9);
937         copy_entry(7,3);
938         copy_entry(5,7);
939         copy_entry(1,5);
940         copy_entry(9,1);
941
942         props.globals.getNode(lw~"tiles").removeChild("tile",9);
943         }
944 else 
945         {
946         return;
947         }
948 }
949
950
951 #####################################
952 # copy tile info in neighbour matrix
953 #####################################
954
955 var copy_entry = func (from_index, to_index) {
956
957 var tNode = props.globals.getNode(lw~"tiles");
958
959 var f = tNode.getChild("tile",from_index,0);
960 var t = tNode.getChild("tile",to_index,0);
961
962 t.getNode("latitude-deg").setValue(f.getNode("latitude-deg").getValue());
963 t.getNode("longitude-deg").setValue(f.getNode("longitude-deg").getValue());
964 t.getNode("generated-flag").setValue(f.getNode("generated-flag").getValue());
965 t.getNode("tile-index").setValue(f.getNode("tile-index").getValue());
966 t.getNode("timestamp-sec").setValue(f.getNode("timestamp-sec").getValue());
967 t.getNode("orientation-deg").setValue(f.getNode("orientation-deg").getValue());
968 t.getNode("code").setValue(f.getNode("code").getValue());
969
970
971 }
972
973 #####################################
974 # create adjacent tile coordinates
975 #####################################
976
977 var create_neighbour = func (blat, blon, index, alpha) {
978
979 var x = 0.0;
980 var y = 0.0;
981 var phi = alpha * math.pi/180.0;
982
983 calc_geo(blat);
984
985 if ((index == 0) or (index == 3) or (index == 6)) {x =-40000.0;}
986 if ((index == 2) or (index == 5) or (index == 8)) {x = 40000.0;}
987
988 if ((index == 0) or (index == 1) or (index == 2)) {y = 40000.0;}
989 if ((index == 6) or (index == 7) or (index == 8)) {y = -40000.0;}
990
991 var t = props.globals.getNode(lw~"tiles").getChild("tile",index,1);
992
993 # use the last built tile code as default, in case a tile isn't formed when reached,
994 # the code is not empty but has a plausible value
995
996 var default_code = getprop(lw~"tiles/code");
997
998 t.getNode("latitude-deg",1).setValue(blat + get_lat(x,y,phi));
999 t.getNode("longitude-deg",1).setValue(blon + get_lon(x,y,phi));
1000 t.getNode("generated-flag",1).setValue(0);
1001 t.getNode("tile-index",1).setValue(-1);
1002 t.getNode("code",1).setValue(default_code);
1003 t.getNode("timestamp-sec",1).setValue(weather_dynamics.time_lw);
1004 t.getNode("orientation-deg",1).setValue(0.0);
1005 }
1006
1007 #####################################
1008 # find the 8 adjacent tile coordinates
1009 # after the initial setup call
1010 #####################################
1011
1012 var create_neighbours = func (blat, blon, alpha)        {
1013
1014 var x = 0.0;
1015 var y = 0.0;
1016 var phi = alpha * math.pi/180.0;
1017
1018 calc_geo(blat);
1019
1020 x = -40000.0; y = 40000.0; 
1021 setprop(lw~"tiles/tile[0]/latitude-deg",blat + get_lat(x,y,phi));
1022 setprop(lw~"tiles/tile[0]/longitude-deg",blon + get_lon(x,y,phi));
1023 setprop(lw~"tiles/tile[0]/generated-flag",0);
1024 setprop(lw~"tiles/tile[0]/tile-index",-1);
1025 setprop(lw~"tiles/tile[0]/code","");
1026 setprop(lw~"tiles/tile[0]/timestamp-sec",weather_dynamics.time_lw);
1027 setprop(lw~"tiles/tile[0]/orientation-deg",alpha);
1028
1029 x = 0.0; y = 40000.0; 
1030 setprop(lw~"tiles/tile[1]/latitude-deg",blat + get_lat(x,y,phi));
1031 setprop(lw~"tiles/tile[1]/longitude-deg",blon + get_lon(x,y,phi));
1032 setprop(lw~"tiles/tile[1]/generated-flag",0);
1033 setprop(lw~"tiles/tile[1]/tile-index",-1);
1034 setprop(lw~"tiles/tile[1]/code","");
1035 setprop(lw~"tiles/tile[1]/timestamp-sec",weather_dynamics.time_lw);
1036 setprop(lw~"tiles/tile[1]/orientation-deg",alpha);
1037
1038 x = 40000.0; y = 40000.0; 
1039 setprop(lw~"tiles/tile[2]/latitude-deg",blat + get_lat(x,y,phi));
1040 setprop(lw~"tiles/tile[2]/longitude-deg",blon + get_lon(x,y,phi));
1041 setprop(lw~"tiles/tile[2]/generated-flag",0);
1042 setprop(lw~"tiles/tile[2]/tile-index",-1);
1043 setprop(lw~"tiles/tile[2]/code","");
1044 setprop(lw~"tiles/tile[2]/timestamp-sec",weather_dynamics.time_lw);
1045 setprop(lw~"tiles/tile[2]/orientation-deg",alpha);
1046
1047 x = -40000.0; y = 0.0; 
1048 setprop(lw~"tiles/tile[3]/latitude-deg",blat + get_lat(x,y,phi));
1049 setprop(lw~"tiles/tile[3]/longitude-deg",blon + get_lon(x,y,phi));
1050 setprop(lw~"tiles/tile[3]/generated-flag",0);
1051 setprop(lw~"tiles/tile[3]/tile-index",-1);
1052 setprop(lw~"tiles/tile[3]/code","");
1053 setprop(lw~"tiles/tile[3]/timestamp-sec",weather_dynamics.time_lw);
1054 setprop(lw~"tiles/tile[3]/orientation-deg",alpha);
1055
1056 # this is the current tile
1057 x = 0.0; y = 0.0; 
1058 setprop(lw~"tiles/tile[4]/latitude-deg",blat + get_lat(x,y,phi));
1059 setprop(lw~"tiles/tile[4]/longitude-deg",blon + get_lon(x,y,phi));
1060 setprop(lw~"tiles/tile[4]/generated-flag",1);
1061 setprop(lw~"tiles/tile[4]/tile-index",1);
1062 setprop(lw~"tiles/tile[4]/code","");
1063 setprop(lw~"tiles/tile[4]/timestamp-sec",weather_dynamics.time_lw);
1064 setprop(lw~"tiles/tile[4]/orientation-deg",getprop(lw~"tmp/tile-orientation-deg"));
1065
1066
1067 x = 40000.0; y = 0.0; 
1068 setprop(lw~"tiles/tile[5]/latitude-deg",blat + get_lat(x,y,phi));
1069 setprop(lw~"tiles/tile[5]/longitude-deg",blon + get_lon(x,y,phi));
1070 setprop(lw~"tiles/tile[5]/generated-flag",0);
1071 setprop(lw~"tiles/tile[5]/tile-index",-1);
1072 setprop(lw~"tiles/tile[5]/code","");
1073 setprop(lw~"tiles/tile[5]/timestamp-sec",weather_dynamics.time_lw);
1074 setprop(lw~"tiles/tile[5]/orientation-deg",alpha);
1075
1076 x = -40000.0; y = -40000.0; 
1077 setprop(lw~"tiles/tile[6]/latitude-deg",blat + get_lat(x,y,phi));
1078 setprop(lw~"tiles/tile[6]/longitude-deg",blon + get_lon(x,y,phi));
1079 setprop(lw~"tiles/tile[6]/generated-flag",0);
1080 setprop(lw~"tiles/tile[6]/tile-index",-1);
1081 setprop(lw~"tiles/tile[6]/code","");
1082 setprop(lw~"tiles/tile[6]/timestamp-sec",weather_dynamics.time_lw);
1083 setprop(lw~"tiles/tile[6]/orientation-deg",alpha);
1084
1085 x = 0.0; y = -40000.0; 
1086 setprop(lw~"tiles/tile[7]/latitude-deg",blat + get_lat(x,y,phi));
1087 setprop(lw~"tiles/tile[7]/longitude-deg",blon + get_lon(x,y,phi));
1088 setprop(lw~"tiles/tile[7]/generated-flag",0);
1089 setprop(lw~"tiles/tile[7]/tile-index",-1);
1090 setprop(lw~"tiles/tile[7]/code","");
1091 setprop(lw~"tiles/tile[7]/timestamp-sec",weather_dynamics.time_lw);
1092 setprop(lw~"tiles/tile[7]/orientation-deg",alpha);
1093
1094 x = 40000.0; y = -40000.0; 
1095 setprop(lw~"tiles/tile[8]/latitude-deg",blat + get_lat(x,y,phi));
1096 setprop(lw~"tiles/tile[8]/longitude-deg",blon + get_lon(x,y,phi));
1097 setprop(lw~"tiles/tile[8]/generated-flag",0);
1098 setprop(lw~"tiles/tile[8]/tile-index",-1);
1099 setprop(lw~"tiles/tile[8]/code","");
1100 setprop(lw~"tiles/tile[8]/timestamp-sec",weather_dynamics.time_lw);
1101 setprop(lw~"tiles/tile[8]/orientation-deg",alpha);
1102 }
1103
1104
1105
1106
1107 ###############################
1108 # housekeeping loop
1109 ###############################
1110
1111 var housekeeping_loop = func (index, index1) {
1112
1113 if (local_weather.local_weather_running_flag == 0) {return;}
1114
1115 var n = 5;
1116 var n_max = size(cloudSceneryArray);
1117 n_cloudSceneryArray = n_max;
1118 var s = size(active_tile_list);
1119
1120 var m_max = size(cloudArray);
1121
1122 setprop(lw~"clouds/cloud-scenery-count",n_max+m_max);
1123
1124 # don't do anything as long as the array is empty
1125
1126 if ((n_max == 0) and (m_max == 0)) # nothing to do, loop over
1127         {if (getprop(lw~"housekeeping-loop-flag") ==1) {settimer( func {housekeeping_loop(index, index1)}, 0);} return;}
1128
1129 # parse the flags
1130
1131
1132 # now process the Scenery array
1133
1134 if (index > n_max-1) {index = 0;}
1135
1136 var i_max = index + n;
1137 if (i_max > n_max) {i_max = n_max;}
1138
1139 for (var i = index; i < i_max; i = i+1)
1140         {
1141         var c = cloudSceneryArray[i];
1142
1143         var flag = 0;
1144         
1145         for (var j = 0; j < s; j = j+1)
1146                 {
1147                 if (active_tile_list[j] == c.index) {flag = 1; break;}
1148                 if (c.index == 0) {flag =1; break;} # clouds in tile index 0 are special
1149                 }
1150
1151         if (flag == 0)
1152                 {
1153                 c.removeNodes();
1154                 cloudSceneryArray = delete_from_vector(cloudSceneryArray,i);
1155                 i = i -1; i_max = i_max - 1; n_max = n_max - 1;
1156                 n_cloudSceneryArray = n_cloudSceneryArray -1;
1157                 continue;
1158                 }
1159         }
1160
1161
1162 # now process the hard coded cloud array and see a tile has been removed
1163
1164 if (index1 > m_max-1) {index1 = 0;}
1165
1166 var j_max = index1 + n;
1167 if (j_max > m_max) {j_max = m_max;}
1168
1169 for (var j = index1; j < j_max; j = j+1)
1170         {
1171         var c = cloudArray[j];
1172
1173         var flag = 0;
1174         
1175         for (var k = 0; k < s; k = k+1)
1176                 {
1177                 if (active_tile_list[k] == c.index) {flag = 1; break;}
1178                 }
1179
1180         if (flag == 0)
1181                 {
1182                 c.remove();
1183                 cloudArray = delete_from_vector(cloudArray,j);
1184                 j = j -1; j_max = j_max - 1; m_max = m_max - 1;
1185                 continue;
1186                 }
1187         }
1188
1189 if (getprop(lw~"housekeeping-loop-flag") ==1) {settimer( func {housekeeping_loop(i,j)}, 0);}
1190 }
1191
1192
1193 ###############################
1194 # watchdog loop for debugging
1195 ###############################
1196
1197
1198 var watchdog_loop = func {
1199
1200 var tNode = props.globals.getNode(lw~"tiles", 1).getChildren("tile");
1201
1202 var i = 0;
1203
1204 print("====================");
1205
1206 var viewpos = geo.aircraft_position();
1207 var n_stations = size(local_weather.weatherStationArray);
1208
1209 var sum_T = 0.0;
1210 var sum_p = 0.0;
1211 var sum_D = 0.0;
1212 var sum_norm = 0.0;
1213
1214 var sum_wind = [0,0];
1215
1216 var wsize = size(local_weather.windIpointArray);
1217
1218 var alt = getprop("position/altitude-ft");      
1219
1220 for (var i = 0; i < wsize; i=i+1) {
1221         
1222
1223         var w = local_weather.windIpointArray[i];
1224
1225         var wpos = geo.Coord.new();
1226         wpos.set_latlon(w.lat,w.lon,1000.0);
1227
1228
1229
1230         var d = viewpos.distance_to(wpos);
1231         if (d <100.0) {d = 100.0;} # to prevent singularity at zero
1232
1233         sum_norm = sum_norm + (1./d);
1234
1235         var res = local_weather.wind_altitude_interpolation(alt,w);
1236         
1237         sum_wind = local_weather.add_vectors(sum_wind[0], sum_wind[1], res[0], (res[1]/d));     
1238         
1239         print(i, " dir: ", res[0], " speed: ", res[1], " d: ",d);
1240         }
1241
1242 print("dir_int: ", sum_wind[0], " speed_int: ", sum_wind[1]/sum_norm);
1243
1244 if (0==1)
1245 {
1246 for (var i = 0; i < n_stations; i=i+1) {
1247         
1248         s = local_weather.weatherStationArray[i];
1249         
1250
1251         var stpos = geo.Coord.new();
1252         stpos.set_latlon(s.lat,s.lon,0.0);
1253
1254         var d = viewpos.distance_to(stpos);
1255         if (d <100.0) {d = 100.0;} # to prevent singularity at zero
1256
1257         sum_norm = sum_norm + 1./d * s.weight;
1258
1259         sum_T = sum_T + (s.T/d) * s.weight;
1260         sum_D = sum_D + (s.D/d) * s.weight;
1261         sum_p = sum_p + (s.p/d) * s.weight;
1262         
1263         print(i, " p: ", s.p, " T: ", s.T, " D: ", s.D, " d: ",d);
1264         
1265         }
1266
1267 print("p_int: ", sum_p/sum_norm, " T_int: ", sum_T/sum_norm, " D_int: ", sum_D/sum_norm);
1268 }
1269
1270 if (0==1)
1271 {
1272
1273 foreach(t; tNode)
1274         {
1275         var code = t.getNode("code").getValue();
1276         var index = t.getNode("tile-index").getValue();
1277         var flag = t.getNode("generated-flag").getValue();
1278         var alpha = t.getNode("orientation-deg").getValue();
1279
1280         print(i,": code: ", code, " unique id: ", index, " flag: ", flag, " alpha: ",alpha); 
1281         
1282         i = i + 1;
1283         }
1284 print("alpha: ",getprop(lw~"tmp/tile-orientation-deg"));
1285
1286 var lat = getprop("/position/latitude-deg");
1287 var lon = getprop("/position/longitude-deg");
1288
1289 var res = local_weather.wind_interpolation(lat,lon,0.0);
1290
1291 print("Wind: ", res[0], " tile alpha: ", getprop(lw~"tiles/tile[4]/orientation-deg"));
1292 print("Mismatch: ", relangle(res[0], getprop(lw~"tiles/tile[4]/orientation-deg")));
1293 }
1294
1295
1296 print("====================");
1297
1298 settimer(watchdog_loop, 10.0);
1299 }
1300
1301
1302
1303 ###################
1304 # global variables
1305 ###################
1306
1307 # these already exist in different namespace, but for ease of typing we define them here as well
1308
1309 var lat_to_m = 110952.0; # latitude degrees to meters
1310 var m_to_lat = 9.01290648208234e-06; # meters to latitude degrees
1311 var ft_to_m = 0.30480;
1312 var m_to_ft = 1.0/ft_to_m;
1313 var inhg_to_hp = 33.76389;
1314 var hp_to_inhg = 1.0/inhg_to_hp;
1315 var lon_to_m = 0.0; #local_weather.lon_to_m;
1316 var m_to_lon = 0.0; # local_weather.m_to_lon;
1317 var lw = "/local-weather/";
1318
1319 var cloud_view_distance = getprop(lw~"config/clouds-visible-range-m");
1320
1321 var modelArrays = [];
1322 var active_tile_list = [];
1323
1324
1325 #####################################################
1326 # hashes to manage clouds in scenery or in the buffer
1327 #####################################################
1328
1329 var cloudBufferArray = [];
1330
1331
1332 var cloudBuffer = {
1333         new: func(lat, lon, alt, path, orientation, index, type) {
1334                 var c = { parents: [cloudBuffer] };
1335                 c.lat = lat;
1336                 c.lon = lon;
1337                 c.alt = alt;
1338                 c.path = path;
1339                 c.orientation = orientation;
1340                 c.index = index;
1341                 c.type = type;
1342                 return c;
1343         },
1344         get_distance: func {
1345                 var pos = geo.aircraft_position();
1346                 var cpos = geo.Coord.new();
1347                 cpos.set_latlon(me.lat,me.lon,0.0);
1348                 return pos.distance_to(cpos);
1349         },
1350         get_course: func {
1351                 var pos = geo.aircraft_position();
1352                 var cpos = geo.Coord.new();
1353                 cpos.set_latlon(me.lat,me.lon,0.0);
1354                 return pos.course_to(cpos);
1355         },
1356         show: func {
1357                 print("lat: ",me.lat," lon: ",me.lon," alt: ",me.alt);
1358                 print("path: ",me.path);
1359         
1360         },
1361         move: func {
1362                 var windfield = weather_dynamics.get_windfield(me.index);
1363                 var dt = weather_dynamics.time_lw - me.timestamp;
1364                 me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
1365                 me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
1366                 me.timestamp = weather_dynamics.time_lw;
1367         },
1368   
1369 };
1370
1371
1372 var cloudSceneryArray = [];
1373 var n_cloudSceneryArray = 0;
1374
1375 var cloudScenery = {
1376         new: func(index, type, cloudNode, modelNode) {
1377                 var c = { parents: [cloudScenery] };
1378                 c.index = index;
1379                 c.type = type;
1380                 c.cloudNode = cloudNode;
1381                 c.modelNode = modelNode;
1382                 c.calt = cloudNode.getNode("position/altitude-ft");
1383                 c.clat = cloudNode.getNode("position/latitude-deg");
1384                 c.clon = cloudNode.getNode("position/longitude-deg");
1385                 c.alt = c.calt.getValue();
1386                 c.lat = c.clat.getValue();
1387                 c.lon = c.clon.getValue();
1388                 return c;
1389         },
1390         removeNodes: func {
1391                 me.modelNode.remove();
1392                 me.cloudNode.remove();
1393         },
1394         to_buffer: func {
1395                 var path = me.modelNode.getNode("path").getValue();
1396                 var orientation = me.cloudNode.getNode("orientation/true-heading-deg").getValue();
1397                 var b = cloudBuffer.new(me.lat, me.lon, me.alt, path, orientation, me.index, me.type);
1398
1399                 if (local_weather.dynamics_flag == 1)
1400                         {
1401                         b.timestamp = me.timestamp;
1402
1403                         if (me.type !=0) # Cumulus clouds get some extra info
1404                                 {
1405                                 b.flt = me.flt;
1406                                 b.rel_alt = me.rel_alt;
1407                                 b.evolution_timestamp = me.evolution_timestamp;
1408                                 }
1409                         }
1410
1411                 me.removeNodes();
1412                 return b;
1413         },
1414         get_distance: func {
1415                 var pos = geo.aircraft_position();
1416                 var cpos = geo.Coord.new();
1417                 var lat = me.clat.getValue();
1418                 var lon = me.clon.getValue();
1419                 cpos.set_latlon(lat,lon,0.0);
1420                 return pos.distance_to(cpos);
1421         },
1422         get_course: func {
1423                 var pos = geo.aircraft_position();
1424                 var cpos = geo.Coord.new();
1425                 var lat = me.clat.getValue();
1426                 var lon = me.clon.getValue();
1427                 cpos.set_latlon(lat,lon,0.0);
1428                 return pos.course_to(cpos);
1429         },
1430         get_altitude: func {
1431                 return me.calt.getValue();
1432         },
1433         correct_altitude: func {        
1434                 var lat = me.lat;
1435                 var lon = me.lon;
1436                 var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1];
1437                 var elevation = compat_layer.get_elevation(lat, lon);
1438                 
1439                 if (local_weather.detailed_terrain_interaction_flag == 1)
1440                         {
1441                         var phi = local_weather.get_wind_direction(me.index) * math.pi/180.0;
1442                         var grad = local_weather.get_terrain_gradient(lat, lon, elevation, phi, 1000.0);
1443                         }
1444                 else 
1445                         {var grad = 0.0;}
1446
1447                 var alt_new = local_weather.get_convective_altitude(convective_alt, elevation, me.index, grad);
1448                 me.target_alt = alt_new + me.rel_alt;
1449         },
1450         correct_altitude_and_age: func {        
1451                 var lat = me.lat;
1452                 var lon = me.lon;
1453                 var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1];
1454                 
1455                 # get terrain elevation and landcover   
1456
1457                 var elevation = -1.0; var p_cover = 0.2;# defaults if there is no info
1458                 var info = geodinfo(lat, lon);
1459                 if (info != nil) 
1460                         {
1461                         elevation = info[0] * local_weather.m_to_ft;
1462                         if (info[1] != nil)
1463                                 {
1464                                 var landcover = info[1].names[0];
1465                                 if (contains(local_weather.landcover_map,landcover)) {p_cover = local_weather.landcover_map[landcover];}
1466                                 else {p_cover = 0.2;}
1467                                 }       
1468                         }
1469
1470                 if (local_weather.detailed_terrain_interaction_flag == 1)
1471                         {
1472                         var phi = local_weather.get_wind_direction(me.index) * math.pi/180.0;
1473                         var grad = local_weather.get_terrain_gradient(lat, lon, elevation, phi, 1000.0);
1474                         var lee_bias = local_weather.get_lee_bias(grad);
1475                         }
1476                 else 
1477                         {       
1478                         var grad = 0.0;
1479                         var lee_bias = 1.0;
1480                         }
1481
1482                 # correct the altitude
1483                 var alt_new = local_weather.get_convective_altitude(convective_alt, elevation, me.index, grad);
1484                 me.target_alt = alt_new + me.rel_alt;
1485
1486                 # correct fractional lifetime based on terrain below
1487
1488                 var current_lifetime = math.sqrt(p_cover * lee_bias)/math.sqrt(0.35) * weather_dynamics.cloud_convective_lifetime_s;
1489                 var fractional_increase = (weather_dynamics.time_lw - me.evolution_timestamp)/current_lifetime;
1490                 me.flt = me.flt + fractional_increase;
1491                 me.evolution_timestamp = weather_dynamics.time_lw;
1492         },
1493         to_target_alt: func {   
1494                 if (me.type ==0) {return;}
1495                 var alt_diff = me.target_alt - me.alt;
1496                 if (alt_diff == 0.0) {return;}
1497                 var max_vertical_movement_ft = weather_dynamics.dt_lw * weather_dynamics.cloud_max_vertical_speed_fts;
1498                 if (abs(alt_diff) < max_vertical_movement_ft)
1499                         {
1500                         me.alt = me.target_alt; 
1501                         }
1502                 else if (alt_diff < 0)
1503                         {
1504                         me.alt = me.alt -max_vertical_movement_ft;
1505                         }
1506                 else
1507                         {
1508                         me.alt = me.alt + max_vertical_movement_ft;
1509                         }
1510                 setprop(lw~"clouds/tile["~me.index~"]/cloud["~me.write_index~"]/position/altitude-ft", me.alt);
1511         },
1512         move: func {
1513                 
1514                 
1515                 var windfield = weather_dynamics.windfield;
1516                 var dt = weather_dynamics.time_lw - me.timestamp;
1517
1518                 me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
1519                 me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
1520                 
1521                 setprop(lw~"clouds/tile["~me.index~"]/cloud["~me.write_index~"]/position/latitude-deg", me.lat);
1522                 setprop(lw~"clouds/tile["~me.index~"]/cloud["~me.write_index~"]/position/longitude-deg", me.lon);
1523                 
1524                 me.timestamp = weather_dynamics.time_lw;
1525                 
1526         },
1527         show: func {
1528                 var lat = me.clat.getValue();
1529                 var lon = me.clon.getValue();
1530                 var alt = me.calt.getValue();           
1531                 
1532                 var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1];
1533                 var elevation = compat_layer.get_elevation(lat, lon);
1534                 print("lat :", lat, " lon: ", lon, " alt: ", alt);
1535                 print("path: ", me.modelNode.getNode("path").getValue());
1536                 print("elevation: ", compat_layer.get_elevation(lat, lon), " cloudbase: ", convective_alt);
1537                 if (me.type !=0) {print("relative: ", me.rel_alt, "target: ", me.target_alt);}
1538         },
1539 };
1540
1541 var cloudArray = [];
1542
1543 var cloud = {
1544         new: func(type, subtype) {
1545                 var c = { parents: [cloud] };
1546                 c.type = type;
1547                 c.subtype = subtype;            
1548                 c.tracer_flag = 0;
1549                 return c;
1550         },
1551         remove: func {
1552                 var p = props.Node.new({ "layer" : 0,
1553                          "index": me.cloud_index });
1554                 fgcommand("del-cloud", p);
1555         },
1556         move: func {    
1557                 # this doesn't move a cloud in the scenery, but updates its position in internal space  
1558                 var windfield = local_weather.windfield;
1559                 var dt = local_weather.time_lw - me.timestamp;
1560
1561                 me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
1562                 me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
1563                 me.timestamp = weather_dynamics.time_lw;
1564                 
1565         },
1566         correct_altitude: func {        
1567                 var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1];
1568                 var elevation = compat_layer.get_elevation(me.lat, me.lon);
1569                 
1570                 if (local_weather.detailed_terrain_interaction_flag == 1)
1571                         {
1572                         var phi = local_weather.get_wind_direction(me.index) * math.pi/180.0;
1573                         var grad = local_weather.get_terrain_gradient(me.lat, me.lon, elevation, phi, 1000.0);
1574                         }
1575                 else 
1576                         {var grad = 0.0;}
1577
1578                 var alt_new = local_weather.get_convective_altitude(convective_alt, elevation, me.index, grad);
1579                 var target_alt = alt_new + me.rel_alt;
1580
1581                 var p = props.Node.new({ "layer" : 0,
1582                          "index": me.cloud_index,
1583                          "lat-deg": me.lat,
1584                          "lon-deg": me.lon,
1585                          "alt-ft": target_alt
1586                          });
1587                 fgcommand("move-cloud",p);
1588
1589                 me.alt = target_alt;
1590         },
1591 };
1592
1593
1594 ###################
1595 # helper functions
1596 ###################
1597
1598 var calc_geo = func(clat) {
1599
1600 lon_to_m  = math.cos(clat*math.pi/180.0) * lat_to_m;
1601 m_to_lon = 1.0/lon_to_m;
1602 }
1603
1604 var get_lat = func (x,y,phi) {
1605
1606 return (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat;
1607 }
1608
1609 var get_lon = func (x,y,phi) {
1610
1611 return (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon;
1612 }
1613
1614
1615 var relangle = func (alpha, beta) {
1616
1617 var angdiff = abs (alpha - beta);
1618
1619 if ((360.0 - angdiff) < angdiff)
1620         {angdiff = 360.0 - angdiff};
1621
1622 return angdiff;
1623 }
1624
1625
1626 var norm_relangle = func (alpha, beta) {
1627
1628 var angdiff = beta - alpha;
1629
1630 while (angdiff < 0.0) 
1631         {angdiff = angdiff + 360.0;}
1632
1633 while (angdiff > 360.0)
1634         {angdiff = angdiff - 360.0;}
1635
1636 if (angdiff == 360.0) 
1637         {angdiff = 0.0;}
1638
1639 return angdiff;
1640 }
1641
1642
1643 var delete_from_vector = func(vec, index) {
1644
1645 var n = index+1;
1646
1647 var vec_end = subvec(vec, n);
1648
1649 setsize(vec, n-1);
1650 return vec~vec_end;
1651         
1652 }