Support for creating high altitude noctilucent clouds in Advanced Weather/ALS
[fg:toms-fgdata.git] / Nasal / local_weather / compat_layer.nas
1
2 ########################################################
3 # compatibility layer for local weather package
4 # Thorsten Renk, March 2011
5 ########################################################
6
7 # function                      purpose
8 #
9 # setDefaultCloudsOff           to remove the standard Flightgear 3d clouds
10 # setVisibility                 to set the visibility to a given value
11 # setLift                       to set lift to given value
12 # setRain                       to set rain to a given value
13 # setSnow                       to set snow to a given value
14 # setTurbulence                 to set turbulence to a given value
15 # setTemperature                to set temperature to a given value
16 # setPressure                   to set pressure to a given value
17 # setDewpoint                   to set the dewpoint to a given value
18 # setLight                      to set light saturation to given value
19 # setWind                       to set wind
20 # setWindSmoothly               to set the wind gradually across a second
21 # smooth_wind_loop              (helper function for setWindSmoothly)
22 # create_cloud                  to place a single cloud into the scenery
23 # create_cloud_array            to place clouds from storage arrays into the scenery
24 # get_elevation                 to get the terrain elevation at given coordinates
25 # get_elevation_vector          to get terrain elevation at given coordinate vector
26 # set_wxradarecho_storm         to provide info about a storm to the wxradar
27
28
29 # This file contains portability wrappers for the local weather system: 
30 #   http://wiki.flightgear.org/index.php/A_local_weather_system
31 #   
32 # This module is intended to provide a certain degree of backward compatibility for past 
33 # FlightGear releases, while sketching out the low level APIs used and required by the 
34 # local weather system, as these
35 # are being added to FlightGear.
36 #
37 # This file contains various workarounds for doing things that are currently not yet directly 
38 # supported by the core FlightGear/Nasal APIs (fgfs 2.0).
39 #
40 # Some of these workarounds are purely implemented in Nasal space, and may thus not provide sufficient
41 # performance in some situations.
42 #
43 # The goal is to move all such workarounds eventually  into this module, so that the high level weather modules
44 # only refer to this "compatibility layer" (using an "ideal API"), while this module handles 
45 # implementation details 
46 # and differences among different versions of FlightGear, so that key APIs can be ported to C++ space 
47 # for the sake
48 # of improving runtime performance and efficiency.
49 #
50 # This provides an abstraction layer that isolates the rest of the local weather system from low 
51 # level implementation details.
52
53 # C++ developers who want to help improve the local weather system (or the FlightGear/Nasal 
54 # interface in general) should 
55 # check out this file (as well as the wiki page) for APIs or features that shall eventually be 
56 # re/implemented in C++ space for
57 # improving the local weather system.
58 #
59
60 # This module provides a handful of helpers for dynamically querying the Nasal API of the running fgfs binary,
61 # so that it can make use of new APIs (where available), while still working with older fgfs versions.
62 #
63 # Note: The point of these helpers is that they should really only be used 
64 # by this module, and not in other parts/files of the 
65 # local weather system. Any hard coded special cases should be moved into this module.
66 #
67 # The compatibility layer is currently work in progress and will be extended as new Nasal 
68 # APIs are being added to FlightGear.
69
70 var weather_dynamics = nil;
71 var weather_tile_management = nil;
72 var compat_layer = nil;
73 var weather_tiles = nil;
74
75
76 _setlistener("/nasal/local_weather/loaded", func { 
77
78 compat_layer = local_weather;
79 weather_dynamics = local_weather;
80 weather_tile_management = local_weather;
81 weather_tiles = local_weather;
82
83
84 var result = "yes";
85
86 if (1==0) # no compatibility tests for 2.4 binary, it has the required features
87         {
88 print("Compatibility layer: testing for hard coded support");
89
90 if (props.globals.getNode("/rendering/scene/saturation", 0) == nil)
91         {result = "no"; features.can_set_light = 0;}
92 else
93         {result = "yes"; features.can_set_light = 1;}
94 print("* can set light saturation:        "~result);
95
96
97 if (props.globals.getNode("/rendering/scene/scattering", 0) == nil)
98         {result = "no"; features.can_set_scattering = 0;}
99 else
100         {result = "yes"; features.can_set_scattering = 1;}
101 print("* can set horizon scattering:      "~result);
102
103 if (props.globals.getNode("/environment/terrain", 0) == nil)
104         {result = "no"; features.terrain_presampling = 0;}
105 else
106         {result = "yes"; features.terrain_presampling = 1;setprop("/environment/terrain/area[0]/enabled",1);}
107 print("* hard coded terrain presampling:  "~result);
108
109 if ((props.globals.getNode("/environment/terrain/area[0]/enabled",1).getBoolValue() == 1) and (features.terrain_presampling ==1))
110         {result = "yes"; features.terrain_presampling_active = 1;}
111 else
112         {result = "no"; features.terrain_presampling_active = 0;}
113 print("* terrain presampling initialized: "~result);
114
115
116 if (props.globals.getNode("/environment/config/enabled", 0) == nil)
117         {result = "no"; features.can_disable_environment = 0;}
118 else
119         {result = "yes"; features.can_disable_environment = 1;}
120 print("* can disable global weather:      "~result);
121
122
123 print("Compatibility layer: tests done.");
124         }
125
126
127 # features of a 2.4 binary
128
129 # switch terrainsampler to active, should be initialized
130
131
132 features.can_set_light = 1;
133 features.can_set_scattering = 1;
134 features.terrain_presampling = 1;
135 features.terrain_presampling_active = 1;
136 features.can_disable_environment = 1;
137
138
139
140 # features of a current GIT binary
141
142 features.fast_geodinfo = 1;
143
144
145 # do actual startup()
146 local_weather.updateMenu();
147 local_weather.startup();
148
149 });
150
151
152
153
154
155 var setDefaultCloudsOff = func {
156
157 var layers = props.globals.getNode("/environment/clouds").getChildren("layer");
158         
159 foreach (var l; layers)
160         {
161         l.getNode("coverage-type").setValue(5);
162         }
163         
164
165
166 # we store that information ourselves, so this should be zero, but rain forces us to go for an offset
167 setprop("/environment/clouds/layer[0]/elevation-ft",0.0);
168                 
169 # layer wrapping off
170 setprop("/sim/rendering/clouds3d-wrap",0);
171
172 # rain altitude limit off 
173
174 props.globals.getNode("/environment/params/use-external-precipitation-level").setBoolValue("true");
175
176 setprop("/sim/rendering/minimum-sky-visibility", 0.0);
177
178 # just to be sure, set other parameters off
179
180 compat_layer.setRain(0.0);
181 compat_layer.setSnow(0.0);
182 compat_layer.setLight(1.0);
183
184 }
185
186
187 ####################################
188 # set visibility to given value
189 ####################################
190
191 var setVisibility = func (vis) {
192
193 setprop("/environment/visibility-m",vis);
194         
195 }
196
197
198 var setVisibilitySmoothly = func (vis) {
199
200
201 visibility_target = vis;
202 visibility_current = getprop("/environment/visibility-m");
203
204 if (smooth_visibility_loop_flag == 0)
205         {
206         smooth_visibility_loop_flag = 1;
207         visibility_loop();
208         }
209 }
210
211 var visibility_loop = func {
212
213 if (local_weather.local_weather_running_flag == 0) {return;}
214
215 if (visibility_target == visibility_current)
216         {smooth_visibility_loop_flag = 0; return;}
217
218 if (visibility_target < visibility_current)
219         {
220         var vis_goal = visibility_target;
221         if (vis_goal < 0.97 * visibility_current) {vis_goal = 0.97 * visibility_current;}
222         }
223 else
224         {
225         var vis_goal = visibility_target;
226         if (vis_goal > 1.03 * visibility_current) {vis_goal = 1.03 * visibility_current;}
227         }
228 #       print(vis_goal, " ",local_weather.interpolated_conditions.visibility_m );
229 if (local_weather.interpolated_conditions.visibility_m > vis_goal)
230         {setprop("/environment/visibility-m",vis_goal);}
231         visibility_current = vis_goal;  
232
233 settimer( func {visibility_loop(); },0);
234 }
235
236
237 ####################################
238 # set thermal lift to given value
239 ####################################
240
241 var setLift = func (lift) {
242
243 setprop("/environment/local-weather-lift-fps",lift);
244         
245 }
246
247 ####################################
248 # set rain to given value
249 ####################################
250
251 var setRain = func (rain) {
252
253 setprop("/environment/rain-norm", rain);
254 }
255
256 ####################################
257 # set snow to given value
258 ####################################
259
260 var setSnow = func (snow) {
261
262 setprop("/environment/snow-norm", snow);
263 }
264
265
266 ####################################
267 # set turbulence to given value
268 ####################################
269
270 var setTurbulence = func (turbulence) {
271         
272 setprop("/environment/turbulence/magnitude-norm",turbulence);
273 setprop("/environment/turbulence/rate-hz",3.0);
274 }
275
276
277 ####################################
278 # set temperature to given value
279 ####################################
280
281 var setTemperature = func (T) {
282
283 setprop("/environment/temperature-sea-level-degc",T);
284 }
285
286 ####################################
287 # set pressure to given value
288 ####################################
289
290 var setPressure = func (p) {
291
292 setprop("/environment/pressure-sea-level-inhg",p);
293 }
294
295 ####################################
296 # set dewpoint to given value
297 ####################################
298
299 var setDewpoint = func (D) {
300
301 setprop("/environment/dewpoint-sea-level-degc",D);
302 }
303
304 ####################################
305 # set light saturation to given value
306 ####################################
307
308 var setLight = func (s) {
309
310 setprop("/rendering/scene/saturation",s);
311 }
312
313 var setLightSmoothly = func (s) {
314
315 light_target = s;
316 light_current = getprop("/rendering/scene/saturation");
317
318 if (smooth_light_loop_flag == 0)
319         {
320         smooth_light_loop_flag = 1;
321         light_loop();
322         }
323 }
324
325 var light_loop = func {
326
327 if (local_weather.local_weather_running_flag == 0) {return;}
328
329 if (light_target == light_current)
330         {smooth_light_loop_flag = 0; return;}
331
332 if (light_target < light_current)
333         {
334         var light_goal = light_target;
335         if (light_goal < 0.97 * light_current) {light_goal = 0.97 * light_current;}
336         }
337 else
338         {
339         var light_goal = light_target;
340         if (light_goal > 1.03 * light_current) {light_goal = 1.03 * light_current;}
341         }
342         
343 setprop("/rendering/scene/saturation",light_goal);
344 light_current = light_goal;     
345
346 settimer( func {light_loop(); },0);
347 }
348
349
350 ####################################
351 # set horizon scattering
352 ####################################
353
354 var setScattering = func (s) {
355
356 setprop("/rendering/scene/scattering",s);
357 }
358
359 ####################################
360 # set overcast haze
361 ####################################
362
363 var setOvercast = func (o) {
364
365 setprop("/rendering/scene/overcast",o);
366 }
367
368
369 ####################################
370 # set skydome scattering parameters
371 ####################################
372
373 var setSkydomeShader = func (r, m, d) {
374
375 setprop("/sim/rendering/rayleigh", r);
376 setprop("/sim/rendering/mie", m);
377 setprop("/sim/rendering/dome-density",d);
378 }
379
380 ###########################################################
381 # set wind to given direction and speed
382 ###########################################################
383
384
385 var setWind = func (dir, speed) {
386
387 setprop("/environment/wind-from-heading-deg",dir);
388 setprop("/environment/wind-speed-kt",speed);
389         
390
391 # this is needed to trigger the cloud drift to pick up the new wind setting
392 setprop("/environment/clouds/layer[0]/elevation-ft",0.0);
393         
394 }
395
396 ###########################################################
397 # set wind smoothly to given direction and speed
398 # interpolating across several frames
399 ###########################################################
400
401
402 var setWindSmoothly = func (dir, speed) {
403
404 setWind(dir, speed);    
405 }
406
407
408 ###########################################################
409 # place a single cloud 
410 ###########################################################
411
412 var create_cloud = func(path, lat, long, alt, heading) {
413
414 var tile_counter = getprop(lw~"tiles/tile-counter");
415 var buffer_flag = getprop(lw~"config/buffer-flag");
416 var d_max = weather_tile_management.cloud_view_distance + 1000.0;
417
418 # noctilucent clouds should not be deleted with the tile, hence they're assigned to tile zero
419 if (find("noctilucent",path) != -1)
420         {tile_counter=0;}
421
422 # check if we deal with a convective cloud - no need to do this any more, convective clouds go via a different system
423
424 var convective_flag = 0;
425
426 #if (find("cumulus",path) != -1)
427 #       {
428 #       if ((find("alto",path) != -1) or (find("cirro", path) != -1) or (find("strato", path) != -1))
429 #               {convective_flag = 0;}
430 #       else if ((find("small",path) != -1) or (find("whisp",path) != -1)) 
431 #               {convective_flag = 1;}
432 #       else if (find("bottom",path) != -1) 
433 #               {convective_flag = 4;}
434 #       else    
435 #               {convective_flag = 2;}
436 #       
437 #       }
438 #else if (find("congestus",path) != -1)
439 #       {
440 #       if (find("bottom",path) != -1) 
441 #               {convective_flag = 5;}
442 #       else
443 #               {convective_flag = 3;}
444 #       } 
445
446 #print("path: ", path, " flag: ", convective_flag);
447
448 # first check if the cloud should be stored in the buffer
449 # we keep it if it is in visual range or at high altitude (where visual range is different)
450
451
452
453 # now check if we are writing from the buffer, in this case change tile index
454 # to buffered one
455
456 if (getprop(lw~"tmp/buffer-status") == "placing")
457         {
458         tile_counter = buffered_tile_index;
459         }
460
461
462
463 # if the cloud is not buffered, get property tree nodes and write it 
464 # into the scenery
465
466 var n = props.globals.getNode("local-weather/clouds", 1);
467 var c = n.getChild("tile",tile_counter,1);
468
469
470 var cloud_number = n.getNode("placement-index").getValue();
471                 for (var i = cloud_number; 1; i += 1)
472                         if (c.getChild("cloud", i, 0) == nil)
473                                 break;
474 var cl = c.getChild("cloud", i, 1);
475 n.getNode("placement-index").setValue(i);
476
477 var placement_index = i;
478
479 var model_number = n.getNode("model-placement-index").getValue();
480 var m = props.globals.getNode("models", 1);
481                 for (var i = model_number; 1; i += 1)
482                         if (m.getChild("model", i, 0) == nil)
483                                 break;
484 var model = m.getChild("model", i, 1);
485 n.getNode("model-placement-index").setValue(i); 
486
487
488
489 var latN = cl.getNode("position/latitude-deg", 1); latN.setValue(lat);
490 var lonN = cl.getNode("position/longitude-deg", 1); lonN.setValue(long);
491 var altN = cl.getNode("position/altitude-ft", 1); altN.setValue(alt);
492 var hdgN = cl.getNode("orientation/true-heading-deg", 1); hdgN.setValue(heading);
493
494 cl.getNode("tile-index",1).setValue(tile_counter);
495
496 model.getNode("path", 1).setValue(path);
497 model.getNode("latitude-deg", 1).setValue(lat);
498 model.getNode("longitude-deg", 1).setValue(long);
499 model.getNode("elevation-ft", 1).setValue(alt);
500 model.getNode("heading-deg", 1).setValue(local_weather.wind.cloudlayer[0]+180.0);
501 model.getNode("tile-index",1).setValue(tile_counter);
502 model.getNode("speed-kt",1).setValue(local_weather.wind.cloudlayer[1]);
503 model.getNode("load", 1).remove();
504
505
506 #model.getNode("latitude-deg-prop", 1).setValue(latN.getPath());
507 #model.getNode("longitude-deg-prop", 1).setValue(lonN.getPath());
508 #model.getNode("elevation-ft-prop", 1).setValue(altN.getPath());
509 #model.getNode("heading-deg-prop", 1).setValue(hdgN.getPath());
510
511 # sort the cloud into the cloud hash array
512
513 if (buffer_flag == 1)
514         {
515         var cs = weather_tile_management.cloudScenery.new(tile_counter, convective_flag, cl, model);
516         append(weather_tile_management.cloudSceneryArray,cs);
517         }
518
519 # if weather dynamics is on, also create a timestamp property and sort the cloud hash into quadtree
520
521 if (local_weather.dynamics_flag == 1)
522         {
523         cs.timestamp = weather_dynamics.time_lw;
524         cs.write_index = placement_index;
525
526
527         if (getprop(lw~"tmp/buffer-status") == "placing")
528                 {
529                 var blat = buffered_tile_latitude;
530                 var blon = buffered_tile_longitude;
531                 var alpha = buffered_tile_alpha;
532                 }
533         else
534                 {
535                 var blat = getprop(lw~"tiles/tmp/latitude-deg");
536                 var blon = getprop(lw~"tiles/tmp/longitude-deg");
537                 var alpha = getprop(lw~"tmp/tile-orientation-deg");
538                 }
539         weather_dynamics.sort_into_quadtree(blat, blon, alpha, lat, long, weather_dynamics.cloudQuadtrees[tile_counter-1], cs); 
540         }
541
542 }
543
544
545
546
547
548 ###########################################################
549 # place a  model
550 ###########################################################
551
552 var place_model = func(path, lat, lon, alt, heading, pitch, yaw) {
553
554
555
556 var m = props.globals.getNode("models", 1);
557                 for (var i = 0; 1; i += 1)
558                         if (m.getChild("model", i, 0) == nil)
559                                 break;
560 var model = m.getChild("model", i, 1);
561
562
563 model.getNode("path", 1).setValue(path);
564 model.getNode("latitude-deg", 1).setValue(lat);
565 model.getNode("longitude-deg", 1).setValue(lon);
566 model.getNode("elevation-ft", 1).setValue(alt);
567 model.getNode("heading-deg", 1).setValue(heading);
568 model.getNode("pitch-deg", 1).setValue(pitch);
569 model.getNode("roll-deg", 1).setValue(yaw);
570 model.getNode("load", 1).remove();
571
572
573 }
574
575
576
577
578 ###########################################################
579 # place a single cloud using hard-coded system
580 ###########################################################
581
582 var create_cloud_new = func(c) {
583
584
585
586 var tile_counter = getprop(lw~"tiles/tile-counter");
587 cloud_index = cloud_index + 1;
588
589 c.index = tile_counter;
590 c.cloud_index = cloud_index;
591
592 # light must be such that the top of a cloud cannot be darker than the bottom
593
594 if (c.bottom_shade > c.top_shade) {c.bottom_shade = c.top_shade;}
595 c.middle_shade = c.top_shade;
596
597 # write the actual cloud into the scenery
598
599
600 var p = props.Node.new({ "layer" : 0,
601                          "index": cloud_index,
602                          "lat-deg": c.lat,
603                          "lon-deg": c.lon,
604                          "min-sprite-width-m": c.min_width,
605                          "max-sprite-width-m": c.max_width,
606                          "min-sprite-height-m": c.min_height,
607                          "max-sprite-height-m": c.max_height,
608                          "num-sprites": c.n_sprites,
609                          "min-bottom-lighting-factor": c.bottom_shade,
610                          "min-middle-lighting-factor": c.middle_shade,
611                          "min-top-lighting-factor": c.top_shade,
612                          "min-shade-lighting-factor": c.bottom_shade,
613                          "texture": c.texture_sheet,
614                          "num-textures-x": c.num_tex_x,
615                          "num-textures-y": c.num_tex_y,
616                          "min-cloud-width-m": c.min_cloud_width,
617                          "max-cloud-width-m": c.min_cloud_width,
618                          "min-cloud-height-m": c.min_cloud_height + c.min_cloud_height * 0.2 * local_weather.height_bias,       
619                          "max-cloud-height-m": c.min_cloud_height + c.min_cloud_height * 0.2 * local_weather.height_bias,       
620                          "z-scale": c.z_scale,
621                          "height-map-texture": 0,
622                          "alt-ft" :  c.alt });
623 fgcommand("add-cloud", p);
624
625 #print("alt: ", c.alt);
626
627 # add other management properties to the hash if dynamics is on
628
629 if (local_weather.dynamics_flag == 1)
630         {
631         c.timestamp = weather_dynamics.time_lw;
632         }
633
634
635 # add cloud to array
636
637 append(weather_tile_management.cloudArray,c);
638         
639
640 }
641
642
643
644
645
646
647
648 ###########################################################
649 # place a cloud layer from arrays, split across frames 
650 ###########################################################
651
652 var create_cloud_array = func (i, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation) {
653
654 if (getprop(lw~"tmp/thread-status") != "placing") {return;}
655 if (getprop(lw~"tmp/convective-status") != "idle") {return;}
656 if ((i < 0) or (i==0)) 
657         {
658         if (local_weather.debug_output_flag == 1) 
659                 {print("Cloud placement from array finished!"); }
660
661         # then place all clouds using the new rendering system
662         if (local_weather.hardcoded_clouds_flag == 1)
663                 {
664                 var s = size(local_weather.cloudAssemblyArray);
665                 create_new_cloud_array(s,cloudAssemblyArray);
666                 }
667         
668         setprop(lw~"tmp/thread-status", "idle");
669
670         # now set flag that tile has been completely processed
671         var dir_index = props.globals.getNode(lw~"tiles/tmp/dir-index").getValue();
672
673         setprop(lw~"tiles/tile["~dir_index~"]/generated-flag",2);       
674
675         return;
676         }
677
678
679 var k_max = 30;
680 var s = size(clouds_path);  
681
682 if (s < k_max) {k_max = s;}
683
684 for (var k = 0; k < k_max; k = k+1)
685         {
686         if (getprop(lw~"config/dynamics-flag") ==1)
687                 {
688                 cloud_mean_altitude = local_weather.clouds_mean_alt[s-k-1];
689                 cloud_flt = local_weather.clouds_flt[s-k-1];
690                 cloud_evolution_timestamp = local_weather.clouds_evolution_timestamp[s-k-1];
691                 }
692         create_cloud(clouds_path[s-k-1], clouds_lat[s-k-1], clouds_lon[s-k-1], clouds_alt[s-k-1], clouds_orientation[s-k-1]);
693         #create_cloud_new(clouds_path[s-k-1], clouds_lat[s-k-1], clouds_lon[s-k-1], clouds_alt[s-k-1], clouds_orientation[s-k-1]);
694         }
695
696 setsize(clouds_path,s-k_max);
697 setsize(clouds_lat,s-k_max);
698 setsize(clouds_lon,s-k_max);
699 setsize(clouds_alt,s-k_max);
700 setsize(clouds_orientation,s-k_max);
701
702 if (getprop(lw~"config/dynamics-flag") ==1)
703                 {
704                 setsize(local_weather.clouds_mean_alt,s-k_max);
705                 setsize(local_weather.clouds_flt,s-k_max);
706                 setsize(local_weather.clouds_evolution_timestamp,s-k_max);
707                 }
708
709 settimer( func {create_cloud_array(i - k, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation ) }, 0 );
710 };
711
712
713 var create_new_cloud_array = func (i, cloudArray)
714 {
715
716
717
718
719 if ((i < 0) or (i==0)) 
720         {
721         if (local_weather.debug_output_flag == 1) 
722                 {print("Processing add-cloud calls finished!"); }
723         return;
724         }
725
726
727 var k_max = 20;
728 var s = size(cloudArray);  
729
730 if (s < k_max) {k_max = s;}
731
732 for (var k = 0; k < k_max; k = k+1)
733         {
734         local_weather.create_cloud_new(cloudArray[s-k-1]);
735         #print(cloudArray[s-k-1].alt);
736         }
737
738 setsize(cloudArray,s-k_max);
739
740
741
742 settimer( func {create_new_cloud_array(i - k, cloudArray) }, 0 );
743 }
744
745
746
747
748
749 ###########################################################
750 # get terrain elevation
751 ###########################################################
752
753 var get_elevation = func (lat, lon) {
754
755 var info = geodinfo(lat, lon);
756         if (info != nil) {var elevation = info[0] * local_weather.m_to_ft;}
757         else {var elevation = -1.0; }
758
759
760 return elevation;
761 }
762
763 ###########################################################
764 # get terrain elevation vector
765 ###########################################################
766
767 var get_elevation_array = func (lat, lon) {
768
769 var elevation = [];
770 var n = size(lat);
771
772
773 for(var i = 0; i < n; i=i+1)
774         {
775         append(elevation, get_elevation(lat[i], lon[i]));
776         }
777         
778
779 return elevation;
780 }
781
782 ###########################################################
783 # set the wxradar echo of a storm
784 ###########################################################
785
786 var set_wxradarecho_storm = func (lat, lon, base, top, radius, ref, turb, type) {
787
788 # look for the next free index in the wxradar property tree entries
789
790 var n = props.globals.getNode("/instrumentation/wxradar", 1);
791                 for (var i = 0; 1; i += 1)
792                         if (n.getChild("storm", i, 0) == nil)
793                                 break;
794 var s = n.getChild("storm", i, 1);
795
796
797 s.getNode("latitude-deg",1).setValue(lat);
798 s.getNode("longitude-deg",1).setValue(lon);
799 s.getNode("heading-deg",1).setValue(0.0);
800 s.getNode("base-altitude-ft",1).setValue(base);
801 s.getNode("top-altitude-ft",1).setValue(top);
802 s.getNode("radius-nm",1).setValue(radius * m_to_nm);
803 s.getNode("reflectivity-norm",1).setValue(ref);
804 s.getNode("turbulence-norm",1).setValue(turb);
805 s.getNode("type",1).setValue(type);
806 s.getNode("show",1).setValue(1);
807 }
808
809 ###########################################################
810 # remove unused echos
811 ###########################################################
812
813 var remove_wxradar_echos = func { 
814
815 var distance_to_remove = 70000.0;
816
817 var storms = props.globals.getNode("/instrumentation/wxradar", 1).getChildren("storm");
818
819 var pos = geo.aircraft_position();
820
821 foreach (s; storms)
822         {
823         var d_sq = local_weather.calc_d_sq(pos.lat(), pos.lon(), s.getNode("latitude-deg").getValue(), s.getNode("longitude-deg").getValue());
824         if (d_sq > distance_to_remove * distance_to_remove)
825                 {
826                 s.remove();
827                 }
828         }
829
830 }
831
832 ############################################################
833 # global variables
834 ############################################################
835
836 # conversions
837
838 var nm_to_m = 1852.00;
839 var m_to_nm = 1.0/nm_to_m; 
840
841 # some common abbreviations
842
843 var lw = "/local-weather/";
844 var ec = "/environment/config/";
845
846 # storage arrays for model vector
847
848 var mvec = [];
849 var msize = 0;
850
851 # loop flags and variables
852
853 var smooth_visibility_loop_flag = 0;
854
855 var visibility_target = 0.0;
856 var visibility_current = 0.0;
857
858 var smooth_light_loop_flag = 0;
859
860 var light_target = 0.0;
861 var light_current = 0.0;
862
863 # available hard-coded support
864
865 var features = {};
866
867 # globals to transmit info if clouds are written from buffer
868
869 var buffered_tile_latitude = 0.0;
870 var buffered_tile_longitude = 0.0;
871 var buffered_tile_alpha = 0.0;
872 var buffered_tile_index = 0;
873
874 # globals to handle additional info for Cumulus cloud dynamics
875
876 var cloud_mean_altitude = 0.0;
877 var cloud_flt = 0.0;
878 var cloud_evolution_timestamp = 0.0;
879
880 # globals to handle new cloud indexing
881
882 var cloud_index = 0;