Advertisement
Guest User

Untitled

a guest
May 6th, 2019
446
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 140.46 KB | None | 0 0
  1.  
  2. ########################################################
  3. # routines to set up, transform and manage local weather
  4. # Thorsten Renk, June 2011
  5. # thermal model by Patrice Poly, April 2010
  6. ########################################################
  7.  
  8. # function purpose
  9. #
  10. # calc_geo to compute the latitude to meter conversion
  11. # calc_d_sq to compute a distance square in local Cartesian approximation
  12. # effect_volume_loop to check if the aircraft has entered an effect volume
  13. # assemble_effect_array to store the size of the effect volume array
  14. # add_vectors to add two vectors in polar coordinates
  15. # wind_altitude_interpolation to interpolate aloft winds in altitude
  16. # wind_interpolation to interpolate aloft winds in altitude and position
  17. # get_slowdown_fraction to compute the effect of boundary layer wind slowdown
  18. # interpolation_loop to continuously interpolate weather parameters between stations
  19. # thermal_lift_start to start the detailed thermal model
  20. # thermal_lift_loop to manage the detailed thermal lift model
  21. # thermal_lift_stop to end the detailed thermal lift model
  22. # wave_lift_start to start the detailed wave lift model
  23. # wave_lift_loop to manage the detailed wave lift model
  24. # wave_lift_stop to end the detailed wave lift model
  25. # effect_volume_start to manage parameters when an effect volume is entered
  26. # effect_volume_stop to manage parameters when an effect volume is left
  27. # ts_factor (helper function for thermal lift model)
  28. # tl_factor (helper function for thermal lift model)
  29. # calcLift_max to calculate the maximal available thermal lift for given altitude
  30. # calcLift to calculate the thermal lift at aircraft position
  31. # calcWaveLift to calculate wave lift at aircraft position
  32. # create_cloud_vec to place a single cloud into an array to be written later
  33. # clear_all to remove all clouds, effect volumes and weather stations and stop loops
  34. # create_detailed_cumulus_cloud to place multiple cloudlets into a box based on a size parameter
  35. # create_cumulonimbus_cloud to place multiple cloudlets into a box
  36. # create_cumulonimbus_cloud_rain to place multiple cloudlets into a box and add a rain layer beneath
  37. # create_cumosys (wrapper to place a convective cloud system based on terrain coverage)
  38. # cumulus_loop to place 25 Cumulus clouds each frame
  39. # create_cumulus to place a convective cloud system based on terrain coverage
  40. # recreate_cumulus to respawn convective clouds as part of the convective dynamics algorithm
  41. # cumulus_exclusion_layer to create a layer with 'holes' left for thunderstorm placement
  42. # create_rise_clouds to create a barrier cloud system
  43. # create_streak to create a cloud streak
  44. # create_hollow_layer to create a cloud layer in a hollow cylinder (better for performance)
  45. # create_cloudbox to create a sophisticated cumulus cloud with different textures (experimental)
  46. # terrain_presampling_start to initialize terrain presampling
  47. # terrain_presampling_loop to sample 25 terrain points per frame
  48. # terrain_presampling to sample terrain elevation at a random point within specified area
  49. # terrain_presampling_analysis to analyze terrain presampling results
  50. # wave_detection_loop to detect if and where wave lift should be placed (currently unfinished)
  51. # get_convective_altitude to determine the altitude at which a Cumulus cloud is placed
  52. # manage presampling to take proper action when a presampling call has been finished
  53. # set_wind_model_flag to convert the wind model string into an integer flag
  54. # set_texture_mix to determine the texture mix between smooth and rough cloud appearance
  55. # create_effect_volume to create an effect volume
  56. # set_weather_station to specify a weather station for interpolation
  57. # set_atmosphere_ipoint to specify an interpolation point for visibility, haze and shading in the atmosphere
  58. # set_wind_ipoint to set an aloft wind interpolation point
  59. # set_wind_ipoint_metar to set a wind interpolation point from available ground METAR info where aloft is modelled
  60. # add_aloft_weather_point to fetch properties from the aloft weather module and set an aloft interpolation point
  61. # showDialog to pop up a dialog window
  62. # readFlags to read configuration flags from the property tree into Nasal variables at startup
  63. # streak_wrapper wrapper to execute streak from menu
  64. # convection wrapper wrapper to execute convective clouds from menu
  65. # barrier wrapper wrapper to execute barrier clouds from menu
  66. # single_cloud_wrapper wrapper to create single cloud from menu
  67. # layer wrapper wrapper to create layer from menu
  68. # box wrapper wrapper to create a cloudbox (experimental)
  69. # set_aloft wrapper wrapper to create aloft winds from menu
  70. # set_tile to call a weather tile creation from menu
  71. # startup to prepare the package at startup
  72. # test to serve as a testbed for new functions
  73.  
  74. # object purpose
  75.  
  76. # weatherStation to store info about weather conditions
  77. # atmopshereIpoint to store info about haze and light propagation in the atmosphere
  78. # windIpoint to store an interpolation point of the windfield
  79. # effectVolume to store effect volume info and provide methods to move and time-evolve effect volumes
  80. # thermalLift to store thermal info and provide methods to move and time-evolve a thermal
  81. # waveLift to store wave info
  82.  
  83.  
  84.  
  85.  
  86. ###################################
  87. # geospatial helper functions
  88. ###################################
  89.  
  90. var calc_geo = func(clat) {
  91.  
  92. lon_to_m = math.cos(clat*math.pi/180.0) * lat_to_m;
  93. m_to_lon = 1.0/lon_to_m;
  94.  
  95. weather_dynamics.lon_to_m = lon_to_m;
  96. weather_dynamics.m_to_lon = m_to_lon;
  97.  
  98. }
  99.  
  100.  
  101. var calc_d_sq = func (lat1, lon1, lat2, lon2) {
  102.  
  103. var x = (lat1 - lat2) * lat_to_m;
  104. var y = (lon1 - lon2) * lon_to_m;
  105.  
  106. return (x*x + y*y);
  107. }
  108.  
  109.  
  110. ###################################
  111. # effect volume management loop
  112. ###################################
  113.  
  114. var effect_volume_loop = func (index, n_active) {
  115.  
  116.  
  117. if (local_weather_running_flag == 0) {return;}
  118.  
  119. var n = 25;
  120.  
  121.  
  122. var esize = n_effectVolumeArray;
  123.  
  124. var viewpos = geo.aircraft_position();
  125. var active_counter = n_active;
  126.  
  127. var i_max = index + n;
  128. if (i_max > esize) {i_max = esize;}
  129.  
  130. for (var i = index; i < i_max; i = i+1)
  131. {
  132. var e = effectVolumeArray[i];
  133.  
  134. var flag = 0; # default assumption is that we're not in the volume
  135.  
  136. var ealt_min = e.alt_low * ft_to_m;
  137. var ealt_max = e.alt_high * ft_to_m;
  138.  
  139.  
  140. if ((viewpos.alt() > ealt_min) and (viewpos.alt() < ealt_max)) # we are in the correct alt range
  141. {
  142. # so we load geometry next
  143.  
  144. var geometry = e.geometry;
  145. var elat = e.lat;
  146. var elon = e.lon;
  147. var rx = e.r1;
  148.  
  149. if (geometry == 1) # we have a cylinder
  150. {
  151. var d_sq = calc_d_sq(viewpos.lat(), viewpos.lon(), elat, elon);
  152. if (d_sq < (rx*rx)) {flag =1;}
  153. }
  154. else if (geometry == 2) # we have an elliptic shape
  155. {
  156. # get orientation
  157.  
  158. var ry = e.r2;
  159. var phi = e.phi;
  160.  
  161. phi = phi * math.pi/180.0;
  162.  
  163.  
  164. # first get unrotated coordinates
  165. var xx = (viewpos.lon() - elon) * lon_to_m;
  166. var yy = (viewpos.lat() - elat) * lat_to_m;
  167.  
  168. # then rotate to align with the shape
  169. var x = xx * math.cos(phi) - yy * math.sin(phi);
  170. var y = yy * math.cos(phi) + xx * math.sin(phi);
  171.  
  172. # then check elliptic condition
  173. if ((x*x)/(rx*rx) + (y*y)/(ry*ry) <1) {flag = 1;}
  174. }
  175. else if (geometry == 3) # we have a rectangular shape
  176. {
  177. # get orientation
  178.  
  179. var ry = e.r2;
  180. var phi = e.phi;
  181.  
  182. phi = phi * math.pi/180.0;
  183. # first get unrotated coordinates
  184. var xx = (viewpos.lon() - elon) * lon_to_m;
  185. var yy = (viewpos.lat() - elat) * lat_to_m;
  186. # then rotate to align with the shape
  187. var x = xx * math.cos(phi) - yy * math.sin(phi);
  188. var y = yy * math.cos(phi) + xx * math.sin(phi);
  189. # then check rectangle condition
  190. if ((x>-rx) and (x<rx) and (y>-ry) and (y<ry)) {flag = 1;}
  191. }
  192. } # end if altitude
  193.  
  194.  
  195. # if flag ==1 at this point, we are inside the effect volume
  196. # but we only need to take action on entering and leaving, so we check also active_flag
  197.  
  198. # if (flag==1) {print("Inside volume");}
  199.  
  200. var active_flag = e.active_flag;
  201.  
  202. if ((flag==1) and (active_flag ==0)) # we just entered the node
  203. {
  204. #print("Entered volume");
  205. e.active_flag = 1;
  206. effect_volume_start(e);
  207. }
  208. else if ((flag==0) and (active_flag ==1)) # we left an active node
  209. {
  210. #print("Left volume!");
  211. e.active_flag = 0;
  212. effect_volume_stop(e);
  213. }
  214. if (flag==1) {active_counter = active_counter + 1;} # we still count the active volumes
  215.  
  216. } # end foreach
  217.  
  218. # at this point, all active effect counters should have been set to zero if we're outside all volumes
  219. # however there seem to be rare configurations of overlapping volumes for which this doesn't happen
  220. # therefore we zero them for redundancy here so that the interpolation loop can take over
  221. # and set the properties correctly for outside
  222.  
  223.  
  224. if (i == esize) # we check the number of actives and reset all counters
  225. {
  226. if (active_counter == 0)
  227. {
  228. var vNode = props.globals.getNode("local-weather/effect-volumes", 1);
  229. vNode.getChild("number-active-vis").setValue(0);
  230. vNode.getChild("number-active-snow").setValue(0);
  231. vNode.getChild("number-active-rain").setValue(0);
  232. vNode.getChild("number-active-lift").setValue(0);
  233. vNode.getChild("number-active-turb").setValue(0);
  234. vNode.getChild("number-active-sat").setValue(0);
  235. }
  236. #print("n_active: ", active_counter);
  237. active_counter = 0; i = 0;
  238. }
  239.  
  240. # and we repeat the loop as long as the control flag is set
  241.  
  242.  
  243. if (getprop(lw~"effect-loop-flag") ==1) {settimer( func {effect_volume_loop(i, active_counter); },0);}
  244. }
  245.  
  246.  
  247. ###################################
  248. # assemble effect volume array
  249. ###################################
  250.  
  251.  
  252. var assemble_effect_array = func {
  253.  
  254. n_effectVolumeArray = size(effectVolumeArray);
  255. }
  256.  
  257.  
  258.  
  259. ###################################
  260. # vector addition
  261. ###################################
  262.  
  263. var add_vectors = func (phi1, r1, phi2, r2) {
  264.  
  265. phi1 = phi1 * math.pi/180.0;
  266. phi2 = phi2 * math.pi/180.0;
  267.  
  268. var x1 = r1 * math.sin(phi1);
  269. var x2 = r2 * math.sin(phi2);
  270.  
  271. var y1 = r1 * math.cos(phi1);
  272. var y2 = r2 * math.cos(phi2);
  273.  
  274. var x = x1+x2;
  275. var y = y1+y2;
  276.  
  277. var phi = math.atan2(x,y) * 180.0/math.pi;
  278. var r = math.sqrt(x*x + y*y);
  279.  
  280. var vec = [];
  281.  
  282. append(vec, phi);
  283. append(vec,r);
  284.  
  285. return vec;
  286. }
  287.  
  288.  
  289. ###################################
  290. # windfield altitude interpolation
  291. ###################################
  292.  
  293.  
  294. var wind_altitude_interpolation = func (altitude, w) {
  295.  
  296. if (altitude < wind_altitude_array[0]) {var alt_wind = wind_altitude_array[0];}
  297. else if (altitude > wind_altitude_array[8]) {var alt_wind = 0.99* wind_altitude_array[8];}
  298. else {var alt_wind = altitude;}
  299.  
  300. for (var i = 0; i<9; i=i+1)
  301. {if (alt_wind < wind_altitude_array[i]) {break;}}
  302.  
  303.  
  304. #var altNodeMin = w.getChild("altitude",i-1);
  305. #var altNodeMax = w.getChild("altitude",i);
  306.  
  307. #var vmin = altNodeMin.getNode("windspeed-kt").getValue();
  308. #var vmax = altNodeMax.getNode("windspeed-kt").getValue();
  309.  
  310. var vmin = w.alt[i-1].v;
  311. var vmax = w.alt[i].v;
  312.  
  313. #var dir_min = altNodeMin.getNode("wind-from-heading-deg").getValue();
  314. #var dir_max = altNodeMax.getNode("wind-from-heading-deg").getValue();
  315.  
  316. var dir_min = w.alt[i-1].d;
  317. var dir_max = w.alt[i].d;
  318.  
  319. var f = (alt_wind - wind_altitude_array[i-1])/(wind_altitude_array[i] - wind_altitude_array[i-1]);
  320. if (vmin == nil) {
  321. #print("Vmin is null!");
  322. #print(i);
  323. #print(w.alt);
  324. #print(w.alt[i].v);
  325. #print(w.alt[i-1].v);
  326. #print(w.alt[i-1].d);
  327. vmin = 0;
  328. }
  329. var res = add_vectors(dir_min, (1-f) * vmin, dir_max, f * vmax);
  330.  
  331. return res;
  332. }
  333.  
  334.  
  335. ###################################
  336. # windfield spatial interpolation
  337. ###################################
  338.  
  339. var wind_interpolation = func (lat, lon, alt) {
  340.  
  341. var sum_norm = 0;
  342. var sum_wind = [0,0];
  343.  
  344. var wsize = size(windIpointArray);
  345.  
  346. for (var i = 0; i < wsize; i=i+1) {
  347.  
  348.  
  349. var w = windIpointArray[i];
  350. var wpos = geo.Coord.new();
  351. wpos.set_latlon(w.lat,w.lon,1000.0);
  352.  
  353. var ppos = geo.Coord.new();
  354. ppos.set_latlon(lat,lon,1000.0);
  355.  
  356. var d = ppos.distance_to(wpos);
  357. if (d <100.0) {d = 100.0;} # to prevent singularity at zero
  358.  
  359. sum_norm = sum_norm + (1./d) * w.weight;
  360.  
  361. var res = wind_altitude_interpolation(alt,w);
  362.  
  363. sum_wind = add_vectors(sum_wind[0], sum_wind[1], res[0], (res[1]/d) * w.weight);
  364.  
  365. # gradually fade in the interpolation weight of newly added points to
  366. # avoid sudden jumps
  367.  
  368. if (w.weight < 1.0) {w.weight = w.weight + 0.02;}
  369.  
  370. }
  371.  
  372. sum_wind[1] = sum_wind[1] /sum_norm;
  373.  
  374. return sum_wind;
  375. }
  376.  
  377.  
  378. ###################################
  379. # boundary layer computations
  380. ###################################
  381.  
  382.  
  383. var get_slowdown_fraction = func {
  384.  
  385. var tile_index = getprop(lw~"tiles/tile[4]/tile-index");
  386. var altitude_agl = getprop("/position/altitude-agl-ft");
  387. var altitude = getprop("/position/altitude-ft");
  388.  
  389.  
  390.  
  391. if (presampling_flag == 0)
  392. {
  393. var base_layer_thickness = 600.0;
  394. var f_slow = 1.0/3.0;
  395. }
  396. else
  397. {
  398. var alt_median = alt_50_array[tile_index - 1];
  399. var alt_difference = alt_median - (altitude - altitude_agl);
  400. var base_layer_thickness = 150.0;
  401.  
  402. # get the boundary layer size dependent on terrain altitude above terrain median
  403.  
  404. if (alt_difference > 0.0) # we're low and the boundary layer grows
  405. {var boundary_alt = base_layer_thickness + 0.3 * alt_difference;}
  406. else # the boundary layer shrinks
  407. {var boundary_alt = base_layer_thickness + 0.1 * alt_difference;}
  408.  
  409. if (boundary_alt < 50.0){boundary_alt = 50.0;}
  410. if (boundary_alt > 3000.0) {boundary_alt = 3000.0;}
  411.  
  412. # get the boundary effect as a function of bounday layer size
  413.  
  414. var f_slow = 1.0 - (0.2 + 0.17 * math.ln(boundary_alt/base_layer_thickness));
  415. }
  416.  
  417. if (debug_output_flag == 1)
  418. {
  419. #print("Boundary layer thickness: ",base_layer_thickness);
  420. #print("Boundary layer slowdown: ", f_slow);
  421. }
  422. return f_slow;
  423. }
  424.  
  425.  
  426. ###################################
  427. # interpolation management loop
  428. ###################################
  429.  
  430. var interpolation_loop = func {
  431.  
  432. if (local_weather_running_flag == 0) {return;}
  433.  
  434. var viewpos = geo.aircraft_position();
  435.  
  436.  
  437.  
  438. #var vis_before = getprop(lwi~"visibility-m");
  439. var vis_before = interpolated_conditions.visibility_m;
  440.  
  441. # if applicable, do some work for fps sampling
  442.  
  443. if (fps_control_flag == 1)
  444. {
  445. fps_samples = fps_samples +1;
  446. fps_sum = fps_sum + getprop("/sim/frame-rate");
  447. }
  448.  
  449.  
  450. # determine at which distance we no longer keep an interpolation point, needs to be larger for METAR since points are more scarce
  451.  
  452. if (metar_flag == 1)
  453. {var distance_to_unload = 250000.0;}
  454. else
  455. {var distance_to_unload = 120000.0;}
  456.  
  457. # if we can set environment without a reset, the loop can run a bit faster for smoother interpolation
  458. # so determine the suitable timing
  459.  
  460.  
  461. var interpolation_loop_time = 0.1;
  462. var vlimit = 1.01;
  463.  
  464.  
  465. # get an inverse distance weighted average from all defined weather stations
  466.  
  467. var sum_alt = 0.0;
  468. var sum_vis = 0.0;
  469. var sum_T = 0.0;
  470. var sum_p = 0.0;
  471. var sum_D = 0.0;
  472. var sum_norm = 0.0;
  473.  
  474. var n_stations = size(weatherStationArray);
  475.  
  476. for (var i = 0; i < n_stations; i=i+1) {
  477.  
  478. var s = weatherStationArray[i];
  479.  
  480.  
  481. var stpos = geo.Coord.new();
  482. stpos.set_latlon(s.lat,s.lon,0.0);
  483.  
  484. var d = viewpos.distance_to(stpos);
  485. if (d <100.0) {d = 100.0;} # to prevent singularity at zero
  486.  
  487. sum_norm = sum_norm + 1./d * s.weight;
  488.  
  489. sum_alt = sum_alt + (s.alt/d) * s.weight;
  490. sum_vis = sum_vis + (s.vis/d) * s.weight;
  491. sum_T = sum_T + (s.T/d) * s.weight;
  492. sum_D = sum_D + (s.D/d) * s.weight;
  493. sum_p = sum_p + (s.p/d) * s.weight;
  494.  
  495. # gradually fade in the interpolation weight of newly added stations to
  496. # avoid sudden jumps
  497.  
  498. if (s.weight < 1.0) {s.weight = s.weight + 0.02;}
  499.  
  500. # automatically delete stations out of range
  501. # take care not to unload if weird values appear for a moment
  502. # never unload if only one station left
  503. if ((d > distance_to_unload) and (d < (distance_to_unload + 20000.0)) and (n_stations > 1))
  504. {
  505. if (debug_output_flag == 1)
  506. {print("Distance to weather station ", d, " m, unloading ...", i);}
  507. weatherStationArray = weather_tile_management.delete_from_vector(weatherStationArray,i);
  508. i = i-1; n_stations = n_stations -1;
  509. }
  510. }
  511.  
  512. setprop(lwi~"station-number", i+1);
  513.  
  514.  
  515. var ialt = sum_alt/sum_norm;
  516. var vis = sum_vis/sum_norm;
  517. var p = sum_p/sum_norm;
  518. var D = sum_D/sum_norm + temperature_offset;
  519. var T = sum_T/sum_norm + temperature_offset;
  520.  
  521.  
  522.  
  523. # get an inverse distance weighted average from all defined atmospheric condition points
  524.  
  525. sum_norm = 0.0;
  526. var sum_vis_aloft = 0.0;
  527. var sum_vis_alt1 = 0.0;
  528. var sum_vis_ovcst = 0.0;
  529. var sum_ovcst = 0.0;
  530. var sum_ovcst_alt_low = 0.0;
  531. var sum_ovcst_alt_high = 0.0;
  532. var sum_scatt = 0.0;
  533. var sum_scatt_alt_low = 0.0;
  534. var sum_scatt_alt_high = 0.0;
  535.  
  536. var n_iPoints = size(atmosphereIpointArray);
  537.  
  538. for (var i = 0; i < n_iPoints; i=i+1) {
  539.  
  540. var a = atmosphereIpointArray[i];
  541.  
  542.  
  543. var apos = geo.Coord.new();
  544. apos.set_latlon(a.lat,a.lon,0.0);
  545.  
  546. var d = viewpos.distance_to(apos);
  547. if (d <100.0) {d = 100.0;} # to prevent singularity at zero
  548.  
  549. sum_norm = sum_norm + 1./d * a.weight;
  550. sum_vis_aloft = sum_vis_aloft + (a.vis_aloft/d) * a.weight;
  551. sum_vis_alt1 = sum_vis_alt1 + (a.vis_alt1/d) * a.weight;
  552. sum_vis_ovcst = sum_vis_ovcst + (a.vis_ovcst/d) * a.weight;
  553. sum_ovcst = sum_ovcst + (a.ovcst/d) * a.weight;
  554. sum_ovcst_alt_low = sum_ovcst_alt_low + (a.ovcst_alt_low/d) * a.weight;
  555. sum_ovcst_alt_high = sum_ovcst_alt_high + (a.ovcst_alt_high/d) * a.weight;
  556. sum_scatt = sum_scatt + (a.scatt/d) * a.weight;
  557. sum_scatt_alt_low = sum_scatt_alt_low + (a.scatt_alt_low/d) * a.weight;
  558. sum_scatt_alt_high = sum_scatt_alt_high + (a.scatt_alt_high/d) * a.weight;
  559.  
  560. # gradually fade in the interpolation weight of newly added stations to
  561. # avoid sudden jumps
  562.  
  563. if (a.weight < 1.0) {a.weight = a.weight + 0.02;}
  564.  
  565. # automatically delete stations out of range
  566. # take care not to unload if weird values appear for a moment
  567. # never unload if only one station left
  568. if ((d > distance_to_unload) and (d < (distance_to_unload + 20000.0)) and (n_iPoints > 1))
  569. {
  570. if (debug_output_flag == 1)
  571. {print("Distance to atmosphere interpolation point ", d, " m, unloading ...", i);}
  572. atmosphereIpointArray = weather_tile_management.delete_from_vector(atmosphereIpointArray,i);
  573. i = i-1; n_iPoints = n_iPoints -1;
  574. }
  575. }
  576.  
  577. setprop(lwi~"atmosphere-ipoint-number", i+1);
  578.  
  579.  
  580.  
  581. var vis_aloft = sum_vis_aloft/sum_norm;
  582. var vis_alt1 = sum_vis_alt1/sum_norm;
  583. var vis_ovcst = sum_vis_ovcst/sum_norm;
  584. var ovcst_max = sum_ovcst/sum_norm;
  585. var ovcst_alt_low = sum_ovcst_alt_low/sum_norm;
  586. var ovcst_alt_high = sum_ovcst_alt_high/sum_norm;
  587. var scatt_max = sum_scatt/sum_norm;
  588. var scatt_alt_low = sum_scatt_alt_low/sum_norm;
  589. var scatt_alt_high = sum_scatt_alt_high/sum_norm;
  590.  
  591.  
  592.  
  593.  
  594. # altitude model for visibility - increase above the lowest inversion layer to simulate ground haze
  595.  
  596. vis = vis * ground_haze_factor;
  597.  
  598. var altitude = getprop("position/altitude-ft");
  599. # var current_mean_terrain_elevation = ialt;
  600.  
  601. var alt1 = vis_alt1;
  602. var alt2 = alt1 + 1500.0;
  603.  
  604.  
  605. setprop("/environment/ground-visibility-m",vis);
  606. setprop("/environment/ground-haze-thickness-m",alt2 * ft_to_m);
  607.  
  608. # compute the visibility gradients
  609.  
  610. if (realistic_visibility_flag == 1)
  611. {
  612. vis_aloft = vis_aloft * 2.0;
  613. vis_ovcst = vis_ovcst * 3.0;
  614. }
  615.  
  616. var inc1 = 0.0 * (vis_aloft - vis)/(vis_alt1 - ialt);
  617. var inc2 = 0.9 * (vis_aloft - vis)/1500.0;
  618. var inc3 = (vis_ovcst - vis_aloft)/(ovcst_alt_high - vis_alt1+1500);
  619. var inc4 = 0.5;
  620.  
  621.  
  622. if (realistic_visibility_flag == 1)
  623. {inc4 = inc4 * 3.0;}
  624.  
  625. # compute the visibility
  626.  
  627. if (altitude < alt1)
  628. {vis = vis + inc1 * altitude;}
  629. else if (altitude < alt2)
  630. {
  631. vis = vis + inc1 * alt1 + inc2 * (altitude - alt1);
  632. }
  633. else if (altitude < ovcst_alt_high)
  634. {
  635. vis = vis + inc1 * alt1 + inc2 * (alt2-alt1) + inc3 * (altitude - alt2);
  636. }
  637. else if (altitude > ovcst_alt_high)
  638. {
  639. vis = vis + inc1 * alt1 + inc2 * (alt2-alt1) + inc3 * (ovcst_alt_high - alt2) + inc4 * (altitude - ovcst_alt_high);
  640. }
  641.  
  642. # limit visibility (otherwise memory consumption may be very bad...)
  643.  
  644. if (vis > max_vis_range)
  645. {vis = max_vis_range;}
  646.  
  647.  
  648. # determine scattering shader parameters if scattering shader is on
  649.  
  650. if (scattering_shader_flag == 1)
  651. {
  652.  
  653. # values to be used with new exposure filter
  654. var rayleigh = 0.0003;
  655. var mie = 0.005;
  656. var density = 0.3;
  657.  
  658. var vis_factor = (vis - 30000.0)/90000.0;
  659. if (vis_factor < 0.0) {vis_factor = 0.0;}
  660. if (vis_factor > 1.0) {vis_factor = 1.0;}
  661.  
  662.  
  663. if (altitude < 36000.0)
  664. {
  665. rayleigh = 0.0003 - 0.0001 * vis_factor;
  666. mie = 0.005 - vis_factor * 0.002;
  667. }
  668. else if (altitude < 85000.0)
  669. {
  670. rayleigh = (0.0003 - 0.0001 * vis_factor) - (altitude-36000.0)/49000.0 * 0.0001;
  671. mie = 0.005 - vis_factor * 0.002 - (altitude-36000.0)/49000.0 * 0.002;
  672. }
  673. else
  674. {rayleigh = 0.0002 - 0.0001 * vis_factor; mie = 0.003 - vis_factor * 0.002;}
  675.  
  676. # now the pollution factor
  677.  
  678. if (altitude < alt1)
  679. {
  680. rayleigh = rayleigh +0.0003 * air_pollution_norm + 0.0004 * air_pollution_norm * (1.0 - (altitude/alt1) * (altitude/alt1));
  681. density = density + 0.05 * air_pollution_norm + 0.05 * air_pollution_norm * (1.0 - (altitude/alt1) * (altitude/alt1));
  682. }
  683. else
  684. {
  685. rayleigh = rayleigh + 0.0003 * air_pollution_norm;
  686. density = density + 0.05 * air_pollution_norm;
  687. }
  688.  
  689.  
  690. }
  691.  
  692.  
  693. # compute the horizon shading
  694.  
  695. if (altitude < scatt_alt_low)
  696. {
  697. var scatt = scatt_max;
  698. }
  699. else if (altitude < scatt_alt_high)
  700. {
  701. var scatt = scatt_max + (0.95 - scatt_max) * (altitude - scatt_alt_low)/(scatt_alt_high-scatt_alt_low);
  702. }
  703. else
  704. {var scatt = 0.95;}
  705.  
  706.  
  707. # compute the cloud layer self shading correction
  708.  
  709. var sun_angle = 1.57079632675 - getprop("/sim/time/sun-angle-rad");
  710. var cloud_layer_shading = 1.0 - (0.8*(1.0 - scatt_max) * math.pow(math.cos(sun_angle),100.0));
  711.  
  712. # compute the overcast haze
  713.  
  714. if (altitude < ovcst_alt_low)
  715. {
  716. var ovcst = ovcst_max;
  717. }
  718. else if (altitude < ovcst_alt_high)
  719. {
  720. var ovcst = ovcst_max - ovcst_max * (altitude - ovcst_alt_low)/(ovcst_alt_high-ovcst_alt_low);
  721. }
  722. else
  723. {var ovcst = 0.0;}
  724.  
  725.  
  726. # compute heating and cooling of various terrain and object types
  727.  
  728. var time = getprop("sim/time/utc/day-seconds");
  729. time = time + getprop("sim/time/local-offset");
  730.  
  731. # low thermal inertia follows the Sun more or less directly
  732. # high thermal inertia takes some time to reach full heat
  733.  
  734. var t_factor1 = 0.5 * (1.0-math.cos((time * sec_to_rad)));
  735. var t_factor2 = 0.5 * (1.0-math.cos((time * sec_to_rad)-0.4));
  736. var t_factor3 = 0.5 * (1.0-math.cos((time * sec_to_rad)-0.9));
  737.  
  738. var amp = scatt_max;
  739.  
  740. setprop("/environment/surface/delta-T-soil", amp * (-5.0 + 10.0 * t_factor2));
  741. setprop("/environment/surface/delta-T-vegetation", amp * (-5.0 + 10.0 * t_factor1));
  742. setprop("/environment/surface/delta-T-rock", amp * (-7.0 + 14.0 * t_factor1));
  743. setprop("/environment/surface/delta-T-water", amp * (-1.0 + 2.0* t_factor3));
  744. setprop("/environment/surface/delta-T-structure", amp * 10.0* t_factor1);
  745. setprop("/environment/surface/delta-T-cloud", amp * (-2.0 + 2.0* t_factor3));
  746.  
  747. # compute base turbulence
  748.  
  749. var base_turbulence = 0.0;
  750.  
  751. if (altitude < alt1)
  752. {
  753. base_turbulence = lowest_layer_turbulence;
  754. }
  755.  
  756.  
  757.  
  758. # limit relative changes of the visibility, will make for gradual transitions
  759.  
  760. if (vis/vis_before > vlimit)
  761. {vis = vlimit * vis_before;}
  762. else if (vis/vis_before < (2.0-vlimit))
  763. {vis = (2.0-vlimit) * vis_before;}
  764.  
  765.  
  766.  
  767.  
  768. # write all properties into the weather interpolation record
  769.  
  770. setprop(lwi~"mean-terrain-altitude-ft",ialt);
  771.  
  772.  
  773. if (vis > 0.0) interpolated_conditions.visibility_m = vis;
  774. interpolated_conditions.temperature_degc = T;
  775. interpolated_conditions.dewpoint_degc = D;
  776. if (p>10.0) interpolated_conditions.pressure_sea_level_inhg = p;
  777.  
  778.  
  779.  
  780. if (scattering_shader_flag == 1)
  781. {
  782. local_weather.setSkydomeShader(rayleigh, mie, density);
  783. setprop("/environment/cloud-self-shading", cloud_layer_shading);
  784. }
  785.  
  786. local_weather.setScattering(scatt);
  787. local_weather.setOvercast(ovcst);
  788.  
  789.  
  790.  
  791.  
  792. # now check if an effect volume writes the property and set only if not
  793. # but set visibility if interpolated is smaller than effect-specified
  794.  
  795. var flag = getprop("local-weather/effect-volumes/number-active-vis");
  796.  
  797. if ((flag ==0) and (vis > 0.0) and (getprop(lw~"lift-loop-flag") == 0) and (compat_layer.smooth_visibility_loop_flag == 0))
  798. {
  799. compat_layer.setVisibility(vis);
  800. }
  801. else if ((getprop("/local-weather/current/visibility-m") > vis) and (compat_layer.smooth_visibility_loop_flag == 0))
  802. {
  803. compat_layer.setVisibility(vis);
  804. }
  805.  
  806.  
  807.  
  808.  
  809.  
  810. flag = getprop("local-weather/effect-volumes/number-active-lift");
  811.  
  812. if (flag ==0)
  813. {
  814. #setprop(lw~"current/thermal-lift",0.0);
  815. }
  816.  
  817. # no need to check for these, as they are not modelled in effect volumes
  818.  
  819. compat_layer.setTemperature(T);
  820. compat_layer.setDewpoint(D);
  821. if (p>0.0) {compat_layer.setPressure(p);}
  822.  
  823.  
  824. # determine whether low haze is icy and whether we see scattering
  825.  
  826. var ice_hex_sheet = 0.0;
  827. var ice_hex_column = 0.0;
  828.  
  829. if (T < -5.0)
  830. {
  831. ice_hex_column = (-T - 5.0) /10.0;
  832. ice_hex_sheet = (-T - 10.0 + (T-D)) /20.0;
  833.  
  834.  
  835. var sheet_bias = (T-D)/ 20;
  836. if (sheet_bias > 1.0) {sheet_bias = 1.0;}
  837. ice_hex_column = ice_hex_column * sheet_bias;
  838.  
  839. if (ice_hex_sheet > 1.0) {ice_hex_sheet = 1.0;}
  840. if (ice_hex_column > 1.0) {ice_hex_column = 1.0;}
  841. }
  842. #print("Col: ",ice_hex_column);
  843. #print("Sheet: ", ice_hex_sheet);
  844.  
  845. setprop("/environment/scattering-phenomena/ice-hexagonal-column-factor", ice_hex_column);
  846. setprop("/environment/scattering-phenomena/ice-hexagonal-sheet-factor", ice_hex_sheet);
  847.  
  848. # now determine the local wind
  849.  
  850.  
  851. var tile_index = getprop(lw~"tiles/tile[4]/tile-index");
  852.  
  853. if (wind_model_flag ==1) # constant
  854. {
  855. var winddir = weather_dynamics.tile_wind_direction[0];
  856. var windspeed = weather_dynamics.tile_wind_speed[0];
  857.  
  858. wind.cloudlayer = [winddir,windspeed];
  859.  
  860. }
  861. else if (wind_model_flag ==2) # constant in tile
  862. {
  863. var winddir = weather_dynamics.tile_wind_direction[tile_index-1];
  864. var windspeed = weather_dynamics.tile_wind_speed[tile_index-1];
  865.  
  866. wind.cloudlayer = [winddir, windspeed];
  867.  
  868. }
  869. else if (wind_model_flag ==3) # aloft interpolated, constant in tiles
  870. {
  871. var w = windIpointArray[0];
  872. var res = wind_altitude_interpolation(altitude,w);
  873. var winddir = res[0];
  874. var windspeed = res[1];
  875.  
  876. wind.cloudlayer = wind_altitude_interpolation(0.0,w);
  877.  
  878. }
  879. else if (wind_model_flag == 5) # aloft waypoint interpolated
  880. {
  881. var res = wind_interpolation(viewpos.lat(), viewpos.lon(), altitude);
  882.  
  883. var winddir = res[0];
  884. var windspeed = res[1];
  885.  
  886. wind.cloudlayer = wind_interpolation(viewpos.lat(), viewpos.lon(), 0.0);
  887. }
  888.  
  889.  
  890. wind.surface = [wind.cloudlayer[0], wind.cloudlayer[1] * get_slowdown_fraction()];
  891.  
  892. # now do the boundary layer computations
  893.  
  894. var altitude_agl = getprop("/position/altitude-agl-ft");
  895.  
  896. if (altitude_agl < 50.0)
  897. {
  898. base_turbulence = base_turbulence * altitude_agl/50.0;
  899. }
  900.  
  901.  
  902. if (presampling_flag == 0)
  903. {
  904. var boundary_alt = 600.0;
  905. var windspeed_ground = windspeed/3.0;
  906.  
  907. var f_min = 2.0/3.0;
  908.  
  909. if (altitude_agl < boundary_alt)
  910. {var windspeed_current = windspeed_ground + 2.0 * windspeed_ground * (altitude_agl/boundary_alt);}
  911. else
  912. {var windspeed_current = windspeed;}
  913. }
  914. else
  915. {
  916. var alt_median = alt_50_array[tile_index - 1];
  917. var alt_difference = alt_median - (altitude - altitude_agl);
  918. var base_layer_thickness = 150.0;
  919.  
  920. # get the boundary layer size dependent on terrain altitude above terrain median
  921.  
  922. if (alt_difference > 0.0) # we're low and the boundary layer grows
  923. {var boundary_alt = base_layer_thickness + 0.3 * alt_difference;}
  924. else # the boundary layer shrinks
  925. {var boundary_alt = base_layer_thickness + 0.1 * alt_difference;}
  926.  
  927. if (boundary_alt < 50.0){boundary_alt = 50.0;}
  928. if (boundary_alt > 3000.0) {boundary_alt = 3000.0;}
  929.  
  930. # get the boundary effect as a function of bounday layer size
  931.  
  932. var f_min = 0.2 + 0.17 * math.ln(boundary_alt/base_layer_thickness);
  933.  
  934.  
  935. if (altitude_agl < boundary_alt)
  936. {
  937. var windspeed_current = (1-f_min) * windspeed + f_min * windspeed * (altitude_agl/boundary_alt);
  938. }
  939. else
  940. {var windspeed_current = windspeed;}
  941.  
  942. }
  943.  
  944.  
  945. var windspeed_ground = (1.0-f_min) * windspeed;
  946.  
  947.  
  948. # set the wind hash before gusts, it represents mean wind
  949.  
  950. wind.current = [winddir,windspeed_current];
  951.  
  952.  
  953.  
  954. # determine gusts and turbulence in the bounday layer
  955.  
  956. var gust_frequency = getprop(lw~"tmp/gust-frequency-hz");
  957.  
  958.  
  959.  
  960.  
  961. if (gust_frequency > 0.0)
  962. {
  963. var gust_relative_strength = getprop(lw~"tmp/gust-relative-strength");
  964. var gust_angvar = getprop(lw~"tmp/gust-angular-variation-deg");
  965.  
  966. # if we have variability in the direction of the wind, the winds will
  967. # drift by the Markov chain code below to adjust to a new winddir as computed
  968. # above - however if the wind is not variable but still gusty, this won't happen
  969. # so we have to take care of it explicitly
  970.  
  971. if (gust_angvar > 0.0)
  972. {var winddir_last = interpolated_conditions.wind_from_heading_deg;}
  973. else
  974. {var winddir_last = winddir;}
  975.  
  976. var alt_scaling_factor = 1.2 * windspeed / 10.0;
  977. if (alt_scaling_factor < 1.0) {alt_scaling_factor = 1.0;}
  978.  
  979. # expected mean number of gusts in time interval (should be < 1)
  980. var p_gust = gust_frequency * interpolation_loop_time;
  981.  
  982. # real time series show a ~10-30 second modulation as well
  983. var p_squall = gust_frequency * 0.1 * interpolation_loop_time;
  984. var squall_scale = getprop("/local-weather/tmp/squall-scaling-norm");
  985.  
  986. if (rand() < p_squall)
  987. {
  988. squall_scale = rand();
  989. # prefer large changes
  990. if ((squall_scale > 0.3) and (squall_scale < 0.7))
  991. {squall_scale = rand();}
  992.  
  993. setprop("/local-weather/tmp/squall-scaling-norm", squall_scale);
  994. }
  995.  
  996. winddir_change = 0.0;
  997.  
  998. if (rand() < p_gust) # we change the offsets for windspeed and direction
  999. {
  1000. var alt_fact = 1.0 - altitude_agl/(boundary_alt * alt_scaling_factor);
  1001. if (alt_fact < 0.0) {alt_fact = 0.0};
  1002.  
  1003. var random_factor = 0.3 * rand() + 0.7 * squall_scale;
  1004.  
  1005. windspeed_multiplier = (1.0 + (random_factor * gust_relative_strength * alt_fact));
  1006. winddir_change = alt_fact * (1.0 - 2.0 * rand()) * gust_angvar;
  1007. winddir_change = winddir_change * 0.2; # Markov chain parameter, max. change per frame is 1/5
  1008.  
  1009. # if the Markov chain reaches the boundary, reflect
  1010.  
  1011. #print("Winddir: ", winddir, " winddir_last: ", winddir_last, " winddir_change: ", winddir_change);
  1012. if (weather_tile_management.relangle(winddir_last + winddir_change, winddir) > gust_angvar)
  1013. {winddir_change = -winddir_change;}
  1014.  
  1015. }
  1016. windspeed_current = windspeed_current * windspeed_multiplier;
  1017. winddir = winddir_last + winddir_change;
  1018. }
  1019.  
  1020.  
  1021.  
  1022.  
  1023.  
  1024. compat_layer.setWindSmoothly(winddir, windspeed_current);
  1025.  
  1026. # set the interpolated conditions to the wind including gust
  1027.  
  1028. interpolated_conditions.wind_from_heading_deg = winddir;
  1029. interpolated_conditions.windspeed_kt = windspeed_current;
  1030.  
  1031. # hack to get access to the water shader
  1032.  
  1033. setprop("/environment/config/boundary/entry[0]/wind-from-heading-deg",winddir);
  1034. setprop("/environment/config/boundary/entry[0]/wind-speed-kt",windspeed_ground);
  1035.  
  1036. # end hack
  1037.  
  1038.  
  1039.  
  1040.  
  1041. # set turbulence
  1042. flag = getprop("local-weather/effect-volumes/number-active-turb");
  1043.  
  1044. var wind_enhancement_factor = windspeed_current/15.0;
  1045. if (wind_enhancement_factor > 1.5) {wind_enhancement_factor = 1.5;}
  1046.  
  1047. if ((flag ==0))
  1048. {compat_layer.setTurbulence(base_turbulence * wind_enhancement_factor);}
  1049.  
  1050. # set scattering on the ground - this doesn't affect fog but is diffuse and specular light reduction
  1051. # so it is stronger than normal scattering
  1052.  
  1053. var scatt_ground = (scatt_max - 0.4)/0.6;
  1054. if (scatt_ground < 0.0) {scatt_ground = 0.0;}
  1055.  
  1056. setprop("/environment/surface/scattering", scatt_ground);
  1057.  
  1058. if (getprop(lw~"interpolation-loop-flag") ==1) {settimer(interpolation_loop, 0.0);}
  1059.  
  1060. }
  1061.  
  1062. ###################################
  1063. # thermal lift loop startup
  1064. ###################################
  1065.  
  1066. var thermal_lift_start = func (ev) {
  1067.  
  1068.  
  1069. # if another lift loop is already running, do nothing
  1070. if (getprop(lw~"lift-loop-flag") == 1) {return;}
  1071.  
  1072. # copy the properties from effect volume to the lift object
  1073.  
  1074. l = thermalLift.new(ev.lat, ev.lon, ev.radius, ev.height, ev.cn, ev.sh, ev.max_lift, ev.f_lift_radius);
  1075.  
  1076. l.index = ev.index;
  1077.  
  1078. if (dynamics_flag == 1)
  1079. {
  1080. l.timestamp = weather_dynamics.time_lw;
  1081. if (dynamical_convection_flag == 1)
  1082. {
  1083. l.flt = ev.flt;
  1084. l.evolution_timestamp = ev.evolution_timestamp;
  1085. }
  1086. }
  1087.  
  1088.  
  1089.  
  1090. thermal = l;
  1091.  
  1092. if (debug_output_flag == 1)
  1093. {
  1094. print("Entering thermal lift...");
  1095. print("strength: ", thermal.max_lift, " radius: ", thermal.radius);
  1096. if (dynamical_convection_flag ==1)
  1097. {print("fractional lifetime: ", thermal.flt);}
  1098.  
  1099. }
  1100.  
  1101. # and start the lift loop, unless another one is already running
  1102. # so we block overlapping calls
  1103.  
  1104.  
  1105. setprop(lw~"lift-loop-flag",1);
  1106. settimer(thermal_lift_loop,0);
  1107.  
  1108. }
  1109.  
  1110. ###################################
  1111. # thermal lift loop
  1112. ###################################
  1113.  
  1114. var thermal_lift_loop = func {
  1115.  
  1116. if (local_weather_running_flag == 0) {return;}
  1117.  
  1118. var apos = geo.aircraft_position();
  1119.  
  1120. var tlat = thermal.lat;
  1121. var tlon = thermal.lon;
  1122.  
  1123. var tpos = geo.Coord.new();
  1124. tpos.set_latlon(tlat,tlon,0.0);
  1125.  
  1126. var d = apos.distance_to(tpos);
  1127. var alt = getprop("position/altitude-ft");
  1128.  
  1129. if (dynamical_convection_flag == 1)
  1130. {var flt = thermal.flt;}
  1131. else
  1132. {var flt = 0.5;}
  1133.  
  1134. var lift = calcLift(d, alt, thermal.radius, thermal.height, thermal.cn, thermal.sh, thermal.max_lift, thermal.f_lift_radius, flt);
  1135.  
  1136. if (getprop(lw~"wave-loop-flag") ==1)
  1137. {
  1138. lift = lift + getprop(lw~"current/wave-lift");
  1139. }
  1140.  
  1141. # compute a reduction in visibility when entering the cloudbase
  1142.  
  1143. #var vis = getprop(lw~"interpolation/visibility-m");
  1144.  
  1145. var vis = interpolated_conditions.visibility_m;
  1146.  
  1147. if (alt > 0.9 * thermal.height)
  1148. {
  1149. var visibility_reduction = math.pow((alt - 0.9 * thermal.height)/(0.2 * thermal.height),0.1);
  1150. visibility_reduction = visibility_reduction * (1.0 - math.pow(d/(0.8*thermal.radius),14));
  1151.  
  1152. if (visibility_reduction > 1.0) {visibility_reduction = 1.0;} # this shouldn't ever happen
  1153. if (visibility_reduction < 0.0) {visibility_reduction = 0.0;}
  1154. vis = vis * (1.0 - 0.98 * visibility_reduction);
  1155.  
  1156. }
  1157.  
  1158. setprop(lw~"current/visibility-m",vis);
  1159. compat_layer.setVisibility(vis);
  1160.  
  1161.  
  1162.  
  1163.  
  1164. setprop(lw~"current/thermal-lift",lift);
  1165. compat_layer.setLift(lift);
  1166.  
  1167. # if dynamics is on, move the thermal and occasionally compute altitude and age
  1168.  
  1169. if (dynamics_flag == 1)
  1170. {
  1171. thermal.move();
  1172.  
  1173. if ((rand() < 0.01) and (presampling_flag == 1)) # check every 100 frames
  1174. {
  1175. if (dynamical_convection_flag == 1)
  1176. {
  1177. thermal.correct_altitude_and_age();
  1178. if (thermal.flt > 1.1)
  1179. {thermal_lift_stop();}
  1180. }
  1181. else
  1182. {
  1183. thermal.correct_altitude();
  1184. }
  1185. }
  1186. }
  1187.  
  1188.  
  1189. if (getprop(lw~"lift-loop-flag") ==1) {settimer(thermal_lift_loop, 0);}
  1190. }
  1191.  
  1192.  
  1193.  
  1194.  
  1195.  
  1196. ###################################
  1197. # thermal lift loop stop
  1198. ###################################
  1199.  
  1200. var thermal_lift_stop = func {
  1201.  
  1202. setprop(lw~"lift-loop-flag",0);
  1203. setprop(lw~"current/thermal-lift",0.0);
  1204. compat_layer.setLift(0.0);
  1205.  
  1206. if (debug_output_flag == 1)
  1207. {
  1208. print("Leaving thermal lift...");
  1209. }
  1210.  
  1211. }
  1212.  
  1213.  
  1214. ###################################
  1215. # wave lift loop startup
  1216. ###################################
  1217.  
  1218. var wave_lift_start = func (ev) {
  1219.  
  1220. # copy the properties from effect volume to the wave object
  1221.  
  1222.  
  1223. w = waveLift.new (ev.lat, ev.lon, ev.r1, ev.r2, ev.phi, ev.height, ev.max_lift);
  1224. w.index = ev.index;
  1225. wave = w;
  1226.  
  1227. # and start the lift loop, unless another one is already running
  1228. # so we block overlapping calls
  1229.  
  1230. if (getprop(lw~"wave-loop-flag") == 0)
  1231. {setprop(lw~"wave-loop-flag",1); settimer(wave_lift_loop,0);}
  1232.  
  1233. }
  1234.  
  1235. ###################################
  1236. # wave lift loop
  1237. ###################################
  1238.  
  1239. var wave_lift_loop = func {
  1240.  
  1241. if (local_weather_running_flag == 0) {return;}
  1242.  
  1243. var lat = getprop("position/latitude-deg");
  1244. var lon = getprop("position/longitude-deg");
  1245. var alt = getprop("position/altitude-ft");
  1246.  
  1247.  
  1248. var phi = wave.phi * math.pi/180.0;
  1249.  
  1250. var xx = (lon - wave.lon) * lon_to_m;
  1251. var yy = (lat - wave.lat) * lat_to_m;
  1252.  
  1253. var x = xx * math.cos(phi) - yy * math.sin(phi);
  1254. var y = yy * math.cos(phi) + xx * math.sin(phi);
  1255.  
  1256. var lift = calcWaveLift(x,y,alt);
  1257.  
  1258. # check if we are in a thermal, if so set wave lift and let the thermal lift loop add that
  1259.  
  1260. if (getprop(lw~"lift-loop-flag") ==1)
  1261. {
  1262. setprop(lw~"current/wave-lift",lift);
  1263. }
  1264. else
  1265. {
  1266. setprop(lw~"current/thermal-lift",lift);
  1267. }
  1268.  
  1269. if (getprop(lw~"wave-loop-flag") ==1) {settimer(wave_lift_loop, 0);}
  1270. }
  1271.  
  1272.  
  1273.  
  1274.  
  1275. ###################################
  1276. # wave lift loop stop
  1277. ###################################
  1278.  
  1279. var wave_lift_stop = func {
  1280.  
  1281. setprop(lw~"wave-loop-flag",0);
  1282. setprop(lw~"current/thermal-lift",0.0);
  1283. }
  1284.  
  1285.  
  1286.  
  1287. ####################################
  1288. # action taken when in effect volume
  1289. ####################################
  1290.  
  1291. var effect_volume_start = func (ev) {
  1292.  
  1293. var cNode = props.globals.getNode(lw~"current");
  1294.  
  1295.  
  1296. if (ev.vis_flag ==1)
  1297. {
  1298. # first store the current setting in case we need to restore on leaving
  1299.  
  1300. var vis = ev.vis;
  1301. ev.vis_r = cNode.getNode("visibility-m").getValue();
  1302.  
  1303. # then set the new value in current and execute change
  1304. cNode.getNode("visibility-m").setValue(vis);
  1305. #compat_layer.setVisibility(vis);
  1306. print(vis);
  1307. compat_layer.setVisibilitySmoothly(vis);
  1308.  
  1309. # then count the number of active volumes on entry (we need that to determine
  1310. # what to do on exit)
  1311. ev.n_entry_vis = getprop(lw~"effect-volumes/number-active-vis");
  1312.  
  1313. # and add to the counter
  1314. setprop(lw~"effect-volumes/number-active-vis",getprop(lw~"effect-volumes/number-active-vis")+1);
  1315. }
  1316.  
  1317. if (ev.rain_flag == 1)
  1318. {
  1319. var rain = ev.rain;
  1320. #print("Setting rain to:", rain);
  1321. ev.rain_r = cNode.getNode("rain-norm").getValue();
  1322. cNode.getNode("rain-norm").setValue(rain);
  1323. compat_layer.setRain(rain);
  1324. ev.n_entry_rain = getprop(lw~"effect-volumes/number-active-rain");
  1325. setprop(lw~"effect-volumes/number-active-rain",getprop(lw~"effect-volumes/number-active-rain")+1);
  1326. }
  1327. if (ev.snow_flag == 1)
  1328. {
  1329. var snow = ev.snow;
  1330. ev.snow_r = cNode.getNode("snow-norm").getValue();
  1331. cNode.getNode("snow-norm").setValue(snow);
  1332. compat_layer.setSnow(snow);
  1333. ev.n_entry_snow = getprop(lw~"effect-volumes/number-active-snow");
  1334. setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")+1);
  1335. }
  1336. if (ev.turb_flag == 1)
  1337. {
  1338. var turbulence = ev.turb;
  1339. ev.turb_r = cNode.getNode("turbulence").getValue();
  1340. cNode.getNode("turbulence").setValue(turbulence);
  1341. compat_layer.setTurbulence(turbulence);
  1342. ev.n_entry_turb = getprop(lw~"effect-volumes/number-active-turb");
  1343. setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")+1);
  1344. }
  1345. if (ev.sat_flag == 1)
  1346. {
  1347. var saturation = ev.sat;
  1348. ev.sat_r = getprop("/rendering/scene/saturation");
  1349. compat_layer.setLightSmoothly(saturation);
  1350. ev.n_entry_sat = getprop(lw~"effect-volumes/number-active-sat");
  1351. setprop(lw~"effect-volumes/number-active-sat",getprop(lw~"effect-volumes/number-active-sat")+1);
  1352. }
  1353.  
  1354. if (ev.lift_flag == 1)
  1355. {
  1356. var lift = ev.lift;
  1357. ev.lift_r = cNode.getNode("thermal-lift").getValue();
  1358. cNode.getNode("thermal-lift").setValue(lift);
  1359. compat_layer.setLift(lift);
  1360. ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift");
  1361. setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1);
  1362. }
  1363. else if (ev.lift_flag == 2)
  1364. {
  1365. ev.lift_r = cNode.getNode("thermal-lift").getValue();
  1366. ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift");
  1367. setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1);
  1368. thermal_lift_start(ev);
  1369. }
  1370. else if (ev.lift_flag == 3)
  1371. {
  1372. ev.lift_r = cNode.getNode("thermal-lift").getValue();
  1373. ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift");
  1374. setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1);
  1375. wave_lift_start(ev);
  1376. }
  1377.  
  1378. }
  1379.  
  1380.  
  1381.  
  1382. var effect_volume_stop = func (ev) {
  1383.  
  1384. var cNode = props.globals.getNode(lw~"current");
  1385.  
  1386.  
  1387. if (ev.vis_flag == 1)
  1388. {
  1389.  
  1390. var n_active = getprop(lw~"effect-volumes/number-active-vis");
  1391.  
  1392.  
  1393. var n_entry = ev.n_entry_vis;
  1394.  
  1395. # if no other nodes affecting property are active, restore to outside
  1396. # else restore settings as they have been when entering the volume when the number
  1397. # of active volumes is the same as on entry (i.e. volumes are nested), otherwise
  1398. # leave property at current because new definitions are already active and should not
  1399. # be cancelled
  1400.  
  1401. if (n_active ==1){var vis = interpolated_conditions.visibility_m;}
  1402. else if ((n_active -1) == n_entry)
  1403. {var vis = ev.vis_r;}
  1404. else {var vis = cNode.getNode("visibility-m").getValue();}
  1405. cNode.getNode("visibility-m").setValue(vis);
  1406. compat_layer.setVisibilitySmoothly(vis);
  1407.  
  1408. # and subtract from the counter
  1409. setprop(lw~"effect-volumes/number-active-vis",getprop(lw~"effect-volumes/number-active-vis")-1);
  1410. }
  1411. if (ev.rain_flag == 1)
  1412. {
  1413. var n_active = getprop(lw~"effect-volumes/number-active-rain");
  1414. var n_entry = ev.n_entry_rain;
  1415.  
  1416. if (n_active ==1){var rain = interpolated_conditions.rain_norm;}
  1417. else if ((n_active -1) == n_entry)
  1418. {var rain = ev.rain_r;}
  1419. else {var rain = cNode.getNode("rain-norm").getValue();}
  1420. cNode.getNode("rain-norm").setValue(rain);
  1421. compat_layer.setRain(rain);
  1422. setprop(lw~"effect-volumes/number-active-rain",getprop(lw~"effect-volumes/number-active-rain")-1);
  1423. }
  1424.  
  1425. if (ev.snow_flag == 1)
  1426. {
  1427. var n_active = getprop(lw~"effect-volumes/number-active-snow");
  1428. var n_entry = ev.n_entry_snow;
  1429.  
  1430. if (n_active ==1){var snow = interpolated_conditions.snow_norm;}
  1431. else if ((n_active -1) == n_entry)
  1432. {var snow = ev.snow_r;}
  1433. else {var snow = cNode.getNode("snow-norm").getValue();}
  1434. cNode.getNode("snow-norm").setValue(snow);
  1435. compat_layer.setSnow(snow);
  1436. setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")-1);
  1437. }
  1438.  
  1439. if (ev.turb_flag == 1)
  1440. {
  1441. var n_active = getprop(lw~"effect-volumes/number-active-turb");
  1442. var n_entry = ev.n_entry_turb;
  1443. if (n_active ==1){var turbulence = interpolated_conditions.turbulence;}
  1444. else if ((n_active -1) == n_entry)
  1445. {var turbulence = ev.turb_r;}
  1446. else {var turbulence = cNode.getNode("turbulence").getValue();}
  1447. cNode.getNode("turbulence").setValue(turbulence);
  1448. compat_layer.setTurbulence(turbulence);
  1449. setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")-1);
  1450. }
  1451.  
  1452. if (ev.sat_flag == 1)
  1453. {
  1454. var n_active = getprop(lw~"effect-volumes/number-active-sat");
  1455. var n_entry = ev.n_entry_sat;
  1456. if (n_active ==1){var saturation = 1.0;}
  1457. else if ((n_active -1) == n_entry)
  1458. {var saturation = ev.sat_r;}
  1459. else {var saturation = getprop("/rendering/scene/saturation");}
  1460. compat_layer.setLightSmoothly(saturation);
  1461. setprop(lw~"effect-volumes/number-active-sat",getprop(lw~"effect-volumes/number-active-sat")-1);
  1462. }
  1463.  
  1464. if (ev.lift_flag == 1)
  1465. {
  1466. var n_active = getprop(lw~"effect-volumes/number-active-lift");
  1467. var n_entry = ev.n_entry_lift;
  1468. if (n_active ==1){var lift = interpolated_conditions.thermal_lift;}
  1469. else if ((n_active -1) == n_entry)
  1470. {var lift = ev.lift_r;}
  1471. else {var lift = cNode.getNode("thermal-lift").getValue();}
  1472. cNode.getNode("thermal-lift").setValue(lift);
  1473. compat_layer.setLift(lift);
  1474. setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1);
  1475. }
  1476. else if (ev.lift_flag == 2)
  1477. {
  1478. thermal_lift_stop();
  1479. setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1);
  1480. }
  1481. else if (ev.lift_flag == 3)
  1482. {
  1483. wave_lift_stop();
  1484. setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1);
  1485. }
  1486.  
  1487. }
  1488.  
  1489.  
  1490.  
  1491. #########################################
  1492. # compute thermal lift in detailed model
  1493. #########################################
  1494.  
  1495. var ts_factor = func (t, alt, height) {
  1496.  
  1497. var t1 = 0.1; # fractional time at which lift is fully developed
  1498. var t2 = 0.9; # fractional time at which lift starts to decay
  1499. var t3 = 1.0; # fractional time at which lift is gone
  1500.  
  1501. # no time dependence modelled yet
  1502. # return 1.0;
  1503.  
  1504.  
  1505.  
  1506. var t_a = t - (alt/height) * t1 - t1;
  1507.  
  1508. if (t_a<0) {return 0.0;}
  1509. else if (t_a<t1) {return 0.5 + 0.5 * math.cos((1.0-t_a/t1)* math.pi);}
  1510. else if (t_a < t2) {return 1.0;}
  1511. else {return 0.5 - 0.5 * math.cos((1.0-(t2-t_a)/(t3-t2))*math.pi);}
  1512. }
  1513.  
  1514. var tl_factor = func (t, alt, height) {
  1515.  
  1516. var t1 = 0.1; # fractional time at which lift is fully developed
  1517. var t2 = 0.9; # fractional time at which lift starts to decay
  1518. var t3 = 1.0; # fractional time at which lift is gone
  1519.  
  1520. # no time dependence modelled yet
  1521. # return 1.0;
  1522.  
  1523. var t_a = t - (alt/height) * t1;
  1524.  
  1525. if (t_a<0) {return 0.0;}
  1526. else if (t_a<t1) {return 0.5 + 0.5 * math.cos((1.0-t_a/t1)* math.pi);}
  1527. else if (t_a < t2) {return 1.0;}
  1528. else {return 0.5 - 0.5 * math.cos((1.0-(t2-t_a)/(t3-t2))*math.pi);}
  1529. }
  1530.  
  1531.  
  1532. var calcLift_max = func (alt, max_lift, height) {
  1533.  
  1534. alt_agl = getprop("/position/altitude-agl-ft");
  1535.  
  1536. # no lift below ground
  1537. if (alt_agl < 0.0) {return 0.0;}
  1538.  
  1539. # lift ramps up to full within 200 m
  1540. else if (alt_agl < 200.0*m_to_ft)
  1541. {return max_lift * 0.5 * (1.0 + math.cos((1.0-alt_agl/(200.0*m_to_ft))*math.pi));}
  1542.  
  1543. # constant max. lift in main body
  1544. else if ((alt_agl > 200.0*m_to_ft) and (alt < height))
  1545. {return max_lift;}
  1546.  
  1547. # decreasing lift from cloudbase to 10% above base
  1548. else if ((alt > height ) and (alt < height*1.1))
  1549. {return max_lift * 0.5 * (1.0 - math.cos((1.0-10.0*(alt-height)/height)*math.pi));}
  1550.  
  1551. # no lift available above
  1552. else {return 0.0;}
  1553. }
  1554.  
  1555.  
  1556.  
  1557. var calcLift = func (d, alt, R, height, cn, sh, max_lift, f_lift_radius, t) {
  1558.  
  1559. # radius of slice at given altitude
  1560. var r_total = (cn + alt/height*(1.0-cn)) * (R - R * (1.0- sh ) * (1.0 - ((2.0*alt/height)-1.0)*((2.0*alt/height)-1.0)));
  1561.  
  1562.  
  1563. # print("r_total: ", r_total, "d: ",d);
  1564. # print("alt: ", alt, "height: ",height);
  1565.  
  1566. # no lift if we're outside the radius or above the thermal
  1567. if ((d > r_total) or (alt > 1.1*height)) { return 0.0; }
  1568.  
  1569. # fraction of radius providing lift
  1570. var r_lift = f_lift_radius * r_total;
  1571.  
  1572. # print("r_lift: ", r_lift);
  1573.  
  1574. # if we are in the sink portion, get the max. sink for this time and altitude and adjust for actual position
  1575. if ((d < r_total ) and (d > r_lift))
  1576. {
  1577. var s_max = 0.5 * calcLift_max(alt, max_lift, height) * ts_factor(t, alt, height);
  1578. # print("s_max: ", s_max);
  1579. return s_max * math.sin(math.pi * (1.0 + (d-r_lift) * (1.0/(r_total - r_lift))));
  1580. }
  1581. # else we are in the lift portion, get the max. lift for this time and altitude and adjust for actual position
  1582. else
  1583. {
  1584. var l_max = calcLift_max(alt, max_lift, height) * tl_factor(t, alt, height);
  1585. # print("l_max: ", l_max);
  1586. return l_max * math.cos(math.pi * (d/(2.0 * r_lift)));
  1587. }
  1588. }
  1589.  
  1590. #########################################
  1591. # compute wave lift in detailed model
  1592. #########################################
  1593.  
  1594. var calcWaveLift = func (x,y, alt) {
  1595.  
  1596. var lift = wave.max_lift * math.cos((y/wave.y) * 1.5 * math.pi);
  1597.  
  1598. if (abs(x)/wave.x > 0.9)
  1599. {
  1600. lift = lift * (abs(x) - 0.9 * wave.x)/(0.1 * wave.x);
  1601. }
  1602.  
  1603.  
  1604.  
  1605. lift = lift * 2.71828 * math.exp(-alt/wave.height) * alt/wave.height;
  1606.  
  1607. var alt_agl = getprop("/position/altitude-agl-ft");
  1608.  
  1609. if (alt_agl < 1000.0)
  1610. {
  1611. lift = lift * (alt_agl/1000.0) * (alt_agl/1000.0);
  1612. }
  1613.  
  1614. return lift;
  1615. }
  1616.  
  1617.  
  1618.  
  1619.  
  1620.  
  1621.  
  1622. ###########################################################
  1623. # place a single cloud into a vector to be processed
  1624. # separately
  1625. ###########################################################
  1626.  
  1627. var create_cloud_vec = func(path, lat, lon, alt, heading) {
  1628.  
  1629. if (path == "new") # we have to switch to new cloud generating routines
  1630. {
  1631. local_weather.cloudAssembly.lat = lat;
  1632. local_weather.cloudAssembly.lon = lon;
  1633. local_weather.cloudAssembly.alt = alt;
  1634. local_weather.cloudAssembly.top_shade = top_shade;
  1635. local_weather.cloudAssembly.alpha_factor = alpha_factor;
  1636.  
  1637. #print(lat," ",long, " ", alt);
  1638.  
  1639. if (dynamics_flag == 1)
  1640. {
  1641. local_weather.cloudAssembly.mean_alt = cloud_mean_altitude;
  1642. local_weather.cloudAssembly.flt = cloud_fractional_lifetime;
  1643. local_weather.cloudAssembly.evolution_timestamp = cloud_evolution_timestamp;
  1644. local_weather.cloudAssembly.rel_alt = cloudAssembly.alt - cloud_mean_altitude;
  1645. }
  1646.  
  1647. append(cloudAssemblyArray,cloudAssembly);
  1648.  
  1649. # at this point we insert tracers for the depth buffer
  1650.  
  1651. #if (local_weather.cloudAssembly.tracer_flag == 1)
  1652. # {
  1653. # tracerAssembly = local_weather.cloud.new("Tracer", "default");
  1654. # tracerAssembly.texture_sheet = "/Models/Weather/nimbus_sheet1.rgb";
  1655. # tracerAssembly.n_sprites = 1;
  1656. # tracerAssembly.bottom_shade = 0.0;
  1657. # tracerAssembly.top_shade = 0.0;
  1658. # tracerAssembly.num_tex_x = 1;
  1659. # tracerAssembly.num_tex_y = 1;
  1660. # tracerAssembly.lat = lat;
  1661. # tracerAssembly.lon = lon;
  1662. # tracerAssembly.alt = alt + local_weather.cloudAssembly.min_height *0.35 * m_to_ft ;
  1663. # tracerAssembly.min_width = local_weather.cloudAssembly.min_width * 0.35;
  1664. # tracerAssembly.max_width = local_weather.cloudAssembly.max_width * 0.35;
  1665. # tracerAssembly.min_height = local_weather.cloudAssembly.min_height * 0.35;
  1666. # tracerAssembly.max_height = local_weather.cloudAssembly.max_height * 0.35;
  1667. # tracerAssembly.min_cloud_width = local_weather.cloudAssembly.min_cloud_width * 0.35;
  1668. # tracerAssembly.min_cloud_height = local_weather.cloudAssembly.min_cloud_height * 0.35;
  1669. # tracerAssembly.z_scale = local_weather.cloudAssembly.z_scale;
  1670. # append(cloudAssemblyArray,tracerAssembly);
  1671. # }
  1672.  
  1673. return;
  1674. }
  1675.  
  1676. append(clouds_path,path);
  1677. append(clouds_lat,lat);
  1678. append(clouds_lon,lon);
  1679. append(clouds_alt,alt);
  1680. append(clouds_orientation,heading);
  1681.  
  1682. # globals (needed for Cumulus clouds) should be set if needed by the main cloud generating call
  1683.  
  1684. if (dynamics_flag ==1)
  1685. {
  1686. append(clouds_mean_alt, cloud_mean_altitude);
  1687. append(clouds_flt, cloud_fractional_lifetime);
  1688. append(clouds_evolution_timestamp,cloud_evolution_timestamp);
  1689. }
  1690.  
  1691. }
  1692. ###########################################################
  1693. # clear all clouds and effects
  1694. ###########################################################
  1695.  
  1696. var clear_all = func {
  1697.  
  1698. # clear the clouds and models
  1699.  
  1700. var cloudNode = props.globals.getNode(lw~"clouds", 1);
  1701. cloudNode.removeChildren("tile");
  1702.  
  1703. var modelNode = props.globals.getNode("models", 1).getChildren("model");
  1704.  
  1705. foreach (var m; modelNode)
  1706. {
  1707. var l = m.getNode("tile-index",1).getValue();
  1708. if (l != nil)
  1709. {
  1710. m.remove();
  1711. }
  1712. }
  1713.  
  1714.  
  1715. # remove the hard-coded clouds
  1716.  
  1717. foreach (c; weather_tile_management.cloudArray)
  1718. {
  1719. c.remove();
  1720. }
  1721. setsize(weather_tile_management.cloudArray,0);
  1722.  
  1723. # reset pressure continuity
  1724.  
  1725. weather_tiles.last_pressure = 0.0;
  1726.  
  1727. # stop all loops
  1728.  
  1729. setprop(lw~"effect-loop-flag",0);
  1730. setprop(lw~"interpolation-loop-flag",0);
  1731. setprop(lw~"tile-loop-flag",0);
  1732. setprop(lw~"lift-loop-flag",0);
  1733. setprop(lw~"wave-loop-flag",0);
  1734. setprop(lw~"dynamics-loop-flag",0);
  1735. setprop(lw~"timing-loop-flag",0);
  1736. setprop(lw~"buffer-loop-flag",0);
  1737. setprop(lw~"housekeeping-loop-flag",0);
  1738. setprop(lw~"convective-loop-flag",0);
  1739. setprop(lw~"shadow-loop-flag",0);
  1740. setprop(lw~"thunderstorm-loop-flag",0);
  1741.  
  1742. weather_dynamics.convective_loop_kill_flag = 1; # long-running loop needs a different scheme to end
  1743.  
  1744. # also remove rain, snow, haze and light effects
  1745.  
  1746. compat_layer.setRain(0.0);
  1747. compat_layer.setSnow(0.0);
  1748. compat_layer.setLight(1.0);
  1749.  
  1750.  
  1751. # set placement indices to zero
  1752.  
  1753. setprop(lw~"clouds/placement-index",0);
  1754. setprop(lw~"clouds/model-placement-index",0);
  1755. setprop(lw~"effect-volumes/effect-placement-index",0);
  1756. setprop(lw~"effect-volumes/number",0);
  1757. setprop(lw~"effect-volumes/number-active-rain",0);
  1758. setprop(lw~"effect-volumes/number-active-snow",0);
  1759. setprop(lw~"effect-volumes/number-active-vis",0);
  1760. setprop(lw~"effect-volumes/number-active-turb",0);
  1761. setprop(lw~"effect-volumes/number-active-lift",0);
  1762. setprop(lw~"effect-volumes/number-active-sat",0);
  1763. setprop(lw~"tiles/tile-counter",0);
  1764.  
  1765.  
  1766. # remove any quadtrees and arrays
  1767.  
  1768. settimer ( func { setsize(weather_dynamics.cloudQuadtrees,0);},0.1); # to avoid error generation in this frame
  1769. setsize(effectVolumeArray,0);
  1770. n_effectVolumeArray = 0;
  1771.  
  1772. # remove any impostors
  1773.  
  1774. weather_tile_management.remove_impostors();
  1775.  
  1776. # clear out the visual shadows
  1777.  
  1778. for (var i = 0; i<cloudShadowArraySize; i=i+1)
  1779. {
  1780. setprop("/local-weather/cloud-shadows/cloudpos-x["~i~"]",0.0);
  1781. setprop("/local-weather/cloud-shadows/cloudpos-y["~i~"]",0.0);
  1782. }
  1783.  
  1784. # clear any wxradar echos
  1785.  
  1786. if (wxradar_support_flag ==1)
  1787. {props.globals.getNode("/instrumentation/wxradar", 1).removeChildren("storm");}
  1788.  
  1789. # if we have used METAR, we may no longer want to do so
  1790.  
  1791. metar_flag = 0;
  1792.  
  1793.  
  1794. settimer ( func {
  1795. setsize(weather_tile_management.modelArrays,0);
  1796. setsize(weather_dynamics.tile_wind_direction,0);
  1797. setsize(weather_dynamics.tile_wind_speed,0);
  1798. setsize(weather_tile_management.cloudBufferArray,0);
  1799. setsize(weather_tile_management.cloudSceneryArray,0);
  1800. setsize(alt_20_array,0);
  1801. setsize(alt_50_array,0);
  1802. setsize(alt_min_array,0);
  1803. setsize(alt_mean_array,0);
  1804. setsize(weather_dynamics.cloudShadowArray,0);
  1805. setsize(local_weather.thunderstormArray,0);
  1806. setsize(weather_dynamics.cloudShadowCandidateArray,0);
  1807. setsize(weather_dynamics.tile_convective_altitude,0);
  1808. setsize(weather_dynamics.tile_convective_strength,0);
  1809. setsize(weatherStationArray,0);
  1810. setsize(windIpointArray,0);
  1811. setsize(atmosphereIpointArray,0);
  1812. setprop(lw~"clouds/buffer-count",0);
  1813. setprop(lw~"clouds/cloud-scenery-count",0);
  1814. weather_tile_management.n_cloudSceneryArray = 0;
  1815. compat_layer.setScattering(0.8);
  1816. compat_layer.setOvercast(0.0);
  1817. setprop(lwi~"ipoint-number",0);
  1818. setprop(lwi~"atmosphere-ipoint-number", 0);
  1819. },0);
  1820.  
  1821. setprop(lw~"tmp/presampling-status", "idle");
  1822.  
  1823. # reset the random store
  1824.  
  1825. weather_tiles.rnd_store = rand();
  1826.  
  1827. # default 3d clouds layer wrapping back on, just in case
  1828.  
  1829. setprop("/sim/rendering/clouds3d-wrap",1);
  1830.  
  1831. # hand precipitation control back to automatic
  1832.  
  1833. props.globals.getNode("/environment/precipitation-control/detailed-precipitation").setBoolValue("false");
  1834.  
  1835. # indicate that we are no longer running
  1836.  
  1837.  
  1838. local_weather_running_flag = 0;
  1839.  
  1840. }
  1841.  
  1842.  
  1843.  
  1844. ###########################################################
  1845. # detailed Cumulus clouds created from multiple cloudlets
  1846. ###########################################################
  1847.  
  1848. var create_detailed_cumulus_cloud = func (lat, lon, alt, size) {
  1849.  
  1850.  
  1851. # various distribution biases
  1852.  
  1853. var edge_bias = convective_texture_mix;
  1854. size = size + convective_size_bias;
  1855. height_bias = 1.0;
  1856. if (edge_bias > 0.0) {height_bias = height_bias + 15.0 *edge_bias + 20.0 * rand() * edge_bias;}
  1857.  
  1858.  
  1859. #height_bias = 6.0;
  1860.  
  1861.  
  1862. if (size > 2.0)
  1863. {
  1864. if (rand() > (size - 2.0))
  1865. {create_cumulonimbus_cloud(lat, lon, alt, size); }
  1866. else
  1867. {create_cumulonimbus_cloud_rain(lat, lon, alt, size, 0.1 + 0.2* rand());}
  1868. return;
  1869. }
  1870.  
  1871. else if (size>1.5)
  1872. {
  1873. var type = "Congestus";
  1874.  
  1875. var height = 400;
  1876. var n = 3;
  1877. var x = 700.0;
  1878. var y = 200.0;
  1879. var edge = 0.2;
  1880.  
  1881. var alpha = rand() * 180.0;
  1882. edge = edge + edge_bias;
  1883.  
  1884. create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Congestus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
  1885.  
  1886. var type = "Cu (volume)";
  1887. var height = 400;
  1888. var n = 10 + int(height_bias);
  1889. var x = 1400.0;
  1890. var y = 400.0;
  1891. var edge = 0.2;
  1892.  
  1893. edge = edge + edge_bias;
  1894.  
  1895. create_streak(type,lat,lon, alt+ 0.5* (height * height_bias )-offset_map["Cumulus"], height * height_bias ,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
  1896.  
  1897. var btype = "Congestus bottom";
  1898. var n_b = 6;
  1899. height_bias = 1.0;
  1900. var top_shade_store = local_weather.top_shade;
  1901. if (top_shade_store > 0.6) {local_weather.top_shade = 0.6;}
  1902. create_streak(btype,lat,lon, alt -offset_map["Congestus"] -900.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0);
  1903. local_weather.top_shade = top_shade_store;
  1904.  
  1905. if (local_weather.cloud_shadow_flag == 1)
  1906. {
  1907. var cs = local_weather.cloudShadow.new(lat, lon, 0.9 * (1.5 * x)/5000.0 , 0.9);
  1908. cs.index = getprop(lw~"tiles/tile-counter");
  1909. append(cloudShadowCandidateArray,cs);
  1910. }
  1911.  
  1912.  
  1913. }
  1914. else if (size>1.1)
  1915. {
  1916. var type = "Cumulus (cloudlet)";
  1917. var btype = "Cumulus bottom";
  1918. var height = 200;
  1919. var n = 6 + int(height_bias);
  1920. var n_b = 2;
  1921. var x = 900.0;
  1922. var y = 200.0;
  1923. var edge = 0.2;
  1924.  
  1925. var alpha = rand() * 180.0;
  1926. edge = edge + edge_bias;
  1927. create_streak(type,lat,lon, alt+ 0.5* (height* height_bias )-offset_map["Cumulus"], height * height_bias,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
  1928.  
  1929. height_bias = 1.0;
  1930. var top_shade_store = local_weather.top_shade;
  1931. if (top_shade_store > 0.6) {local_weather.top_shade = 0.6;}
  1932. create_streak(btype,lat,lon, alt -offset_map["Cumulus"] - 200.0, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0);
  1933. local_weather.top_shade = top_shade_store;
  1934.  
  1935. if (local_weather.cloud_shadow_flag == 1)
  1936. {
  1937. var cs = local_weather.cloudShadow.new(lat, lon, 0.9 * (1.5 * x)/5000.0 , 0.8);
  1938. cs.index = getprop(lw~"tiles/tile-counter");
  1939. append(cloudShadowCandidateArray,cs);
  1940. }
  1941.  
  1942. }
  1943. else if (size>0.8)
  1944. {
  1945. var type = "Cumulus (cloudlet)";
  1946. var height = 150;
  1947. var n = 4 + int(height_bias);
  1948. var x = 300.0;
  1949. var y = 300.0;
  1950. var edge = 0.3;
  1951.  
  1952. var alpha = rand() * 180.0;
  1953. edge = edge + edge_bias;
  1954. create_streak(type,lat,lon, alt+ 0.5* (height * height_bias )-offset_map["Cumulus"], height * height_bias,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
  1955.  
  1956. n = 2;
  1957. x = 700.0;
  1958. y = 200.0;
  1959. edge = 1.0;
  1960. create_streak(type,lat,lon, alt+ 0.5* (height*height_bias )-offset_map["Cumulus"], height * height_bias,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
  1961.  
  1962. if (local_weather.cloud_shadow_flag == 1)
  1963. {
  1964. var cs = local_weather.cloudShadow.new(lat, lon, 0.9 * (1.5 * x)/5000.0 , 0.7);
  1965. cs.index = getprop(lw~"tiles/tile-counter");
  1966. append(cloudShadowCandidateArray,cs);
  1967. }
  1968.  
  1969.  
  1970. }
  1971.  
  1972. else if (size>0.4)
  1973. {
  1974. var type = "Cumulus (cloudlet)";
  1975. var height = 100;
  1976. var n = 2 + int(height_bias * 0.5);
  1977. var x = 600.0;
  1978. var y = 100.0;
  1979. var edge = 1.0;
  1980.  
  1981. var alpha = rand() * 180.0;
  1982. edge = edge + edge_bias;
  1983. create_streak(type,lat,lon, alt+ 0.5* (height * height_bias)-offset_map["Cumulus"], height * height_bias,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
  1984.  
  1985. if (local_weather.cloud_shadow_flag == 1)
  1986. {
  1987. var cs = local_weather.cloudShadow.new(lat, lon, 0.9 * (1.0 * x)/5000.0 , 0.6);
  1988. cs.index = getprop(lw~"tiles/tile-counter");
  1989. append(cloudShadowCandidateArray,cs);
  1990. }
  1991.  
  1992.  
  1993. }
  1994. else
  1995. {
  1996. var type = "Cumulus (whisp)";
  1997. var height = 100;
  1998. var n = 1;
  1999. var x = 100.0;
  2000. var y = 100.0;
  2001. var edge = 1.0;
  2002.  
  2003. var alpha = rand() * 180.0;
  2004. edge = edge + edge_bias;
  2005. create_streak(type,lat,lon, alt+ 0.3* (height )-offset_map["Cumulus"], height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
  2006. }
  2007. }
  2008.  
  2009. ###########################################################
  2010. # detailed small Cumulonimbus clouds created from multiple cloudlets
  2011. ###########################################################
  2012.  
  2013. var create_cumulonimbus_cloud = func(lat, lon, alt, size) {
  2014.  
  2015.  
  2016. create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,10, 0.2, 0.1, 1.0, 1, 0.8, 0.1, 6);
  2017.  
  2018. #create_cloudbox = func (type, blat, blon, balt, dx,dy,dz,n, f_core, r_core, h_core, n_core, f_bottom, h_bottom, n_bottom)
  2019. }
  2020.  
  2021. ###########################################################
  2022. # detailed small Cumulonimbus and rain created from multiple cloudlets
  2023. ###########################################################
  2024.  
  2025. var create_cumulonimbus_cloud_rain = func(lat, lon, alt, size, rain) {
  2026.  
  2027.  
  2028. create_cloudbox("Cb_box", lat, lon, alt, 2500.0,2000.0, 1000.0,10, 0.2, 0.1, 1.0, 1, 0.8, 0.1, 6);
  2029.  
  2030.  
  2031.  
  2032. # place a rain texture
  2033.  
  2034. var path = "Models/Weather/rain2.xml";
  2035. if (thread_flag == 1)
  2036. {create_cloud_vec(path, lat, lon, alt, 0.0);}
  2037. else
  2038. {compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
  2039.  
  2040.  
  2041.  
  2042. # and some rain underneath
  2043.  
  2044. create_effect_volume(1, lat, lon, 2000.0, 2000.0, 0.0, 0.0, alt+1000.0, 8000.0 + 8000.0 * rand(), rain, -1, -1, -1 ,1,-1 );
  2045.  
  2046.  
  2047. }
  2048.  
  2049.  
  2050. ###########################################################
  2051. # wrappers for convective cloud system to distribute
  2052. # call across several frames if needed
  2053. ###########################################################
  2054.  
  2055. var create_cumosys = func (blat, blon, balt, nc, size) {
  2056.  
  2057.  
  2058. # realistic Cumulus has somewhat larger models, so compensate to get the same coverage
  2059. if (detailed_clouds_flag == 1)
  2060. {nc = int(0.7 * nc);}
  2061.  
  2062. nc = int(nc / cumulus_efficiency_factor);
  2063.  
  2064. if (thread_flag == 1)
  2065. {setprop(lw~"tmp/convective-status", "computing");
  2066. cumulus_loop(blat, blon, balt, nc, size);}
  2067.  
  2068. else
  2069. {create_cumulus(blat, blon, balt, nc, size);
  2070. if (debug_output_flag == 1)
  2071. {print("Convective system done!");}
  2072. }
  2073. }
  2074.  
  2075.  
  2076.  
  2077. var cumulus_loop = func (blat, blon, balt, nc, size) {
  2078.  
  2079. if (local_weather_running_flag == 0) {return;}
  2080.  
  2081. if (local_weather.features.fast_geodinfo == 0)
  2082. {var n = int(25/cumulus_efficiency_factor);}
  2083. else
  2084. {var n = int(200/cumulus_efficiency_factor);}
  2085.  
  2086. if (nc < 0)
  2087. {
  2088. if (debug_output_flag == 1)
  2089. {print("Convective system done!");}
  2090. setprop(lw~"tmp/convective-status", "idle");
  2091. assemble_effect_array();
  2092. convective_size_bias = 0.0;
  2093. height_bias = 1.0;
  2094. return;
  2095. }
  2096.  
  2097. create_cumulus(blat, blon, balt, n, size);
  2098.  
  2099. settimer( func {cumulus_loop(blat, blon, balt, nc-n, size) },0);
  2100. }
  2101.  
  2102. ###########################################################
  2103. # place a convective cloud system
  2104. ###########################################################
  2105.  
  2106. var create_cumulus = func (blat, blon, balt, nc, size) {
  2107.  
  2108.  
  2109.  
  2110. var path = "Models/Weather/blank.ac";
  2111. var i = 0;
  2112. var p = 0.0;
  2113. var rn = 0.0;
  2114. var place_lift_flag = 0;
  2115. var strength = 0.0;
  2116. var detail_flag = detailed_clouds_flag;
  2117.  
  2118. var alpha = getprop(lw~"tmp/tile-orientation-deg") * math.pi/180.0; # the tile orientation
  2119.  
  2120. var tile_index = getprop(lw~"tiles/tile-counter");
  2121. var alt_base = balt;
  2122. if (presampling_flag==1) {alt_base = alt_20_array[tile_index -1];}
  2123.  
  2124.  
  2125.  
  2126.  
  2127. #var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime
  2128.  
  2129. calc_geo(blat);
  2130.  
  2131. # get the local time of the day in seconds
  2132.  
  2133. var t = getprop("sim/time/utc/day-seconds");
  2134. t = t + getprop("sim/time/local-offset");
  2135.  
  2136. # print("t is now:", t);
  2137.  
  2138. # and make a simple sinusoidal model of thermal strength
  2139.  
  2140. # daily variation in number of thermals, peaks at noon
  2141. var t_factor1 = 0.5 * (1.0-math.cos((t * sec_to_rad)));
  2142.  
  2143. # daily variation in strength of thermals, peaks around 15:30
  2144. var t_factor2 = 0.5 * (1.0-math.cos((t * sec_to_rad)-0.9));
  2145.  
  2146.  
  2147. # number of possible thermals equals overall strength times daily variation times geographic variation
  2148. # this is a proxy for solar thermal energy
  2149.  
  2150. nc = t_factor1 * nc * math.cos(blat/180.0*math.pi);
  2151.  
  2152. # var thermal_conditions = getprop(lw~"config/thermal-properties");
  2153.  
  2154.  
  2155. while (i < nc) {
  2156.  
  2157. p = 0.0;
  2158. place_lift_flag = 0;
  2159. strength = 0.0;
  2160.  
  2161. # pick a trial position inside the tile and rotate by tile orientation angle
  2162. var x = (2.0 * rand() - 1.0) * size;
  2163. var y = (2.0 * rand() - 1.0) * size;
  2164.  
  2165. var lat = blat + (y * math.cos(alpha) - x * math.sin(alpha)) * m_to_lat;
  2166. var lon = blon + (x * math.cos(alpha) + y * math.sin(alpha)) * m_to_lon;
  2167.  
  2168. # now check ground cover type on chosen spot
  2169. var info = geodinfo(lat, lon);
  2170.  
  2171. if (info != nil) {
  2172. var elevation = info[0] * m_to_ft;
  2173. if (info[1] != nil){
  2174. var landcover = info[1].names[0];
  2175. if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];}
  2176. else {print(p, " ", info[1].names[0]);}
  2177. }}
  2178. else {
  2179. # to avoid gaps, we create default clouds
  2180.  
  2181. p = p + 0.1;
  2182. var elevation = alt_base;
  2183. # continue;
  2184. }
  2185.  
  2186.  
  2187. # apply some optional corrections, biases clouds towards higher elevations
  2188.  
  2189. var terrain_altitude_factor = 1.0;
  2190. var terrain_strength_factor = 1.0;
  2191.  
  2192. if (detailed_terrain_interaction_flag == 1)
  2193. {
  2194.  
  2195. terrain_altitude_factor = get_terrain_altitude_factor(tile_index, balt, elevation);
  2196. terrain_strength_factor = get_terrain_strength_factor(terrain_altitude_factor);
  2197.  
  2198. }
  2199.  
  2200.  
  2201. # then decide if the thermal energy at the spot generates an updraft and a cloud
  2202.  
  2203. if (rand() < (p * cumulus_efficiency_factor * terrain_altitude_factor)) # we decide to place a cloud at this spot
  2204. {
  2205.  
  2206.  
  2207. # check if we have a terrain elevation analysis available and can use a
  2208. # detailed placement altitude correction
  2209.  
  2210. if (presampling_flag == 1)
  2211. {
  2212.  
  2213. if (detailed_terrain_interaction_flag == 1)
  2214. {
  2215. var grad = get_terrain_gradient(lat, lon, elevation, alpha, 1000.0);
  2216. }
  2217. else
  2218. {var grad = 0.0;}
  2219.  
  2220.  
  2221. var place_alt = get_convective_altitude(balt, elevation, getprop(lw~"tiles/tile-counter"), grad);
  2222. }
  2223. else {var place_alt = balt;}
  2224.  
  2225. # no cloud placement into the ground
  2226. if (place_alt < elevation) {continue;}
  2227.  
  2228. # if we're in a lee, we may not want to place the cloud
  2229.  
  2230. if (detailed_terrain_interaction_flag == 1)
  2231. {
  2232. var p_lee_suppression = get_lee_bias(grad, tile_index);
  2233. if (rand() > p_lee_suppression) {continue;}
  2234. }
  2235.  
  2236.  
  2237. # now decide on the strength of the thermal at the spot and on cloud size
  2238.  
  2239. var rn = rand();
  2240. strength = (1.5 * rn + (2.0 * p * terrain_strength_factor)) * t_factor2;
  2241.  
  2242. # the terrain effect cannot create Cb development, so we have to curb
  2243. # the strength if it would not have been Cb otherwise
  2244.  
  2245. if (strength > 2.0)
  2246. {
  2247. if (((1.5 * rn + (2.0 * p)) * t_factor2) < 2.0)
  2248. {strength = 1.7 + rand() * 0.2;}
  2249. }
  2250.  
  2251.  
  2252. if (strength > 1.0) {place_lift_flag = 1;}
  2253.  
  2254. cloud_mean_altitude = place_alt;
  2255. cloud_fractional_lifetime = rand();
  2256. cloud_evolution_timestamp = weather_dynamics.time_lw;
  2257.  
  2258.  
  2259.  
  2260. if (generate_thermal_lift_flag != 3) # no clouds if we produce blue thermals
  2261. {
  2262. create_detailed_cumulus_cloud(lat, lon, place_alt, strength);
  2263. }
  2264.  
  2265. # now see if we need to create a thermal - first check the flag
  2266. if (generate_thermal_lift_flag == 1) # thermal by constant
  2267. {
  2268. # now check if convection is strong
  2269. if (place_lift_flag == 1)
  2270. {
  2271. var lift = 3.0 + 10.0 * (strength -1.0);
  2272. var radius = 500 + 500 * rand();
  2273. #print("Lift: ", lift * ft_to_m - 1.0);
  2274. create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, -1, lift, 1,-1);
  2275. } # end if place_lift_flag
  2276. } # end if generate-thermal-lift-flag
  2277. else if ((generate_thermal_lift_flag == 2) or (generate_thermal_lift_flag == 3)) # thermal by function
  2278. {
  2279.  
  2280. if (place_lift_flag == 1)
  2281. {
  2282. var lift = (3.0 + 10.0 * (strength -1.0))/thermal_conditions;
  2283. var radius = (500 + 500 * rand())*thermal_conditions;
  2284.  
  2285. create_effect_volume(1, lat, lon, 1.1*radius, 1.1*radius, 0.0, 0.0, place_alt*1.15, -1, -1, -1, lift*0.03, lift, -2,-1);
  2286. } # end if place_lift_flag
  2287.  
  2288. } # end if generate-thermal-lift-flag
  2289.  
  2290.  
  2291. } # end if rand < p
  2292. i = i + 1;
  2293. } # end while
  2294.  
  2295. }
  2296.  
  2297.  
  2298.  
  2299.  
  2300.  
  2301. #################################################################
  2302. # respawn convective clouds to compensate for decay
  2303. # the difference being that new clouds get zero fractional
  2304. # lifetime and are placed based on terrain with a different weight
  2305. ##################################################################
  2306.  
  2307. var recreate_cumulus = func (blat, blon, balt, alpha, nc, size, tile_index) {
  2308.  
  2309. var path = "Models/Weather/blank.ac";
  2310. var i = 0;
  2311. var p = 0.0;
  2312. var rn = 0.0;
  2313. var place_lift_flag = 0;
  2314. var strength = 0.0;
  2315. var detail_flag = detailed_clouds_flag;
  2316.  
  2317. alpha = alpha * math.pi/180.0; # the tile orientation
  2318.  
  2319.  
  2320. # current aircraft position
  2321.  
  2322. var alat = getprop("position/latitude-deg");
  2323. var alon = getprop("position/longitude-deg");
  2324.  
  2325. # get the local time of the day in seconds
  2326.  
  2327. var t = getprop("sim/time/utc/day-seconds");
  2328. t = t + getprop("sim/time/local-offset");
  2329.  
  2330.  
  2331. # and make a simple sinusoidal model of thermal strength
  2332.  
  2333. # daily variation in number of thermals, peaks at noon
  2334. var t_factor1 = 0.5 * (1.0-math.cos((t * sec_to_rad)));
  2335.  
  2336. # daily variation in strength of thermals, peaks around 15:30
  2337. var t_factor2 = 0.5 * (1.0-math.cos((t * sec_to_rad)-0.9));
  2338.  
  2339.  
  2340. # number of possible thermals equals overall strength times daily variation times geographic variation
  2341. # this is a proxy for solar thermal energy
  2342.  
  2343. nc = t_factor1 * nc * math.cos(blat/180.0*math.pi);
  2344.  
  2345. # var thermal_conditions = getprop(lw~"config/thermal-properties");
  2346.  
  2347. var alt_base = alt_20_array[tile_index -1];
  2348.  
  2349. while (i < nc) {
  2350.  
  2351. p = 0.0;
  2352. place_lift_flag = 0;
  2353. strength = 0.0;
  2354.  
  2355. # pick a trial position inside the tile and rotate by tile orientation angle
  2356. var x = (2.0 * rand() - 1.0) * size;
  2357. var y = (2.0 * rand() - 1.0) * size;
  2358.  
  2359. var lat = blat + (y * math.cos(alpha) - x * math.sin(alpha)) * m_to_lat;
  2360. var lon = blon + (x * math.cos(alpha) + y * math.sin(alpha)) * m_to_lon;
  2361.  
  2362. # check if the cloud would be spawned in visual range, if not don't bother
  2363. var d_sq = calc_d_sq(alat, alon, lat, lon);
  2364.  
  2365. if (math.sqrt(d_sq) > weather_tile_management.cloud_view_distance)
  2366. {i = i+1; continue;}
  2367.  
  2368. # now check ground cover type on chosen spot
  2369. var info = geodinfo(lat, lon);
  2370.  
  2371. if (info != nil) {
  2372. var elevation = info[0] * m_to_ft;
  2373. if (info[1] != nil){
  2374. var landcover = info[1].names[0];
  2375. if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];}
  2376. else {print(p, " ", info[1].names[0]);}
  2377. }}
  2378. else {
  2379. # to avoid gaps, we create default clouds
  2380.  
  2381. p = p + 0.1;
  2382. var elevation = alt_base;
  2383. # continue;
  2384. }
  2385.  
  2386.  
  2387. # apply some optional corrections, biases clouds towards higher elevations
  2388.  
  2389. var terrain_altitude_factor = 1.0;
  2390. var terrain_strength_factor = 1.0;
  2391.  
  2392. if (detailed_terrain_interaction_flag == 1)
  2393. {
  2394. terrain_altitude_factor = get_terrain_altitude_factor(tile_index, balt, elevation);
  2395. terrain_strength_factor = get_terrain_strength_factor(terrain_altitude_factor);
  2396. }
  2397.  
  2398.  
  2399.  
  2400.  
  2401. # check if to place a cloud with weight sqrt(p), the lifetime gets another sqrt(p) factor
  2402.  
  2403. if (rand() > math.sqrt(p * cumulus_efficiency_factor * terrain_altitude_factor))
  2404. {i=i+1; continue;}
  2405.  
  2406.  
  2407. # then calculate the strength of the updraft
  2408.  
  2409. strength = (1.5 * rand() + (2.0 * p * terrain_strength_factor)) * t_factor2; # the strength of thermal activity at the spot
  2410. if (strength > 1.0)
  2411. {
  2412. path = select_cloud_model("Cumulus","large"); place_lift_flag = 1;
  2413. }
  2414. else {path = select_cloud_model("Cumulus","small");}
  2415.  
  2416. if (presampling_flag == 1)
  2417. {
  2418. var place_alt = get_convective_altitude(balt, elevation, tile_index,0.0);
  2419. }
  2420. else {var place_alt = balt;}
  2421.  
  2422.  
  2423. # no cloud placement into the ground
  2424. if (place_alt < elevation) {continue;}
  2425.  
  2426. # if we're in a lee, we may not want to place the cloud
  2427.  
  2428. if (detailed_terrain_interaction_flag == 1)
  2429. {
  2430. var p_lee_suppression = get_lee_bias(grad, tile_index);
  2431. if (rand() > math.sqrt(p_lee_suppression)) {continue;}
  2432. }
  2433.  
  2434. cloud_mean_altitude = place_alt;
  2435. cloud_fractional_lifetime = 0.0;
  2436. cloud_evolution_timestamp = weather_dynamics.time_lw;
  2437.  
  2438. compat_layer.cloud_mean_altitude = place_alt;
  2439. compat_layer.cloud_flt = cloud_fractional_lifetime;
  2440. compat_layer.cloud_evolution_timestamp = cloud_evolution_timestamp;
  2441.  
  2442. if (generate_thermal_lift_flag != 3) # no clouds if we produce blue thermals
  2443. {
  2444. if (thread_flag == 1)
  2445. {
  2446. thread_flag = 0; # create clouds immediately
  2447. if (detail_flag == 0){compat_layer.create_cloud(path,lat,lon, place_alt, 0.0);}
  2448. else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);}
  2449. thread_flag = 1; # and restore threading
  2450. }
  2451. else
  2452. {
  2453. if (detail_flag == 0){compat_layer.create_cloud(path, lat, lon, place_alt, 0.0);}
  2454. else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);}
  2455. }
  2456. }
  2457.  
  2458. if (generate_thermal_lift_flag == 1) # thermal by constant
  2459. {
  2460. if (place_lift_flag == 1)
  2461. {
  2462. var lift = 3.0 + 10.0 * (strength -1.0);
  2463. var radius = 500 + 500 * rand();
  2464. create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, -1, lift, 1,-1);
  2465. } # end if place_lift_flag
  2466. } # end if generate-thermal-lift-flag
  2467. else if ((generate_thermal_lift_flag == 2) or (generate_thermal_lift_flag == 3)) # thermal by function
  2468. {
  2469. if (place_lift_flag == 1)
  2470. {
  2471. var lift = (3.0 + 10.0 * (strength -1.0))/thermal_conditions;
  2472. var radius = (500 + 500 * rand())*thermal_conditions;
  2473.  
  2474. create_effect_volume(1, lat, lon, 1.1*radius, 1.1*radius, 0.0, 0.0, place_alt*1.15, -1, -1, -1, lift*0.03, lift, -2,-1);
  2475. } # end if place_lift_flag
  2476.  
  2477. } # end if generate-thermal-lift-flag
  2478.  
  2479.  
  2480. i = i + 1;
  2481. } # end while
  2482.  
  2483. }
  2484.  
  2485.  
  2486.  
  2487.  
  2488.  
  2489.  
  2490.  
  2491. ###########################################################
  2492. # place a barrier cloud system
  2493. ###########################################################
  2494.  
  2495. var create_rise_clouds = func (blat, blon, balt, nc, size, winddir, dist) {
  2496.  
  2497. var path = "Models/Weather/blank.ac";
  2498. var i = 0;
  2499. var p = 0.0;
  2500. var rn = 0.0;
  2501. var nsample = 10;
  2502. var counter = 0;
  2503. var dir = (winddir + 180.0) * math.pi/180.0;
  2504. var step = dist/nsample;
  2505.  
  2506. calc_geo(blat);
  2507.  
  2508. while (i < nc) {
  2509.  
  2510. counter = counter + 1;
  2511. p = 0.0;
  2512.  
  2513. var x = (2.0 * rand() - 1.0) * size;
  2514. var y = (2.0 * rand() - 1.0) * size;
  2515.  
  2516. var lat = blat + y * m_to_lat;
  2517. var lon = blon + x * m_to_lon;
  2518.  
  2519. var elevation = compat_layer.get_elevation(lat, lon);
  2520.  
  2521. #print("elevation: ", elevation, "balt: ", balt);
  2522.  
  2523. if ((elevation < balt) and (elevation != -1.0))
  2524. {
  2525. for (var j = 0; j<nsample; j=j+1)
  2526. {
  2527. d = j * step;
  2528. x = d * math.sin(dir);
  2529. y = d * math.cos(dir);
  2530. var tlat = lat + y * m_to_lat;
  2531. var tlon = lon + x * m_to_lon;
  2532.  
  2533. #print("x: ", x, "y: ", y);
  2534.  
  2535. var elevation1 = compat_layer.get_elevation(tlat,tlon);
  2536. #print("elevation1: ", elevation1, "balt: ", balt);
  2537.  
  2538. if (elevation1 > balt)
  2539. {
  2540. p = 1.0 - j * (1.0/nsample);
  2541. #p = 1.0;
  2542. break;
  2543. }
  2544.  
  2545. }
  2546. }
  2547. if (counter > 500) {print("Cannot place clouds - exiting..."); i = nc;}
  2548. if (rand() < p)
  2549. {
  2550. path = select_cloud_model("Stratus (structured)","large");
  2551. compat_layer.create_cloud(path, lat, lon, balt, 0.0);
  2552. counter = 0;
  2553. i = i+1;
  2554. }
  2555.  
  2556. } # end while
  2557.  
  2558. }
  2559.  
  2560.  
  2561. ###########################################################
  2562. # place a cloud streak
  2563. ###########################################################
  2564.  
  2565. var create_streak = func (type, blat, blong, balt, alt_var, nx, xoffset, edgex, x_var, ny, yoffset, edgey, y_var, direction, tri) {
  2566.  
  2567. var flag = 0;
  2568. var path = "Models/Weather/blank.ac";
  2569. calc_geo(blat);
  2570. var dir = direction * math.pi/180.0;
  2571.  
  2572. var ymin = -0.5 * ny * yoffset;
  2573. var xmin = -0.5 * nx * xoffset;
  2574. var xinc = xoffset * (tri-1.0) /ny;
  2575.  
  2576. var jlow = int(nx*edgex);
  2577. var ilow = int(ny*edgey);
  2578.  
  2579.  
  2580. for (var i=0; i<ny; i=i+1)
  2581. {
  2582. var y = ymin + i * yoffset;
  2583.  
  2584. for (var j=0; j<nx; j=j+1)
  2585. {
  2586. var y0 = y + y_var * 2.0 * (rand() -0.5);
  2587. var x = xmin + j * (xoffset + i * xinc) + x_var * 2.0 * (rand() -0.5);
  2588. var lat = blat + m_to_lat * (y0 * math.cos(dir) - x * math.sin(dir));
  2589. var long = blong + m_to_lon * (x * math.cos(dir) + y0 * math.sin(dir));
  2590.  
  2591. var alt = balt + alt_var * 2 * (rand() - 0.5);
  2592.  
  2593. flag = 0;
  2594. var rn = 6.0 * rand();
  2595.  
  2596. if (((j<jlow) or (j>(nx-jlow-1))) and ((i<ilow) or (i>(ny-ilow-1)))) # select a small or no cloud
  2597. {
  2598. if (rn > 2.0) {flag = 1;} else {path = select_cloud_model(type,"small");}
  2599. }
  2600. if ((j<jlow) or (j>(nx-jlow-1)) or (i<ilow) or (i>(ny-ilow-1)))
  2601. {
  2602. if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"small");}
  2603. }
  2604. else { # select a large cloud
  2605. if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"large");}
  2606. }
  2607.  
  2608.  
  2609. if (flag==0){
  2610. if (thread_flag == 1)
  2611. {create_cloud_vec(path, lat, long, alt, 0.0);}
  2612. else
  2613. {compat_layer.create_cloud(path, lat, long, alt, 0.0);}
  2614.  
  2615.  
  2616. }
  2617. }
  2618.  
  2619. }
  2620.  
  2621. }
  2622.  
  2623.  
  2624.  
  2625.  
  2626.  
  2627.  
  2628.  
  2629. ###########################################################
  2630. # place a cloud layer with a gap in the middle
  2631. # (useful to reduce cloud count in large thunderstorms)
  2632. ###########################################################
  2633.  
  2634. var create_hollow_layer = func (type, blat, blon, balt, bthick, rx, ry, phi, density, edge, gap_fraction) {
  2635.  
  2636.  
  2637. var i = 0;
  2638. var area = math.pi * rx * ry;
  2639. var n = int(area/80000000.0 * 100 * density);
  2640. var path = "Models/Weather/blank.ac";
  2641.  
  2642. phi = phi * math.pi/180.0;
  2643.  
  2644. if (contains(cloud_vertical_size_map, type))
  2645. {var alt_offset = cloud_vertical_size_map[type]/2.0 * m_to_ft;}
  2646. else {var alt_offset = 0.0;}
  2647.  
  2648. while(i<n)
  2649. {
  2650. var x = rx * (2.0 * rand() - 1.0);
  2651. var y = ry * (2.0 * rand() - 1.0);
  2652. var alt = balt + bthick * rand() + 0.8 * alt_offset;
  2653. var res = (x*x)/(rx*rx) + (y*y)/(ry*ry);
  2654.  
  2655.  
  2656. if ((res < 1.0) and (res > (gap_fraction * gap_fraction)))
  2657. {
  2658. var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
  2659. var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
  2660. if (res > ((1.0 - edge) * (1.0- edge)))
  2661. {
  2662. if (rand() > 0.4) {
  2663. path = select_cloud_model(type,"small");
  2664. compat_layer.create_cloud(path, lat, lon, alt, 0.0);
  2665. }
  2666. }
  2667. else {
  2668. path = select_cloud_model(type,"large");
  2669. if (thread_flag == 1)
  2670. {create_cloud_vec(path, lat, lon, alt, 0.0);}
  2671. else
  2672. {compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
  2673. }
  2674. i = i + 1;
  2675. }
  2676. else # we are in the central gap region
  2677. {
  2678. i = i + 1;
  2679. }
  2680. }
  2681.  
  2682. i = 0;
  2683.  
  2684.  
  2685. }
  2686.  
  2687.  
  2688.  
  2689.  
  2690. ###########################################################
  2691. # place a cloud box
  2692. ###########################################################
  2693.  
  2694.  
  2695. var create_cloudbox = func (type, blat, blon, balt, dx,dy,dz,n, f_core, r_core, h_core, n_core, f_bottom, h_bottom, n_bottom) {
  2696.  
  2697. var phi = 0;
  2698.  
  2699. # first get core coordinates
  2700.  
  2701. var core_dx = dx * f_core;
  2702. var core_dy = dy * f_core;
  2703. var core_dz = dz * h_core;
  2704.  
  2705. var core_x_offset = (1.0 * rand() - 0.5) * ((dx - core_dx) * r_core);
  2706. var core_y_offset = (1.0 * rand() - 0.5) * ((dy - core_dy) * r_core);
  2707.  
  2708. # get the bottom geometry
  2709.  
  2710. var bottom_dx = dx * f_bottom;
  2711. var bottom_dy = dy * f_bottom;
  2712. var bottom_dz = dz * h_bottom;
  2713.  
  2714. var bottom_offset = 400.0; # in practice, need a small shift
  2715.  
  2716. # fill the main body of the box
  2717.  
  2718. for (var i=0; i<n; i=i+1)
  2719. {
  2720.  
  2721. var x = 0.5 * dx * (2.0 * rand() - 1.0);
  2722. var y = 0.5 * dy * (2.0 * rand() - 1.0);
  2723.  
  2724. # veto in core region
  2725. if ((x > core_x_offset - 0.5 * core_dx) and (x < core_x_offset + 0.5 * core_dx))
  2726. {
  2727. if ((y > core_y_offset - 0.5 * core_dy) and (y < core_y_offset + 0.5 * core_dy))
  2728. {
  2729. i = i -1;
  2730. continue;
  2731. }
  2732. }
  2733.  
  2734. var alt = balt + bottom_dz + bottom_offset + dz * rand();
  2735.  
  2736. var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
  2737. var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
  2738.  
  2739. var path = select_cloud_model(type,"standard");
  2740.  
  2741.  
  2742. create_cloud_vec(path, lat, lon, alt, 0.0);
  2743.  
  2744. }
  2745.  
  2746. # fill the core region
  2747.  
  2748. for (var i=0; i<n_core; i=i+1)
  2749. {
  2750. var x = 0.5 * core_dx * (2.0 * rand() - 1.0);
  2751. var y = 0.5 * core_dy * (2.0 * rand() - 1.0);
  2752. var alt = balt + bottom_dz + bottom_offset + core_dz * rand();
  2753.  
  2754.  
  2755. var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
  2756. var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
  2757.  
  2758. var path = select_cloud_model(type,"core");
  2759.  
  2760. if (thread_flag == 1)
  2761. {create_cloud_vec(path, lat, lon, alt, 0.0);}
  2762. else
  2763. {compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
  2764.  
  2765. }
  2766.  
  2767. # fill the bottom region
  2768.  
  2769.  
  2770. for (var i=0; i<n_bottom; i=i+1)
  2771. {
  2772. var x = 0.5 * bottom_dx * (2.0 * rand() - 1.0);
  2773. var y = 0.5 * bottom_dy * (2.0 * rand() - 1.0);
  2774. var alt = balt + bottom_dz * rand();
  2775.  
  2776.  
  2777. var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
  2778. var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
  2779.  
  2780. var path = select_cloud_model(type,"bottom");
  2781.  
  2782. var top_shade_store = local_weather.top_shade;
  2783. if (top_shade_store > 0.6) {local_weather.top_shade = 0.6;}
  2784. if (thread_flag == 1)
  2785. {create_cloud_vec(path, lat, lon, alt, 0.0);}
  2786. else
  2787. {compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
  2788. local_weather.top_shade = top_shade_store;
  2789.  
  2790. }
  2791.  
  2792.  
  2793. }
  2794.  
  2795.  
  2796.  
  2797. ###########################################################
  2798. # terrain presampling initialization
  2799. ###########################################################
  2800.  
  2801. var terrain_presampling_start = func (blat, blon, nc, size, alpha) {
  2802.  
  2803. # terrain presampling start is always used the first time, and initializes
  2804. # the hard-coded routine if that is available since the hard-coded routine cannot
  2805. # be yet read out on startup
  2806.  
  2807. # initialize the result vector
  2808.  
  2809. setsize(terrain_n,40);
  2810. for(var j=0;j<40;j=j+1){terrain_n[j]=0;}
  2811.  
  2812. if (thread_flag == 1)
  2813. {
  2814. var status = getprop(lw~"tmp/presampling-status");
  2815. if (status != "idle") # we try a second later
  2816. {
  2817. settimer( func {terrain_presampling_start(blat, blon, nc, size, alpha);},1.00);
  2818. return;
  2819. }
  2820. else
  2821. {
  2822. setprop(lw~"tmp/presampling-status", "sampling");
  2823. terrain_presampling_loop (blat, blon, nc, size, alpha);
  2824. }
  2825. }
  2826. else
  2827. {
  2828. terrain_presampling(blat, blon, nc, size, alpha);
  2829. terrain_presampling_analysis();
  2830. setprop(lw~"tmp/presampling-status", "finished");
  2831. }
  2832.  
  2833. if (compat_layer.features.terrain_presampling == 1)
  2834. {
  2835. print("Starting hard-coded terrain presampling");
  2836. setprop("/environment/terrain/area[0]/enabled",1);
  2837. setprop(lw~"tmp/presampling-status", "sampling");
  2838. setprop("/environment/terrain/area[0]/enabled", 1 );
  2839. setprop("/environment/terrain/area[0]/input/latitude-deg", blat );
  2840. setprop("/environment/terrain/area[0]/input/longitude-deg", blon );
  2841. setprop("/environment/terrain/area[0]/input/use-aircraft-position",1);
  2842. setprop("/environment/terrain/area[0]/input/radius-m",45000.0);
  2843.  
  2844. setprop("/environment/terrain/area[0]/output/valid", 0 );
  2845.  
  2846. }
  2847. }
  2848.  
  2849. ###########################################################
  2850. # terrain presampling loop
  2851. ###########################################################
  2852.  
  2853. var terrain_presampling_loop = func (blat, blon, nc, size, alpha) {
  2854.  
  2855. if ((local_weather_running_flag == 0) and (local_weather_startup_flag == 0)) {return;}
  2856.  
  2857.  
  2858. var n = 25;
  2859. var n_out = 25;
  2860. if (local_weather.features.fast_geodinfo == 0)
  2861. {
  2862. # dynamically drop accuracy if framerate is low
  2863.  
  2864. var dt = getprop("/sim/time/delta-sec");
  2865.  
  2866. if (dt > 0.2) # we have below 20 fps
  2867. {n = 5;}
  2868. else if (dt > 0.1) # we have below 10 fps
  2869. {n = 10;}
  2870. else if (dt > 0.05) # we have below 5 fps
  2871. {n = 15;}
  2872. }
  2873. else
  2874. {
  2875. n = 250; n_out = 250;
  2876. }
  2877.  
  2878. if (nc <= 0) # we're done and may analyze the result
  2879. {
  2880. terrain_presampling_analysis();
  2881. if (debug_output_flag == 1)
  2882. {print("Presampling done!");}
  2883. setprop(lw~"tmp/presampling-status", "finished");
  2884. return;
  2885. }
  2886.  
  2887. terrain_presampling(blat, blon, n, size, alpha);
  2888.  
  2889. settimer( func {terrain_presampling_loop(blat, blon, nc-n_out, size, alpha) },0);
  2890. }
  2891.  
  2892.  
  2893. ###########################################################
  2894. # terrain presampling routine
  2895. ###########################################################
  2896.  
  2897. var terrain_presampling = func (blat, blon, ntries, size, alpha) {
  2898.  
  2899. var phi = alpha * math.pi/180.0;
  2900. var elevation = 0.0;
  2901.  
  2902. var lat_vec = [];
  2903. var lon_vec = [];
  2904. var lat_lon_vec = [];
  2905.  
  2906.  
  2907. for (var i=0; i<ntries; i=i+1)
  2908. {
  2909. var x = (2.0 * rand() - 1.0) * size;
  2910. var y = (2.0 * rand() - 1.0) * size;
  2911.  
  2912. append(lat_vec, blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat);
  2913. append(lon_vec, blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon);
  2914. }
  2915.  
  2916.  
  2917. var elevation_vec = compat_layer.get_elevation_array(lat_vec, lon_vec);
  2918.  
  2919.  
  2920. for (var i=0; i<ntries;i=i+1)
  2921. {
  2922. for(var j=0;j<30;j=j+1)
  2923. {
  2924. if ((elevation_vec[i] != -1.0) and (elevation_vec[i] < 500.0 * (j+1)))
  2925. {terrain_n[j] = terrain_n[j]+1; break;}
  2926. }
  2927.  
  2928. }
  2929.  
  2930.  
  2931.  
  2932. }
  2933.  
  2934. ###########################################################
  2935. # terrain presampling analysis
  2936. ###########################################################
  2937.  
  2938. var terrain_presampling_analysis = func {
  2939.  
  2940. if ((compat_layer.features.terrain_presampling_active == 0) or (getprop(lw~"tiles/tile-counter") == 0))
  2941. {
  2942. var sum = 0;
  2943. var alt_mean = 0;
  2944. var alt_med = 0;
  2945. var alt_20 = 0;
  2946. var alt_min = 0;
  2947. var alt_low_min = 0;
  2948.  
  2949.  
  2950. for (var i=0; i<40;i=i+1)
  2951. {sum = sum + terrain_n[i];}
  2952.  
  2953. var n_tot = sum;
  2954.  
  2955. sum = 0;
  2956. for (var i=0; i<40;i=i+1)
  2957. {
  2958. sum = sum + terrain_n[i];
  2959. if (sum > int(0.5 *n_tot)) {alt_med = i * 500.0; break;}
  2960. }
  2961.  
  2962. sum = 0;
  2963. for (var i=0; i<40;i=i+1)
  2964. {
  2965. sum = sum + terrain_n[i];
  2966. if (sum > int(0.3 *n_tot)) {alt_20 = i * 500.0; break;}
  2967. }
  2968.  
  2969.  
  2970. for (var i=0; i<40;i=i+1) {alt_mean = alt_mean + terrain_n[i] * i * 500.0;}
  2971. alt_mean = alt_mean/n_tot;
  2972.  
  2973. for (var i=0; i<40;i=i+1) {if (terrain_n[i] > 0) {alt_min = i * 500.0; break;}}
  2974.  
  2975. var n_max = 0;
  2976. sum = 0;
  2977.  
  2978. for (var i=0; i<39;i=i+1)
  2979. {
  2980. sum = sum + terrain_n[i];
  2981. if (terrain_n[i] > n_max) {n_max = terrain_n[i];}
  2982. if ((n_max > terrain_n[i+1]) and (sum > int(0.3*n_tot)))
  2983. {alt_low_min = i * 500; break;}
  2984. }
  2985. }
  2986. else
  2987. {
  2988. # print("Hard-coded sampling...");
  2989. var n_tot = getprop("/environment/terrain/area[0]/input/max-samples");
  2990. var alt_mean = getprop("/environment/terrain/area[0]/output/alt-mean-ft");
  2991. var alt_med = getprop("/environment/terrain/area[0]/output/alt-median-ft");
  2992. var alt_min = getprop("/environment/terrain/area[0]/output/alt-min-ft");
  2993. var alt_20 = getprop("/environment/terrain/area[0]/output/alt-offset-ft");
  2994. }
  2995.  
  2996. if (debug_output_flag == 1)
  2997. {print("Terrain presampling analysis results:");
  2998. print("total: ",n_tot," mean: ",alt_mean," median: ",alt_med," min: ",alt_min, " alt_20: ", alt_20);}
  2999.  
  3000.  
  3001.  
  3002. setprop(lw~"tmp/tile-alt-offset-ft",alt_20);
  3003. setprop(lw~"tmp/tile-alt-median-ft",alt_med);
  3004. setprop(lw~"tmp/tile-alt-min-ft",alt_min);
  3005. setprop(lw~"tmp/tile-alt-mean-ft",alt_mean);
  3006. setprop(lw~"tmp/tile-alt-layered-ft",0.5 * (alt_min + alt_20));
  3007.  
  3008. append(alt_50_array, alt_med);
  3009. append(alt_20_array, alt_20);
  3010. append(alt_min_array, alt_min);
  3011. append(alt_mean_array, alt_mean);
  3012.  
  3013.  
  3014. current_mean_alt = 0.5 * (current_mean_alt + alt_20);
  3015.  
  3016.  
  3017. }
  3018.  
  3019.  
  3020.  
  3021. ###########################################################
  3022. # wave conditions search
  3023. ###########################################################
  3024.  
  3025. var wave_detection_loop = func (blat, blon, nx, alpha) {
  3026.  
  3027. if (local_weather_running_flag == 0) {return;}
  3028.  
  3029. var phi = alpha * math.pi/180.0;
  3030. var elevation = 0.0;
  3031. var ny = 20;
  3032.  
  3033.  
  3034. for (var i=0; i<ny; i=i+1)
  3035. {
  3036. var x = 5000.0;
  3037. var y = -20000.0 + i * 2000.0;
  3038.  
  3039. var lat = blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat;
  3040. var lon = blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon;
  3041.  
  3042. elevation = compat_layer.get_elevation(lat, lon);
  3043.  
  3044. print(elevation);
  3045.  
  3046. }
  3047.  
  3048.  
  3049. }
  3050.  
  3051. ###########################################################
  3052. # detailed altitude determination for convective calls
  3053. # clouds follow the terrain to some degree, but not excessively so
  3054. ###########################################################
  3055.  
  3056. var get_convective_altitude = func (balt, elevation, tile_index, grad) {
  3057.  
  3058.  
  3059. var alt_offset = alt_20_array[tile_index - 1];
  3060. var alt_median = alt_50_array[tile_index - 1];
  3061.  
  3062. # get the maximal shift
  3063. var alt_variation = alt_median - alt_offset;
  3064.  
  3065. # always get some amount of leeway
  3066. if (alt_variation < 500.0) {alt_variation = 500.0;}
  3067.  
  3068. # get the correction to the maximal shift by detailed terrain
  3069.  
  3070. if (detailed_terrain_interaction_flag == 1)
  3071. {
  3072. var gradfact = get_gradient_factor(grad);
  3073.  
  3074. if ((local_weather.wind_model_flag == 1) or (local_weather.wind_model_flag == 3))
  3075. {
  3076. var windspeed = tile_wind_speed[0];
  3077. }
  3078. else if ((local_weather.wind_model_flag ==2) or (local_weather.wind_model_flag == 4) or (local_weather.wind_model_flag == 5))
  3079. {
  3080. var windspeed = tile_wind_speed[tile_index-1];
  3081. }
  3082.  
  3083. var gradfact = ((gradfact - 1.0) * windspeed) + 1.0;
  3084. #print("gradfact: ", gradfact);
  3085. }
  3086. else
  3087. {
  3088. var gradfact = 1.0;
  3089. }
  3090.  
  3091. var alt_variation = alt_variation * gradfact;
  3092.  
  3093. # get the difference between offset and foot point
  3094. var alt_diff = elevation - alt_offset;
  3095.  
  3096. # now get the elevation-induced shift
  3097.  
  3098. var fraction = alt_diff / alt_variation;
  3099.  
  3100. if (fraction > 1.0) {fraction = 1.0;} # no placement above maximum shift
  3101. if (fraction < 0.0) {fraction = 0.0;} # no downward shift
  3102.  
  3103. # get the cloud base
  3104.  
  3105. var cloudbase = balt - alt_offset;
  3106.  
  3107. var alt_above_terrain = balt - elevation;
  3108.  
  3109. # the shift strength is weakened if the layer is high above base elevation
  3110. # the reference altitude is 1000 ft, anything higher has less sensitivity to terrain
  3111.  
  3112. var shift_strength = 1000.0/alt_above_terrain;
  3113.  
  3114. if (shift_strength > 1.0) {shift_strength = 1.0;} # no enhancement for very low layers
  3115. if (shift_strength < 0.0) {shift_strength = 1.0;} # this shouldn't happen, but just in case...
  3116.  
  3117. if (alt_diff > alt_variation) {alt_diff = alt_variation;} # maximal shift is given by alt_variation
  3118.  
  3119. # print("balt: ", balt, " new alt: ", balt + shift_strength * alt_diff * fraction);
  3120.  
  3121. return balt + shift_strength * alt_diff * fraction;
  3122.  
  3123. }
  3124.  
  3125.  
  3126. ###########################################################
  3127. # detailed terrain gradient determination in wind direction
  3128. ###########################################################
  3129.  
  3130.  
  3131. var get_terrain_gradient = func (lat, lon, elevation1, phi, dist) {
  3132.  
  3133.  
  3134. # get the first elevation
  3135. # var elevation1 = compat_layer.get_elevation(lat,lon);
  3136.  
  3137. # look <dist> upwind to learn about the history of the cloud
  3138. var elevation2 = compat_layer.get_elevation(lat+weather_tiles.get_lat(0.0,dist,phi), lon+weather_tiles.get_lon(0.0,dist,phi));
  3139.  
  3140. return (elevation2 - elevation1)/(dist * m_to_ft);
  3141. }
  3142.  
  3143. ###########################################################
  3144. # enhancement of the placement altitude due to terrain
  3145. ###########################################################
  3146.  
  3147. var get_gradient_factor = func (grad) {
  3148.  
  3149. if (grad > 0.0)
  3150. {return 1.0;}
  3151. else
  3152. {
  3153. return 1.0 -2.0 * grad;
  3154. }
  3155. }
  3156.  
  3157.  
  3158. ###########################################################
  3159. # suppression of placement in lee terrain
  3160. ###########################################################
  3161.  
  3162. var get_lee_bias = func (grad, tile_index) {
  3163.  
  3164.  
  3165. if ((local_weather.wind_model_flag == 1) or (local_weather.wind_model_flag == 3))
  3166. {
  3167. var windspeed = tile_wind_speed[0];
  3168. }
  3169. else if ((local_weather.wind_model_flag ==2) or (local_weather.wind_model_flag == 4) or (local_weather.wind_model_flag == 5))
  3170. {
  3171. var windspeed = tile_wind_speed[tile_index-1];
  3172. }
  3173.  
  3174.  
  3175. if (grad < 0.0)
  3176. {return 1.0;}
  3177. else
  3178. {
  3179. var lee_bias = 1.0 - (grad * 0.2 * windspeed);
  3180. }
  3181. if (lee_bias < 0.2) {lee_bias = 0.2;}
  3182.  
  3183. return lee_bias;
  3184. }
  3185.  
  3186. ###########################################################
  3187. # enhancement of Cumulus in above average altitude
  3188. ###########################################################
  3189.  
  3190.  
  3191. var get_terrain_altitude_factor = func (tile_index, balt, elevation) {
  3192.  
  3193.  
  3194. var alt_mean = alt_mean_array[tile_index -1];
  3195. var alt_base = alt_20_array[tile_index -1];
  3196.  
  3197. var alt_layer = balt - alt_base;
  3198. var alt_above_terrain = balt - elevation;
  3199. var alt_above_mean = balt - alt_mean;
  3200.  
  3201. # the cloud may still be above terrain even if the layer altitude is negative, but we want to avoid neg. factors here
  3202.  
  3203. if (alt_above_terrain < 0.0) {alt_above_terrain = 0.0;}
  3204.  
  3205. var norm_alt_diff = (alt_above_mean - alt_above_terrain)/alt_layer;
  3206.  
  3207. if (norm_alt_diff > 0.0)
  3208. {
  3209. var terrain_altitude_factor = 1.0 + 2.0 * norm_alt_diff;
  3210. }
  3211. else
  3212. {
  3213. var terrain_altitude_factor = 1.0/(1.0 - 5.0 * norm_alt_diff);
  3214. }
  3215.  
  3216. if (terrain_altitude_factor > 3.0) {terrain_altitude_factor = 3.0;}
  3217. if (terrain_altitude_factor < 0.1) {terrain_altitude_factor = 0.1;}
  3218.  
  3219. return terrain_altitude_factor;
  3220. }
  3221.  
  3222.  
  3223. var get_terrain_strength_factor = func (terrain_altitude_factor) {
  3224.  
  3225. return 1.0+ (0.5 * (terrain_altitude_factor-1.0));
  3226.  
  3227. }
  3228.  
  3229.  
  3230. ###########################################################
  3231. # terrain presampling listener dispatcher
  3232. ###########################################################
  3233.  
  3234. var manage_presampling = func {
  3235.  
  3236.  
  3237.  
  3238. var status = getprop(lw~"tmp/presampling-status");
  3239.  
  3240.  
  3241. # we only take action when the analysis is done
  3242. if (status != "finished") {return;}
  3243.  
  3244. if (getprop(lw~"tiles/tile-counter") == 0) # we deal with a tile setup call from the menu
  3245. {
  3246. set_tile();
  3247. }
  3248. else # the tile setup call came from weather_tile_management
  3249. {
  3250. var lat = getprop(lw~"tiles/tmp/latitude-deg");
  3251. var lon = getprop(lw~"tiles/tmp/longitude-deg");
  3252. var code = getprop(lw~"tiles/tmp/code");
  3253. var dir_index = getprop(lw~"tiles/tmp/dir-index");
  3254.  
  3255. weather_tile_management.generate_tile(code, lat, lon, dir_index);
  3256. }
  3257.  
  3258.  
  3259. # set status to idle again
  3260.  
  3261. setprop(lw~"tmp/presampling-status", "idle");
  3262.  
  3263. }
  3264.  
  3265.  
  3266. ###########################################################
  3267. # hardcoded terrain presampling listener dispatcher
  3268. ###########################################################
  3269.  
  3270. var manage_hardcoded_presampling = func {
  3271.  
  3272. var status = getprop("/environment/terrain/area[0]/enabled");
  3273.  
  3274. print("Hard-coded terrain presampling status: ", status);
  3275.  
  3276. # no action unless the sampler has finished
  3277. if (status ==0) {return;}
  3278.  
  3279. # no action if the sampler hasn't been started
  3280.  
  3281. if (getprop(lw~"tmp/presampling-status") != "sampling") {return;}
  3282.  
  3283. terrain_presampling_analysis();
  3284. if (debug_output_flag == 1)
  3285. {print("Presampling done!");}
  3286. setprop(lw~"tmp/presampling-status", "finished");
  3287.  
  3288.  
  3289. }
  3290.  
  3291. ###########################################################
  3292. # set wind model flag
  3293. ###########################################################
  3294.  
  3295. var set_wind_model_flag = func {
  3296.  
  3297. var wind_model = getprop(lw~"config/wind-model");
  3298.  
  3299. if (wind_model == "constant") {wind_model_flag = 1;}
  3300. else if (wind_model == "constant in tile") {wind_model_flag =2;}
  3301. else if (wind_model == "aloft interpolated") {wind_model_flag =3; }
  3302. else if (wind_model == "airmass interpolated") {wind_model_flag =4;}
  3303. else if (wind_model == "aloft waypoints") {wind_model_flag =5;}
  3304. else {print("Wind model not implemented!"); wind_model_flag =1;}
  3305.  
  3306.  
  3307. }
  3308.  
  3309.  
  3310. ###########################################################
  3311. # set texture mix for convective clouds
  3312. ###########################################################
  3313.  
  3314. var set_texture_mix = func {
  3315.  
  3316. var thermal_properties = getprop(lw~"config/thermal-properties");
  3317. thermal_conditions = thermal_properties;
  3318.  
  3319. convective_texture_mix = -(thermal_properties - 1.0) * 0.4;
  3320.  
  3321. if (convective_texture_mix < -0.2) {convective_texture_mix = -0.2;}
  3322. if (convective_texture_mix > 0.2) {convective_texture_mix = 0.2;}
  3323.  
  3324. lowest_layer_turbulence = 0.7 - thermal_properties;
  3325. if (lowest_layer_turbulence < 0.0) {lowest_layer_turbulence = 0.0;}
  3326. }
  3327.  
  3328. ###########################################################
  3329. # create an effect volume
  3330. ###########################################################
  3331.  
  3332. var create_effect_volume = func (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat) {
  3333.  
  3334.  
  3335. var ev = effectVolume.new (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat);
  3336. ev.index = getprop(lw~"tiles/tile-counter");
  3337. ev.active_flag = 0;
  3338.  
  3339.  
  3340. if (vis < 0.0) {ev.vis_flag = 0;} else {ev.vis_flag = 1;}
  3341. if (rain < 0.0) {ev.rain_flag = 0;} else {ev.rain_flag = 1;}
  3342. if (snow < 0.0) {ev.snow_flag = 0;} else {ev.snow_flag = 1;}
  3343. if (turb < 0.0) {ev.turb_flag = 0;} else {ev.turb_flag = 1;}
  3344. if (lift_flag == 0.0) {ev.lift_flag = 0;} else {ev.lift_flag = 1;}
  3345. if (sat < 0.0) {ev.sat_flag = 0;} else {ev.sat_flag = 1;}
  3346. if (sat > 1.0) {sat = 1.0;}
  3347.  
  3348. if (lift_flag == -2) # we create a thermal by function
  3349. {
  3350. ev.lift_flag = 2;
  3351. ev.radius = 0.8 * r1;
  3352. ev.height = alt_high * 0.87;
  3353. ev.cn = 0.7 + rand() * 0.2;
  3354. ev.sh = 0.7 + rand() * 0.2;
  3355. ev.max_lift = lift;
  3356. ev.f_lift_radius = 0.7 + rand() * 0.2;
  3357. if (dynamics_flag == 1) # globals set by the convective system
  3358. {
  3359. ev.flt = cloud_fractional_lifetime;
  3360. ev.evolution_timestamp = cloud_evolution_timestamp;
  3361. }
  3362. }
  3363.  
  3364. if (lift_flag == -3) # we create a wave lift
  3365. {
  3366. ev.lift_flag = 3;
  3367. ev.height = 10000.0; # scale height in ft
  3368. ev.max_lift = lift;
  3369. ev.index = 0; # static objects are assigned tile id zero
  3370. }
  3371.  
  3372. # set a timestamp if needed
  3373.  
  3374. if (dynamics_flag == 1)
  3375. {
  3376. ev.timestamp = weather_dynamics.time_lw;
  3377. }
  3378.  
  3379. # and add to the counter
  3380. setprop(lw~"effect-volumes/number",getprop(lw~"effect-volumes/number")+1);
  3381.  
  3382. append(effectVolumeArray,ev);
  3383. }
  3384.  
  3385.  
  3386.  
  3387.  
  3388.  
  3389. ###########################################################
  3390. # set a weather station for interpolation
  3391. ###########################################################
  3392.  
  3393. var set_weather_station = func (lat, lon, alt, vis, T, D, p) {
  3394.  
  3395. var s = weatherStation.new (lat, lon, alt, vis, T, D, p);
  3396. s.index = getprop(lw~"tiles/tile-counter");
  3397. s.weight = 0.02;
  3398.  
  3399. # set a timestamp if needed
  3400.  
  3401. if (dynamics_flag == 1)
  3402. {
  3403. s.timestamp = weather_dynamics.time_lw;
  3404. }
  3405. append(weatherStationArray,s);
  3406.  
  3407. }
  3408.  
  3409. ###########################################################
  3410. # set an atmosphere condition point for interpolation
  3411. ###########################################################
  3412.  
  3413. var set_atmosphere_ipoint = func (lat, lon, vis_aloft, vis_alt1, vis_ovcst, ovcst,ovcst_alt_low, ovcst_alt_high, scatt, scatt_alt_low, scatt_alt_high) {
  3414.  
  3415. var a = atmosphereIpoint.new (lat, lon, vis_aloft, vis_alt1, vis_ovcst, ovcst, ovcst_alt_low, ovcst_alt_high, scatt, scatt_alt_low, scatt_alt_high);
  3416. a.index = getprop(lw~"tiles/tile-counter");
  3417. a.weight = 0.02;
  3418.  
  3419. # set a timestamp if needed
  3420.  
  3421. if (dynamics_flag == 1)
  3422. {
  3423. a.timestamp = weather_dynamics.time_lw;
  3424. }
  3425. append(atmosphereIpointArray,a);
  3426.  
  3427. }
  3428.  
  3429. ###########################################################
  3430. # set a wind interpolation point
  3431. ###########################################################
  3432.  
  3433. var set_wind_ipoint = func (lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8) {
  3434.  
  3435. var w = windIpoint.new(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8);
  3436.  
  3437. append(windIpointArray, w);
  3438. }
  3439.  
  3440.  
  3441. ###########################################################
  3442. # set a wind interpolation point from ground METAR data
  3443. ###########################################################
  3444.  
  3445. var set_wind_ipoint_metar = func (lat, lon, d0, v0) {
  3446.  
  3447. # insert a plausible pattern of aloft winds based on ground info
  3448.  
  3449.  
  3450. # direction of Coriolis deflection depends on hemisphere
  3451. if (lat >0.0) {var dsign = -1.0;} else {var dsign = 1.0;}
  3452.  
  3453.  
  3454. var v1 = v0 * (1.0 + rand() * 0.1);
  3455. var d1 = d0 + dsign * 2.0 * rand();
  3456.  
  3457. var v2 = v0 * (1.2 + rand() * 0.2);
  3458. var d2 = d0 + dsign * (3.0 * rand() + 2.0);
  3459.  
  3460. var v3 = v0 * (1.3 + rand() * 0.4) + 5.0;
  3461. var d3 = d0 + dsign * (3.0 * rand() + 4.0);
  3462.  
  3463. var v4 = v0 * (1.7 + rand() * 0.5) + 10.0;
  3464. var d4 = d0 + dsign * (4.0 * rand() + 8.0);
  3465.  
  3466. var v5 = v0 * (1.7 + rand() * 0.5) + 20.0;
  3467. var d5 = d0 + dsign * (4.0 * rand() + 10.0);
  3468.  
  3469. var v6 = v0 * (1.7 + rand() * 0.5) + 40.0;
  3470. var d6 = d0 + dsign * (4.0 * rand() + 12.0);
  3471.  
  3472. var v7 = v0 * (2.0 + rand() * 0.7) + 50.0;
  3473. var d7 = d0 + dsign * (4.0 * rand() + 13.0);
  3474.  
  3475. var v8 = v0 * (2.0 + rand() * 0.7) + 55.0;;
  3476. var d8 = d0 + dsign * (5.0 * rand() + 14.0);
  3477.  
  3478. var w = windIpoint.new(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8);
  3479.  
  3480. append(windIpointArray, w);
  3481.  
  3482.  
  3483.  
  3484. }
  3485.  
  3486. ###########################################################
  3487. # fetch properties from aloft weather module and add ipoint
  3488. ###########################################################
  3489.  
  3490. var add_aloft_weather_point = func () {
  3491. # if (debug_output_flag == 1) {
  3492. print("Adding aloft weather point for ", getprop("/position/latitude-deg"), ",", getprop("/position/longitude-deg"));
  3493. #}
  3494.  
  3495. if (getprop("/environment/aloftwind/windspd/mb1000") == "" or getprop("/environment/aloftwind/winddir/mb1000") == "") { return; }
  3496.  
  3497. set_wind_ipoint(getprop("/position/latitude-deg"), getprop("/position/longitude-deg"),
  3498. getprop("/environment/aloftwind/winddir/mb1000"), getprop("/environment/aloftwind/windspd/mb1000"),
  3499. getprop("/environment/aloftwind/winddir/mb850"), getprop("/environment/aloftwind/windspd/mb850"),
  3500. getprop("/environment/aloftwind/winddir/mb650"), getprop("/environment/aloftwind/windspd/mb650"),
  3501. getprop("/environment/aloftwind/winddir/mb500"), getprop("/environment/aloftwind/windspd/mb500"),
  3502. getprop("/environment/aloftwind/winddir/mb400"), getprop("/environment/aloftwind/windspd/mb400"),
  3503. getprop("/environment/aloftwind/winddir/mb300"), getprop("/environment/aloftwind/windspd/mb300"),
  3504. getprop("/environment/aloftwind/winddir/mb250"), getprop("/environment/aloftwind/windspd/mb250"),
  3505. getprop("/environment/aloftwind/winddir/mb200"), getprop("/environment/aloftwind/windspd/mb200"),
  3506. getprop("/environment/aloftwind/winddir/mb150"), getprop("/environment/aloftwind/windspd/mb150"));
  3507. }
  3508.  
  3509. setlistener("/environment/aloftwind/winddir/mb1000", func() {
  3510. add_aloft_weather_point();
  3511. }, 0, 0);
  3512. ###########################################################
  3513. # helper to show additional dialogs
  3514. ###########################################################
  3515.  
  3516. var showDialog = func (name) {
  3517.  
  3518. fgcommand("dialog-show", props.Node.new({"dialog-name":name}));
  3519.  
  3520. }
  3521.  
  3522.  
  3523. ###########################################################
  3524. # helper to transfer configuration flags in menu to Nasal
  3525. ###########################################################
  3526.  
  3527. var readFlags = func {
  3528.  
  3529. # thermal lift must be 1 for constant thermals (obsolete), 2 for thermals by model (menu default)
  3530. # and 3 for blue thermals (set internally inside the tile only)
  3531.  
  3532. if (getprop(lw~"config/generate-thermal-lift-flag") ==1) {generate_thermal_lift_flag = 2;}
  3533. else {generate_thermal_lift_flag = 0};
  3534.  
  3535. thread_flag = getprop(lw~"config/thread-flag");
  3536. # dynamics_flag = getprop(lw~"config/dynamics-flag");
  3537. presampling_flag = getprop(lw~"config/presampling-flag");
  3538. detailed_clouds_flag = getprop(lw~"config/detailed-clouds-flag");
  3539. dynamical_convection_flag = getprop(lw~"config/dynamical-convection-flag");
  3540. debug_output_flag = getprop(lw~"config/debug-output-flag");
  3541. fps_control_flag = getprop(lw~"config/fps-control-flag");
  3542. realistic_visibility_flag = getprop(lw~"config/realistic-visibility-flag");
  3543. detailed_terrain_interaction_flag = getprop(lw~"config/detailed-terrain-interaction-flag");
  3544. scattering_shader_flag = getprop("/sim/rendering/shaders/skydome");
  3545.  
  3546. # also initialize menu entries
  3547.  
  3548. air_pollution_norm = getprop("/environment/air-pollution-norm");
  3549.  
  3550. }
  3551.  
  3552. ###########################################################
  3553. # wrappers to call functions from the local weather menu bar
  3554. ###########################################################
  3555.  
  3556. var streak_wrapper = func {
  3557.  
  3558. thread_flag = 0;
  3559. dynamics_flag = 0;
  3560. presampling_flag = 0;
  3561.  
  3562. var array = [];
  3563. append(weather_tile_management.modelArrays,array);
  3564. setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
  3565.  
  3566. var lat = getprop("position/latitude-deg");
  3567. var lon = getprop("position/longitude-deg");
  3568. var type = getprop("/local-weather/tmp/cloud-type");
  3569. var alt = getprop("/local-weather/tmp/alt");
  3570. var nx = getprop("/local-weather/tmp/nx");
  3571. var xoffset = getprop("/local-weather/tmp/xoffset");
  3572. var xedge = getprop("/local-weather/tmp/xedge");
  3573. var ny = getprop("/local-weather/tmp/ny");
  3574. var yoffset = getprop("/local-weather/tmp/yoffset");
  3575. var yedge = getprop("/local-weather/tmp/yedge");
  3576. var dir = getprop("/local-weather/tmp/dir");
  3577. var tri = getprop("/local-weather/tmp/tri");
  3578. var rnd_alt = getprop("/local-weather/tmp/rnd-alt");
  3579. var rnd_pos_x = getprop("/local-weather/tmp/rnd-pos-x");
  3580. var rnd_pos_y = getprop("/local-weather/tmp/rnd-pos-y");
  3581.  
  3582. create_streak(type,lat,lon,alt,rnd_alt,nx,xoffset,xedge,rnd_pos_x,ny,yoffset,yedge,rnd_pos_y,dir,tri);
  3583. }
  3584.  
  3585.  
  3586. var convection_wrapper = func {
  3587.  
  3588. thread_flag = 0;
  3589. dynamics_flag = 0;
  3590. presampling_flag = 0;
  3591.  
  3592.  
  3593. var array = [];
  3594. append(weather_tile_management.modelArrays,array);
  3595. setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
  3596.  
  3597. var lat = getprop("position/latitude-deg");
  3598. var lon = getprop("position/longitude-deg");
  3599. var alt = getprop("/local-weather/tmp/conv-alt");
  3600. var size = getprop("/local-weather/tmp/conv-size");
  3601. var strength = getprop("/local-weather/tmp/conv-strength");
  3602.  
  3603. var n = int(10 * size * size * strength);
  3604. create_cumosys(lat,lon,alt,n, size*1000.0);
  3605.  
  3606. }
  3607.  
  3608. var barrier_wrapper = func {
  3609.  
  3610.  
  3611. thread_flag = 0;
  3612. dynamics_flag = 0;
  3613. presampling_flag = 0;
  3614.  
  3615.  
  3616. var array = [];
  3617. append(weather_tile_management.modelArrays,array);
  3618. setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
  3619.  
  3620. var lat = getprop("position/latitude-deg");
  3621. var lon = getprop("position/longitude-deg");
  3622. var alt = getprop("/local-weather/tmp/bar-alt");
  3623. var n = getprop("/local-weather/tmp/bar-n");
  3624. var dir = getprop("/local-weather/tmp/bar-dir");
  3625. var dist = getprop("/local-weather/tmp/bar-dist") * 1000.0;
  3626. var size = getprop("/local-weather/tmp/bar-size") * 1000.0;
  3627.  
  3628. create_rise_clouds(lat, lon, alt, n, size, dir, dist);
  3629.  
  3630. }
  3631.  
  3632. var single_cloud_wrapper = func {
  3633.  
  3634. thread_flag = 0;
  3635. dynamics_flag = 0;
  3636. presampling_flag = 0;
  3637.  
  3638.  
  3639.  
  3640. var array = [];
  3641. append(weather_tile_management.modelArrays,array);
  3642. setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
  3643.  
  3644. var type = getprop("/local-weather/tmp/scloud-type");
  3645. var subtype = getprop("/local-weather/tmp/scloud-subtype");
  3646. var lat = getprop("/local-weather/tmp/scloud-lat");
  3647. var lon = getprop("/local-weather/tmp/scloud-lon");
  3648. var alt = getprop("/local-weather/tmp/scloud-alt");
  3649. var heading = getprop("/local-weather/tmp/scloud-dir");
  3650.  
  3651. var path = select_cloud_model(type,subtype);
  3652.  
  3653. compat_layer.create_cloud(path, lat, lon, alt, heading);
  3654.  
  3655. }
  3656.  
  3657. var layer_wrapper = func {
  3658.  
  3659. thread_flag = 0;
  3660. dynamics_flag = 0;
  3661. presampling_flag = 0;
  3662.  
  3663.  
  3664. var array = [];
  3665. append(weather_tile_management.modelArrays,array);
  3666. setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
  3667.  
  3668. var lat = getprop("position/latitude-deg");
  3669. var lon = getprop("position/longitude-deg");
  3670. var type = getprop(lw~"tmp/layer-type");
  3671. var rx = getprop(lw~"tmp/layer-rx") * 1000.0;
  3672. var ry = getprop(lw~"tmp/layer-ry") * 1000.0;
  3673. var phi = getprop(lw~"tmp/layer-phi");
  3674. var alt = getprop(lw~"tmp/layer-alt");
  3675. var thick = getprop(lw~"tmp/layer-thickness");
  3676. var density = getprop(lw~"tmp/layer-density");
  3677. var edge = getprop(lw~"tmp/layer-edge");
  3678. var rain_flag = getprop(lw~"tmp/layer-rain-flag");
  3679. var rain_density = getprop(lw~"tmp/layer-rain-density");
  3680.  
  3681. create_layer(type, lat, lon, alt, thick, rx, ry, phi, density, edge, rain_flag, rain_density);
  3682.  
  3683. }
  3684.  
  3685. var box_wrapper = func {
  3686.  
  3687. thread_flag = 0;
  3688. dynamics_flag = 0;
  3689. presampling_flag = 0;
  3690.  
  3691.  
  3692. setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
  3693.  
  3694. var lat = getprop("position/latitude-deg");
  3695. var lon = getprop("position/longitude-deg");
  3696. var alt = getprop("position/altitude-ft");
  3697. var x = getprop(lw~"tmp/box-x-m");
  3698. var y = getprop(lw~"tmp/box-y-m");
  3699. var z = getprop(lw~"tmp/box-alt-ft");
  3700. var n = getprop(lw~"tmp/box-n");
  3701. var f_core = getprop(lw~"tmp/box-core-fraction");
  3702. var r_core = getprop(lw~"tmp/box-core-offset");
  3703. var h_core = getprop(lw~"tmp/box-core-height");
  3704. var n_core = getprop(lw~"tmp/box-core-n");
  3705. var f_bottom = getprop(lw~"tmp/box-bottom-fraction");
  3706. var h_bottom = getprop(lw~"tmp/box-bottom-thickness");
  3707. var n_bottom = getprop(lw~"tmp/box-bottom-n");
  3708.  
  3709. var type = "Box_test";
  3710.  
  3711.  
  3712.  
  3713. create_cloudbox(type, lat, lon, alt, x,y,z,n, f_core, r_core, h_core, n_core, f_bottom, h_bottom, n_bottom);
  3714.  
  3715. }
  3716.  
  3717.  
  3718. var set_aloft_wrapper = func {
  3719.  
  3720.  
  3721.  
  3722. var lat = getprop(lw~"tmp/ipoint-latitude-deg");
  3723. var lon = getprop(lw~"tmp/ipoint-longitude-deg");
  3724.  
  3725. var d0 = getprop(lw~"tmp/FL0-wind-from-heading-deg");
  3726. var v0 = getprop(lw~"tmp/FL0-windspeed-kt");
  3727.  
  3728. var d1 = getprop(lw~"tmp/FL50-wind-from-heading-deg");
  3729. var v1 = getprop(lw~"tmp/FL50-windspeed-kt");
  3730.  
  3731. var d2 = getprop(lw~"tmp/FL100-wind-from-heading-deg");
  3732. var v2 = getprop(lw~"tmp/FL100-windspeed-kt");
  3733.  
  3734. var d3 = getprop(lw~"tmp/FL180-wind-from-heading-deg");
  3735. var v3 = getprop(lw~"tmp/FL180-windspeed-kt");
  3736.  
  3737. var d4 = getprop(lw~"tmp/FL240-wind-from-heading-deg");
  3738. var v4 = getprop(lw~"tmp/FL240-windspeed-kt");
  3739.  
  3740. var d5 = getprop(lw~"tmp/FL300-wind-from-heading-deg");
  3741. var v5 = getprop(lw~"tmp/FL300-windspeed-kt");
  3742.  
  3743. var d6 = getprop(lw~"tmp/FL340-wind-from-heading-deg");
  3744. var v6 = getprop(lw~"tmp/FL340-windspeed-kt");
  3745.  
  3746. var d7 = getprop(lw~"tmp/FL390-wind-from-heading-deg");
  3747. var v7 = getprop(lw~"tmp/FL390-windspeed-kt");
  3748.  
  3749. var d8 = getprop(lw~"tmp/FL450-wind-from-heading-deg");
  3750. var v8 = getprop(lw~"tmp/FL450-windspeed-kt");
  3751.  
  3752. set_wind_ipoint(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8);
  3753.  
  3754. if (wind_model_flag == 5)
  3755. {setprop(lwi~"ipoint-number", getprop(lwi~"ipoint-number") + 1);}
  3756.  
  3757. }
  3758.  
  3759. ####################################
  3760. # tile setup call wrapper
  3761. ####################################
  3762.  
  3763. var set_tile = func {
  3764.  
  3765. # check if another instance of local weather is running already
  3766.  
  3767.  
  3768. if (local_weather_running_flag == 1)
  3769. {
  3770. setprop("/sim/messages/pilot", "Local weather: Local weather is already running, use Clear/End before restarting. Aborting...");
  3771. return;
  3772. }
  3773.  
  3774. local_weather_startup_flag = 1;
  3775.  
  3776. # randomize high ice scattering properties
  3777.  
  3778. setprop("/environment/scattering-phenomena/ring-factor", rand());
  3779. setprop("/environment/scattering-phenomena/rainbow-factor", rand());
  3780.  
  3781.  
  3782. var type = getprop("/local-weather/tmp/tile-type");
  3783.  
  3784. # set tile center coordinates to current position
  3785.  
  3786. var lat = getprop("position/latitude-deg");
  3787. var lon = getprop("position/longitude-deg");
  3788.  
  3789. setprop(lw~"tiles/tmp/latitude-deg",lat);
  3790. setprop(lw~"tiles/tmp/longitude-deg",lon);
  3791. setprop(lw~"tiles/tmp/dir-index",4);
  3792.  
  3793. readFlags();
  3794.  
  3795. # check consistency of flags
  3796.  
  3797. if (dynamical_convection_flag == 1)
  3798. {
  3799. if (dynamics_flag == 0)
  3800. {
  3801. print("Dynamical convection needs weather dynamics to run! Aborting...");
  3802. setprop("/sim/messages/pilot", "Local weather: dynamical convection needs weather dynamics to run! Aborting...");
  3803. return;
  3804. }
  3805. if (presampling_flag == 0)
  3806. {
  3807. print("Dynamical convection needs terrain presampling to run! Aborting...");
  3808. setprop("/sim/messages/pilot", "Local weather: dynamical convection needs terrain presampling to run! Aborting...");
  3809. return;
  3810. }
  3811. }
  3812.  
  3813. if (detailed_terrain_interaction_flag == 1)
  3814. {
  3815. if (presampling_flag == 0)
  3816. {
  3817. print("Terrain effect needs terrain presampling to run! Aborting...");
  3818. setprop("/sim/messages/pilot", "Local weather: terrain effect needs terrain presampling to run! Aborting...");
  3819. return;
  3820. }
  3821. }
  3822.  
  3823.  
  3824. # if we can do so, we switch global weather and METAR parsing in environment off at this point
  3825.  
  3826. if (compat_layer.features.can_disable_environment ==1)
  3827. {
  3828. props.globals.getNode("/environment/config/enabled").setBoolValue(0);
  3829. props.globals.getNode("/environment/params/metar-updates-environment").setBoolValue(0);
  3830. }
  3831.  
  3832.  
  3833. # switch off normal 3d clouds
  3834.  
  3835. local_weather.setDefaultCloudsOff();
  3836.  
  3837. # read max. visibility range and set far camera clipping
  3838.  
  3839. max_vis_range = math.exp(getprop(lw~"config/aux-max-vis-range-m"));
  3840. setprop(lw~"config/max-vis-range-m",max_vis_range);
  3841. if (max_vis_range>120000.0){setprop("/sim/rendering/camera-group/zfar",max_vis_range);}
  3842.  
  3843. # now see if we need to presample the terrain
  3844.  
  3845. if ((presampling_flag == 1) and (getprop(lw~"tmp/presampling-status") == "idle"))
  3846. {
  3847. terrain_presampling_start(lat, lon, 1000, 40000, getprop(lw~"tmp/tile-orientation-deg"));
  3848. return;
  3849. }
  3850.  
  3851.  
  3852. # indicate that we're up and running
  3853.  
  3854. local_weather_startup_flag = 0;
  3855. local_weather_running_flag = 1;
  3856.  
  3857. # see if we use METAR for weather setup
  3858.  
  3859. if ((getprop("/environment/metar/valid") == 1) and (getprop(lw~"tmp/tile-management") == "METAR"))
  3860. {
  3861. type = "METAR";
  3862. metar_flag = 1;
  3863.  
  3864. setprop(lw~"METAR/station-id","METAR");
  3865.  
  3866.  
  3867.  
  3868. }
  3869. else if ((getprop("/environment/metar/valid") == 0) and (getprop(lw~"tmp/tile-management") == "METAR"))
  3870. {
  3871. print("No METAR available, aborting...");
  3872. setprop("/sim/messages/pilot", "Local weather: No METAR available! Aborting...");
  3873. return;
  3874. }
  3875.  
  3876.  
  3877. # see if we need to create an aloft wind interpolation structure
  3878.  
  3879. set_wind_model_flag();
  3880.  
  3881.  
  3882. if ((wind_model_flag == 3) or ((wind_model_flag ==5) and (getprop(lwi~"ipoint-number") == 0)))
  3883. {
  3884. if (metar_flag != 1)
  3885. {set_aloft_wrapper();}
  3886. }
  3887.  
  3888.  
  3889. # prepare the first tile wind field
  3890.  
  3891. if (metar_flag == 1) # the winds from current METAR are used
  3892. {
  3893.  
  3894. # METAR reports ground winds, we want to set aloft, so we need to compute the local boundary layer
  3895. # need to set the tile index for this
  3896. setprop(lw~"tiles/tile[4]/tile-index",1);
  3897.  
  3898. var boundary_correction = 1.0/get_slowdown_fraction();
  3899. var metar_base_wind_deg = getprop("environment/metar/base-wind-dir-deg");
  3900. var metar_base_wind_speed = boundary_correction * getprop("environment/metar/base-wind-speed-kt");
  3901.  
  3902. # set the wind hash for the new scheme
  3903.  
  3904. wind.cloudlayer = [metar_base_wind_deg,metar_base_wind_speed];
  3905. wind.surface = [metar_base_wind_deg,metar_base_wind_speed/boundary_correction];
  3906. wind.current = wind.surface;
  3907.  
  3908.  
  3909. if ((wind_model_flag == 1) or (wind_model_flag == 2))
  3910. {
  3911. append(weather_dynamics.tile_wind_direction, metar_base_wind_deg);
  3912. append(weather_dynamics.tile_wind_speed, metar_base_wind_speed);
  3913. setprop(lw~"tmp/tile-orientation-deg",metar_base_wind_deg);
  3914. }
  3915. else if (wind_model_flag == 5)
  3916. {
  3917. var station_lat = getprop("/environment/metar/station-latitude-deg");
  3918. var station_lon = getprop("/environment/metar/station-longitude-deg");
  3919.  
  3920. set_wind_ipoint_metar(station_lat, station_lon, metar_base_wind_deg, metar_base_wind_speed);
  3921.  
  3922. var res = wind_interpolation(lat,lon,0.0);
  3923.  
  3924.  
  3925.  
  3926. append(weather_dynamics.tile_wind_direction,res[0]);
  3927. append(weather_dynamics.tile_wind_speed,res[1]);
  3928. setprop(lw~"tmp/tile-orientation-deg", weather_dynamics.tile_wind_direction[0]);
  3929.  
  3930. # in case of gusty winds, these need to be re-initialized to the base wind
  3931. # from METAR rather than the menu
  3932. interpolated_conditions.wind_from_heading_deg = metar_base_wind_deg;
  3933. interpolated_conditions.windspeed_kt = metar_base_wind_speed;
  3934. }
  3935. else
  3936. {
  3937. print("Wind model currently not supported with live data!");
  3938. setprop("/sim/messages/pilot", "Local weather: Wind model currently not supported with live data! Aborting...");
  3939. return;
  3940. }
  3941. }
  3942. else
  3943. {
  3944. setprop(lw~"tiles/tile[4]/tile-index",1);
  3945. var boundary_correction = get_slowdown_fraction();
  3946.  
  3947. if (wind_model_flag == 5) # it needs to be interpolated
  3948. {
  3949. var res = wind_interpolation(lat,lon,0.0);
  3950.  
  3951. append(weather_dynamics.tile_wind_direction,res[0]);
  3952. append(weather_dynamics.tile_wind_speed,res[1]);
  3953.  
  3954. # set the wind hash for the new scheme
  3955.  
  3956. wind.surface = [res[0],res[1] * boundary_correction];
  3957. wind.cloudlayer = res;
  3958. wind.current = wind.surface;
  3959.  
  3960.  
  3961. }
  3962. else if (wind_model_flag == 3) # it comes from a different menu
  3963. {
  3964. append(weather_dynamics.tile_wind_direction, getprop(lw~"tmp/FL0-wind-from-heading-deg"));
  3965. append(weather_dynamics.tile_wind_speed, getprop(lw~"tmp/FL0-windspeed-kt"));
  3966.  
  3967. # set the wind hash for the new scheme
  3968. wind.cloudlayer = [getprop(lw~"tmp/FL0-wind-from-heading-deg"),getprop(lw~"tmp/FL0-windspeed-kt") ];
  3969. wind.surface = [wind.cloudlayer[0],wind.cloudlayer[1] * boundary_correction];
  3970. wind.current = wind.surface;
  3971.  
  3972. }
  3973. else # it comes from the standard menu
  3974. {
  3975. append(weather_dynamics.tile_wind_direction, getprop(lw~"tmp/tile-orientation-deg"));
  3976. append(weather_dynamics.tile_wind_speed, getprop(lw~"tmp/windspeed-kt"));
  3977.  
  3978. # set the wind hash for the new scheme
  3979. wind.cloudlayer = [getprop(lw~"tmp/tile-orientation-deg"),getprop(lw~"tmp/windspeed-kt")];
  3980. wind.surface = [wind.cloudlayer[0],wind.cloudlayer[1] * boundary_correction];
  3981. wind.current = wind.surface;
  3982.  
  3983. }
  3984.  
  3985. # when the aloft wind menu is used, the lowest winds should be taken from there
  3986. # so we need to overwrite the setting from the tile generating menu in this case
  3987. # otherwise the wrong orientation is built
  3988.  
  3989.  
  3990. if (wind_model_flag ==3)
  3991. {
  3992. setprop(lw~"tmp/tile-orientation-deg", getprop(lw~"tmp/FL0-wind-from-heading-deg"));
  3993. }
  3994. else if (wind_model_flag == 5)
  3995. {
  3996. setprop(lw~"tmp/tile-orientation-deg", weather_dynamics.tile_wind_direction[0]);
  3997. }
  3998. }
  3999.  
  4000. # create all the neighbouring tile coordinate sets
  4001.  
  4002. weather_tile_management.create_neighbours(lat,lon,getprop(lw~"tmp/tile-orientation-deg"));
  4003.  
  4004.  
  4005.  
  4006.  
  4007. setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
  4008.  
  4009.  
  4010. # see if we need to generate a quadtree structure for clouds
  4011.  
  4012. if (dynamics_flag ==1)
  4013. {
  4014. var quadtree = [];
  4015. weather_dynamics.generate_quadtree_structure(0, quadtree);
  4016. append(weather_dynamics.cloudQuadtrees,quadtree);
  4017. }
  4018.  
  4019.  
  4020.  
  4021.  
  4022. if (type == "High-pressure-core")
  4023. {weather_tiles.set_high_pressure_core_tile();}
  4024. else if (type == "High-pressure")
  4025. {weather_tiles.set_high_pressure_tile();}
  4026. else if (type == "High-pressure-border")
  4027. {weather_tiles.set_high_pressure_border_tile();}
  4028. else if (type == "Low-pressure-border")
  4029. {weather_tiles.set_low_pressure_border_tile();}
  4030. else if (type == "Low-pressure")
  4031. {weather_tiles.set_low_pressure_tile();}
  4032. else if (type == "Low-pressure-core")
  4033. {weather_tiles.set_low_pressure_core_tile();}
  4034. else if (type == "Cold-sector")
  4035. {weather_tiles.set_cold_sector_tile();}
  4036. else if (type == "Warm-sector")
  4037. {weather_tiles.set_warm_sector_tile();}
  4038. else if (type == "Tropical")
  4039. {weather_tiles.set_tropical_weather_tile();}
  4040. else if (type == "Coldfront")
  4041. {weather_tiles.set_coldfront_tile();}
  4042. else if (type == "Warmfront")
  4043. {weather_tiles.set_warmfront1_tile();}
  4044. else if (type == "Warmfront-2")
  4045. {weather_tiles.set_warmfront2_tile();}
  4046. else if (type == "Warmfront-3")
  4047. {weather_tiles.set_warmfront3_tile();}
  4048. else if (type == "Warmfront-4")
  4049. {weather_tiles.set_warmfront4_tile();}
  4050. else if (type == "Thunderstorms")
  4051. {weather_tiles.set_thunderstorms_tile();}
  4052. else if (type == "METAR")
  4053. {weather_tiles.set_METAR_tile();}
  4054. else if (type == "Altocumulus sky")
  4055. {weather_tiles.set_altocumulus_tile();setprop(lw~"tiles/code","altocumulus_sky");}
  4056. else if (type == "Broken layers")
  4057. {weather_tiles.set_broken_layers_tile();setprop(lw~"tiles/code","broken_layers");}
  4058. else if (type == "Cold front")
  4059. {weather_tiles.set_coldfront_tile();setprop(lw~"tiles/code","coldfront");}
  4060. else if (type == "Cirrus sky")
  4061. {weather_tiles.set_cirrus_sky_tile();setprop(lw~"tiles/code","cirrus_sky");}
  4062. else if (type == "Fair weather")
  4063. {setprop(lw~"tiles/code","cumulus_sky");weather_tiles.set_fair_weather_tile();}
  4064. else if (type == "Glider's sky")
  4065. {setprop(lw~"tiles/code","gliders_sky");weather_tiles.set_gliders_sky_tile();}
  4066. else if (type == "Blue thermals")
  4067. {setprop(lw~"tiles/code","blue_thermals");weather_tiles.set_blue_thermals_tile();}
  4068. else if (type == "Incoming rainfront")
  4069. {weather_tiles.set_rainfront_tile();setprop(lw~"tiles/code","rainfront");}
  4070. else if (type == "8/8 stratus sky")
  4071. {weather_tiles.set_overcast_stratus_tile();setprop(lw~"tiles/code","overcast_stratus");}
  4072. else if (type == "Test tile")
  4073. {weather_tiles.set_4_8_stratus_tile();setprop(lw~"tiles/code","test");}
  4074. else if (type == "Summer rain")
  4075. {weather_tiles.set_summer_rain_tile();setprop(lw~"tiles/code","summer_rain");}
  4076. else
  4077. {print("Tile not implemented.");setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")-1);return();}
  4078.  
  4079.  
  4080. # mark tile as active
  4081.  
  4082. append(weather_tile_management.active_tile_list,1);
  4083.  
  4084. # start tile management loop if needed
  4085.  
  4086. if (getprop(lw~"tmp/tile-management") != "single tile") {
  4087. if (getprop(lw~"tile-loop-flag") == 0)
  4088. {
  4089. setprop(lw~"tiles/tile[4]/code",getprop(lw~"tiles/code"));
  4090. setprop(lw~"tile-loop-flag",1);
  4091. weather_tile_management.tile_management_loop();}
  4092. }
  4093.  
  4094. # start the interpolation loop
  4095.  
  4096. if (getprop(lw~"interpolation-loop-flag") == 0)
  4097. {setprop(lw~"interpolation-loop-flag",1); local_weather.interpolation_loop();}
  4098.  
  4099. # start the effect volume loop
  4100.  
  4101. if (getprop(lw~"effect-loop-flag") == 0)
  4102. {setprop(lw~"effect-loop-flag",1); local_weather.effect_volume_loop(0,0);}
  4103.  
  4104. # start weather dynamics loops if needed
  4105.  
  4106. if (getprop(lw~"timing-loop-flag") == 0)
  4107. {setprop(lw~"timing-loop-flag",1); local_weather.timing_loop();}
  4108.  
  4109. if (dynamics_flag ==1)
  4110. {
  4111.  
  4112.  
  4113. if (getprop(lw~"dynamics-loop-flag") == 0)
  4114. {
  4115. setprop(lw~"dynamics-loop-flag",1);
  4116. # weather_dynamics.quadtree_loop();
  4117. weather_dynamics.weather_dynamics_loop(0,0);
  4118. }
  4119. if ((getprop(lw~"convective-loop-flag") == 0) and (getprop(lw~"config/dynamical-convection-flag") ==1))
  4120. {
  4121. setprop(lw~"convective-loop-flag",1);
  4122. weather_dynamics.convective_loop();
  4123. }
  4124. }
  4125.  
  4126.  
  4127.  
  4128.  
  4129. # and start the buffer loop and housekeeping loop if needed
  4130.  
  4131. if (buffer_flag == 1)
  4132. {
  4133. if (getprop(lw~"buffer-loop-flag") == 0)
  4134. {
  4135. # setprop(lw~"buffer-loop-flag",1); weather_tile_management.buffer_loop(0);
  4136. setprop(lw~"housekeeping-loop-flag",1); weather_tile_management.housekeeping_loop(0,0);
  4137. }
  4138. }
  4139.  
  4140. # start the sea color loop
  4141. local_weather.init_sea_colors();
  4142.  
  4143. # start the mask loop
  4144. #local_weather.init_mask();
  4145.  
  4146. # create impostors - this should only happen when sufficiently high in air
  4147. weather_tile_management.create_impostors();
  4148.  
  4149. # start the cloud shadow loop
  4150.  
  4151. local_weather.cloud_shadow_flag = getprop("/local-weather/config/generate-cloud-shadows");
  4152.  
  4153. if (local_weather.cloud_shadow_flag == 1)
  4154. {
  4155. setprop(lw~"shadow-loop-flag",1);
  4156. weather_tile_management.shadow_management_loop(0);
  4157. }
  4158.  
  4159. #weather_tile_management.watchdog_loop();
  4160.  
  4161. # start thunderstorm management
  4162.  
  4163. setprop(lw~"thunderstorm-loop-flag",1);
  4164.  
  4165. local_weather.place_model_controlled("lightning", "Models/Weather/lightning_combined.xml", lat, lon, 0.0, 0.0, 0.0, 0.0);
  4166.  
  4167. local_weather.thunderstorm_management_loop();
  4168.  
  4169. }
  4170.  
  4171.  
  4172. #################################################
  4173. # Anything that needs to run at startup goes here
  4174. #################################################
  4175.  
  4176. var startup = func {
  4177. print("Loading local weather routines...");
  4178.  
  4179. # get local Cartesian geometry
  4180.  
  4181. var lat = getprop("position/latitude-deg");
  4182. var lon = getprop("position/longitude-deg");
  4183. calc_geo(lat);
  4184.  
  4185.  
  4186. # copy weather properties at startup to local weather
  4187.  
  4188.  
  4189. interpolated_conditions.visibility_m = getprop(ec~"boundary/entry[0]/visibility-m");
  4190. interpolated_conditions.pressure_sea_level_inhg = getprop(ec~"boundary/entry[0]/pressure-sea-level-inhg");
  4191. interpolated_conditions.temperature_degc = getprop(ec~"boundary/entry[0]/temperature-degc");
  4192. interpolated_conditions.dewpoint_degc = getprop(ec~"boundary/entry[0]/dewpoint-degc");
  4193. interpolated_conditions.wind_from_heading_deg = getprop(ec~"boundary/entry[0]/wind-from-heading-deg");
  4194. interpolated_conditions.wind_speed_kt = getprop(ec~"boundary/entry[0]/wind-speed-kt");
  4195. interpolated_conditions.turbulence = getprop(ec~"boundary/entry[0]/turbulence/magnitude-norm");
  4196. interpolated_conditions.rain_norm = 0.0;
  4197. interpolated_conditions.snow_norm = 0.0;
  4198. interpolated_conditions.thermal_lift = 0.0;
  4199.  
  4200.  
  4201. # before interpolation starts, these are also initially current
  4202.  
  4203. setprop(lw~"current/visibility-m",interpolated_conditions.visibility_m);
  4204. setprop(lw~"current/rain-norm",0.0);
  4205. setprop(lw~"current/snow-norm",0.0);
  4206. setprop(lw~"current/thermal-lift", 0.0);
  4207. setprop(lw~"current/turbulence",interpolated_conditions.turbulence);
  4208.  
  4209.  
  4210. # create default properties for METAR system, should be overwritten by real-weather-fetch
  4211.  
  4212. setprop(lw~"METAR/latitude-deg",lat);
  4213. setprop(lw~"METAR/longitude-deg",lon);
  4214. setprop(lw~"METAR/altitude-ft",0.0);
  4215. setprop(lw~"METAR/wind-direction-deg",0.0);
  4216. setprop(lw~"METAR/wind-strength-kt",10.0);
  4217. setprop(lw~"METAR/visibility-m",17000.0);
  4218. setprop(lw~"METAR/rain-norm",0.0);
  4219. setprop(lw~"METAR/snow-norm",0.0);
  4220. setprop(lw~"METAR/temperature-degc",10.0);
  4221. setprop(lw~"METAR/dewpoint-degc",7.0);
  4222. setprop(lw~"METAR/pressure-inhg",29.92);
  4223. setprop(lw~"METAR/thunderstorm-flag",0);
  4224. setprop(lw~"METAR/layer[0]/cover-oct",4);
  4225. setprop(lw~"METAR/layer[0]/alt-agl-ft", 3000.0);
  4226. setprop(lw~"METAR/layer[1]/cover-oct",0);
  4227. setprop(lw~"METAR/layer[1]/alt-agl-ft", 20000.0);
  4228. setprop(lw~"METAR/layer[2]/cover-oct",0);
  4229. setprop(lw~"METAR/layer[2]/alt-agl-ft", 20000.0);
  4230. setprop(lw~"METAR/layer[3]/cover-oct",0);
  4231. setprop(lw~"METAR/layer[3]/alt-agl-ft", 20000.0);
  4232. setprop(lw~"METAR/available-flag",1);
  4233.  
  4234.  
  4235.  
  4236.  
  4237. # set listeners
  4238.  
  4239. setlistener(lw~"tmp/thread-status", func {var s = size(clouds_path); compat_layer.create_cloud_array(s, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation); });
  4240. setlistener(lw~"tmp/convective-status", func {var s = size(clouds_path); compat_layer.create_cloud_array(s, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation); });
  4241. setlistener(lw~"tmp/effect-thread-status", func {var s = size(effects_geo); effect_placement_loop(s); });
  4242. setlistener(lw~"tmp/presampling-status", func {manage_presampling(); });
  4243.  
  4244. #setlistener(lw~"config/wind-model", func {set_wind_model_flag();});
  4245. setlistener(lw~"config/thermal-properties", func {set_texture_mix();});
  4246.  
  4247. setlistener(lw~"config/clouds-in-dynamics-loop", func(n) {weather_dynamics.max_clouds_in_loop = int(n.getValue());});
  4248.  
  4249. setlistener(lw~"config/clouds-visible-range-m", func(n) {weather_tile_management.cloud_view_distance = n.getValue();});
  4250. setlistener(lw~"config/distance-to-load-tile-m", func(n) {setprop(lw~"config/distance-to-remove-tile-m",n.getValue() + 500.0);});
  4251.  
  4252. setlistener(lw~"config/fps-control-flag", func(n) {fps_control_flag = n.getValue();});
  4253. setlistener(lw~"config/target-framerate", func(n) {target_framerate = n.getValue();});
  4254.  
  4255. setlistener(lw~"config/small-scale-persistence", func(n) {weather_tiles.small_scale_persistence = n.getValue();});
  4256. setlistener(lw~"config/ground-haze-factor", func(n) {ground_haze_factor = n.getValue();});
  4257. setlistener(lw~"config/aux-max-vis-range-m", func(n) {
  4258. max_vis_range = math.exp(n.getValue());
  4259. setprop(lw~"config/max-vis-range-m",max_vis_range);
  4260. if (max_vis_range>120000.0){setprop("/sim/rendering/camera-group/zfar",max_vis_range);}
  4261. });
  4262.  
  4263.  
  4264. setlistener(lw~"config/temperature-offset-degc", func(n) {temperature_offset = n.getValue();});
  4265.  
  4266. setlistener("/environment/air-pollution-norm", func(n) {air_pollution_norm = n.getValue() ;});
  4267.  
  4268. setlistener("/sim/rendering/shaders/skydome", func(n) {scattering_shader_flag = n.getValue() ; if (scattering_shader_flag ==1) {setprop("/sim/rendering/minimum-sky-visibility",0.0);} else {setprop("/sim/rendering/minimum-sky-visibility",1000.0);} });
  4269. }
  4270.  
  4271.  
  4272. #####################################################
  4273. # Standard test call (for development and debug only)
  4274. #####################################################
  4275.  
  4276. var test = func {
  4277.  
  4278. var lat = getprop("position/latitude-deg");
  4279. var lon = getprop("position/longitude-deg");
  4280. var alt = getprop("position/altitude-ft");
  4281.  
  4282. # thread_flag = 0;
  4283. # dynamics_flag = 0;
  4284. # presampling_flag = 0;
  4285.  
  4286.  
  4287. #if (compat_layer.features.can_disable_environment ==1)
  4288. # {
  4289. # props.globals.getNode("/environment/config/enabled").setBoolValue(0);
  4290. # props.globals.getNode("/environment/params/metar-updates-environment").setBoolValue(0);
  4291. # }
  4292. #
  4293. #compat_layer.setDefaultCloudsOff();
  4294.  
  4295. #var array = [];
  4296. #append(weather_tile_management.modelArrays,array);
  4297. #setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
  4298.  
  4299.  
  4300. #var pos = geo.aircraft_position();
  4301.  
  4302. debug.dump(geodinfo(lat, lon));
  4303.  
  4304.  
  4305.  
  4306. #var info = {};
  4307.  
  4308. #for (var i = 0; i< 100000; i=i+1)
  4309. # {
  4310. # info = geodinfo(lat, lon);
  4311. # }
  4312.  
  4313.  
  4314. }
  4315.  
  4316.  
  4317.  
  4318. #################################################################
  4319. # object classes
  4320. #################################################################
  4321.  
  4322. var weatherStation = {
  4323. new: func (lat, lon, alt, vis, T, D, p) {
  4324. var s = { parents: [weatherStation] };
  4325. s.lat = lat;
  4326. s.lon = lon;
  4327. s.alt = alt;
  4328. s.vis = vis;
  4329. s.T = T;
  4330. s.D = D;
  4331. s.p = p;
  4332. s.scattering = 0.8;
  4333. return s;
  4334. },
  4335. move: func {
  4336. var windfield = weather_dynamics.get_windfield(me.index);
  4337. var dt = weather_dynamics.time_lw - me.timestamp;
  4338. me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
  4339. me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
  4340. me.timestamp = weather_dynamics.time_lw;
  4341. },
  4342. };
  4343.  
  4344.  
  4345. var atmosphereIpoint = {
  4346. new: func (lat, lon, vis_aloft, vis_alt1, vis_ovcst, ovcst, ovcst_alt_low, ovcst_alt_high, scatt, scatt_alt_low, scatt_alt_high){
  4347. var a = { parents: [atmosphereIpoint] };
  4348. a.lat = lat;
  4349. a.lon = lon;
  4350. a.vis_aloft = vis_aloft;
  4351. a.vis_alt1 = vis_alt1;
  4352. a.vis_ovcst = vis_ovcst;
  4353. a.ovcst = ovcst;
  4354. a.ovcst_alt_low = ovcst_alt_low;
  4355. a.ovcst_alt_high = ovcst_alt_high;
  4356. a.scatt = scatt;
  4357. a.scatt_alt_low = scatt_alt_low;
  4358. a.scatt_alt_high = scatt_alt_high;
  4359. return a;
  4360. },
  4361. move: func {
  4362. var windfield = weather_dynamics.get_windfield(me.index);
  4363. var dt = weather_dynamics.time_lw - me.timestamp;
  4364. me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
  4365. me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
  4366. me.timestamp = weather_dynamics.time_lw;
  4367. },
  4368. };
  4369.  
  4370.  
  4371. var windIpoint = {
  4372. new: func (lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8) {
  4373. var w = { parents: [windIpoint] };
  4374. w.lat = lat;
  4375. w.lon = lon;
  4376.  
  4377. altvec = [];
  4378. var wv = nil;
  4379.  
  4380. wv = windVec.new(d0, v0);
  4381. append(altvec, wv);
  4382.  
  4383. wv = windVec.new(d1, v1);
  4384. append(altvec, wv);
  4385.  
  4386. wv = windVec.new(d2, v2);
  4387. append(altvec, wv);
  4388.  
  4389. wv = windVec.new(d3, v3);
  4390. append(altvec, wv);
  4391.  
  4392. wv = windVec.new(d4, v4);
  4393. append(altvec, wv);
  4394.  
  4395. wv = windVec.new(d5, v5);
  4396. append(altvec, wv);
  4397.  
  4398. wv = windVec.new(d6, v6);
  4399. append(altvec, wv);
  4400.  
  4401. wv = windVec.new(d7, v7);
  4402. append(altvec, wv);
  4403.  
  4404. wv = windVec.new(d8, v8);
  4405. append(altvec, wv);
  4406.  
  4407. w.alt = altvec;
  4408.  
  4409. w.weight = 0.02;
  4410. return w;
  4411. },
  4412. };
  4413.  
  4414. var windVec = {
  4415. new: func (d, v) {
  4416. var wv = { parents: [windVec] };
  4417. wv.d = d;
  4418. wv.v = v;
  4419. return wv;
  4420. },
  4421.  
  4422. };
  4423.  
  4424.  
  4425.  
  4426.  
  4427.  
  4428. var effectVolume = {
  4429. new: func (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat) {
  4430. var e = { parents: [effectVolume] };
  4431. e.geometry = geometry;
  4432. e.lat = lat;
  4433. e.lon = lon;
  4434. e.r1 = r1;
  4435. e.r2 = r2;
  4436. e.phi = phi;
  4437. e.alt_low = alt_low;
  4438. e.alt_high = alt_high;
  4439. e.vis = vis;
  4440. e.rain = rain;
  4441. e.snow = snow;
  4442. e.turb = turb;
  4443. e.lift = lift;
  4444. e.lift_flag = lift_flag;
  4445. e.sat = sat;
  4446. return e;
  4447. },
  4448. move: func {
  4449. var windfield = weather_dynamics.get_windfield(me.index);
  4450. var dt = weather_dynamics.time_lw - me.timestamp;
  4451. me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
  4452. me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
  4453. me.timestamp = weather_dynamics.time_lw;
  4454. },
  4455. correct_altitude: func {
  4456. var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + alt_20_array[me.index-1];
  4457. var elevation = compat_layer.get_elevation(me.lat, me.lon);
  4458. me.alt_high = local_weather.get_convective_altitude(convective_alt, elevation, me.index,0.0) *1.15;
  4459. me.height = me.alt_high * 0.87;
  4460. },
  4461. correct_altitude_and_age: func {
  4462. var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1];
  4463. var elevation = -1.0; var p_cover = 0.2;
  4464. var info = geodinfo(me.lat, me.lon);
  4465. if (info != nil)
  4466. {
  4467. elevation = info[0] * local_weather.m_to_ft;
  4468. if (info[1] != nil)
  4469. {
  4470. var landcover = info[1].names[0];
  4471. if (contains(landcover_map,landcover)) {p_cover = landcover_map[landcover];}
  4472. else {p_cover = 0.2;}
  4473. }
  4474. }
  4475. me.alt_high = get_convective_altitude(convective_alt, elevation, me.index,0.0) * 1.15;
  4476. me.height = me.alt_high * 0.87;
  4477. var current_lifetime = math.sqrt(p_cover)/math.sqrt(0.35) * weather_dynamics.cloud_convective_lifetime_s;
  4478. var fractional_increase = (weather_dynamics.time_lw - me.evolution_timestamp)/current_lifetime;
  4479. me.flt = me.flt + fractional_increase;
  4480. me.evolution_timestamp = weather_dynamics.time_lw;
  4481. },
  4482. get_distance: func {
  4483. var lat = getprop("position/latitude-deg");
  4484. var lon = getprop("position/longitude-deg");
  4485. return math.sqrt(calc_d_sq(lat, lon, me.lat, me.lon));
  4486. },
  4487. };
  4488.  
  4489.  
  4490. var thermalLift = {
  4491. new: func (lat, lon, radius, height, cn, sh, max_lift, f_lift_radius) {
  4492. var l = { parents: [thermalLift] };
  4493. l.lat = lat;
  4494. l.lon = lon;
  4495. l.radius = radius;
  4496. l.height = height;
  4497. l.cn = cn;
  4498. l.sh = sh;
  4499. l.max_lift = max_lift;
  4500. l.f_lift_radius = f_lift_radius;
  4501. return l;
  4502. },
  4503. move: func {
  4504. var windfield = weather_dynamics.get_windfield(me.index);
  4505. var dt = weather_dynamics.time_lw - me.timestamp;
  4506. me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
  4507. me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
  4508. me.timestamp = weather_dynamics.time_lw;
  4509. },
  4510. correct_altitude: func {
  4511. var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + alt_20_array[me.index-1];
  4512. var elevation = compat_layer.get_elevation(me.lat, me.lon);
  4513. me.height = local_weather.get_convective_altitude(convective_alt, elevation, me.index,0.0);
  4514. },
  4515. correct_altitude_and_age: func {
  4516. var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1];
  4517. var elevation = -1.0; var p_cover = 0.2;
  4518. var info = geodinfo(me.lat, me.lon);
  4519. if (info != nil)
  4520. {
  4521. elevation = info[0] * local_weather.m_to_ft;
  4522. if (info[1] != nil)
  4523. {
  4524. var landcover = info[1].names[0];
  4525. if (contains(landcover_map,landcover)) {p_cover = landcover_map[landcover];}
  4526. else {p_cover = 0.2;}
  4527. }
  4528. }
  4529. me.height = get_convective_altitude(convective_alt, elevation, me.index,0.0);
  4530. var current_lifetime = math.sqrt(p_cover)/math.sqrt(0.35) * weather_dynamics.cloud_convective_lifetime_s;
  4531. var fractional_increase = (weather_dynamics.time_lw - me.evolution_timestamp)/current_lifetime;
  4532. me.flt = me.flt + fractional_increase;
  4533. me.evolution_timestamp = weather_dynamics.time_lw;
  4534. },
  4535.  
  4536. };
  4537.  
  4538.  
  4539. var waveLift = {
  4540. new: func (lat, lon, x, y, phi, height, max_lift) {
  4541. var w = { parents: [waveLift] };
  4542. w.lat = lat;
  4543. w.lon = lon;
  4544. w.x = x;
  4545. w.y = y;
  4546. w.phi = phi;
  4547. w.height = height;
  4548. w.max_lift = max_lift;
  4549. w.phi = getprop(lw~"tmp/tile-orientation-deg");
  4550. return w;
  4551. },
  4552.  
  4553. };
  4554.  
  4555.  
  4556.  
  4557. #################################################################
  4558. # global variable, property creation and the startup listener
  4559. #################################################################
  4560.  
  4561. var rad_E = 6378138.12; # earth radius
  4562. var lat_to_m = 110952.0; # latitude degrees to meters
  4563. var m_to_lat = 9.01290648208234e-06; # meters to latitude degrees
  4564. var ft_to_m = 0.30480;
  4565. var m_to_ft = 1.0/ft_to_m;
  4566. var sec_to_rad = 2.0 * math.pi/86400;
  4567.  
  4568. var lon_to_m = 0.0; # needs to be calculated dynamically
  4569. var m_to_lon = 0.0; # we do this on startup
  4570.  
  4571. # some common abbreviations
  4572.  
  4573. var lw = "/local-weather/";
  4574. var lwi = "/local-weather/interpolation/";
  4575. var ec = "/environment/config/";
  4576.  
  4577. # a hash map of the strength for convection associated with terrain types
  4578.  
  4579. var landcover_map = {BuiltUpCover: 0.35, Town: 0.35, Freeway:0.35, BarrenCover:0.3, HerbTundraCover: 0.25, GrassCover: 0.2, CropGrassCover: 0.2, EvergreenBroadCover: 0.2, EvergreenNeedleCover: 0.2, Sand: 0.25, Grass: 0.2, Grassland: 0.2, Ocean: 0.01, Marsh: 0.05, Lake: 0.01, ShrubCover: 0.15, Shrub: 0.15, Landmass: 0.2, CropWoodCover: 0.15, MixedForestCover: 0.15, DryCropPastureCover: 0.25, MixedCropPastureCover: 0.2, MixedCrop: 0.2, ComplexCrop: 0.2, IrrCropPastureCover: 0.15, DeciduousBroadCover: 0.1, DeciduousNeedleCover: 0.1, Bog: 0.05, Littoral: 0.05, pa_taxiway : 0.35, pa_tiedown: 0.35, pc_taxiway: 0.35, pc_tiedown: 0.35, Glacier: 0.03, SnowCover: 0.04, DryLake: 0.3, IntermittentStream: 0.2, DryCrop: 0.2, Lava: 0.3, GolfCourse: 0.2, Rock: 0.3, Construction: 0.35, PackIce: 0.04, NaturalCrop: 0.2, Default: 0.2};
  4580.  
  4581. # a hash map of average vertical cloud model sizes
  4582.  
  4583. var cloud_vertical_size_map = {Altocumulus: 700.0, Cumulus: 600.0, Congestus: 2000.0, Nimbus: 1000.0, Stratus: 800.0, Stratus_structured: 600.0, Stratus_thin: 400.0, Cirrocumulus: 200.0, Cb_box: 2000.0};
  4584.  
  4585. # a hash map of offsets for the new cloud rendering system
  4586.  
  4587. var offset_map = {Nimbus: 2800.0, Stratus: 2000.0, Stratus_thin: 2500.0, Cirrostratus: 4500.0, Stratus_structured: 1800.0, Stratus_alt: 600.0, Cumulus: 200.0, Congestus: 600.0 };
  4588.  
  4589. # the array of aloft wind interpolation altitudes
  4590.  
  4591. var wind_altitude_array = [0.0, 5000.0, 10000.0, 18000.0, 24000.0, 30000.0, 34000.0, 39000.0, 45000.0];
  4592.  
  4593. # storage arrays for cloud generation
  4594.  
  4595. var clouds_path = [];
  4596. var clouds_lat = [];
  4597. var clouds_lon = [];
  4598. var clouds_alt = [];
  4599. var clouds_orientation = [];
  4600.  
  4601.  
  4602.  
  4603.  
  4604. # storage array for assembled clouds
  4605.  
  4606. var cloudAssemblyArray = [];
  4607.  
  4608. # additional info needed for dynamical clouds: the base altitude around which cloudlets are distributed
  4609. # and the fractional lifetime
  4610.  
  4611. var clouds_mean_alt = [];
  4612. var clouds_flt = [];
  4613. var clouds_evolution_timestamp = [];
  4614.  
  4615.  
  4616. # storage arrays for terrain presampling and results by tile
  4617.  
  4618. var terrain_n = [];
  4619. var alt_50_array = [];
  4620. var alt_20_array = [];
  4621. var alt_min_array = [];
  4622. var alt_mean_array = [];
  4623.  
  4624. # array of currently existing effect volumes
  4625.  
  4626. var effectVolumeArray = [];
  4627. var n_effectVolumeArray = 0;
  4628.  
  4629. # global weather hashes
  4630.  
  4631. var thermal = {};
  4632. var wave = {};
  4633. var interpolated_conditions = {};
  4634. var current_conditions = {};
  4635. var tracerAssembly = {};
  4636.  
  4637.  
  4638. # the wind hash stores the current winds
  4639.  
  4640. var wind = {surface: [0.0,0.0] , cloudlayer: [0.0,0.0], current: [0.0,0.0]};
  4641.  
  4642.  
  4643.  
  4644. # arrays of currently existing weather stations, wind interpolation and atmospheric condition points
  4645.  
  4646. var weatherStationArray = [];
  4647. var windIpointArray = [];
  4648. var atmosphereIpointArray = [];
  4649.  
  4650.  
  4651. # a flag for the wind model (so we don't have to do string comparisons all the time)
  4652. # 1: constant 2: constant in tile 3: aloft interpolated 4: airmass interpolated
  4653.  
  4654. var wind_model_flag = 1;
  4655.  
  4656. # globals governing properties of the Cumulus system
  4657.  
  4658. var convective_texture_mix = 0.0;
  4659. var height_bias = 1.0;
  4660. var convective_size_bias = 0.0;
  4661. var cumulus_efficiency_factor = 1.0;
  4662. var cloud_mean_altitude = 0.0;
  4663. var thermal_conditions = getprop(lw~"config/thermal-properties");
  4664. var lowest_layer_turbulence = 0.6 - thermal_conditions;
  4665. if (lowest_layer_turbulence < 0.0) {lowest_layer_turbulence = 0.0;}
  4666.  
  4667. # global keeping track of lighting
  4668.  
  4669. var top_shade = 1.0;
  4670.  
  4671. # global cloud transparency
  4672.  
  4673. var alpha_factor = 1.0;
  4674.  
  4675. # global cloud size scale;
  4676.  
  4677. var cloud_size_scale = 1.0;
  4678.  
  4679. # globals keeping track of the lifetime when building a Cumulus from individual cloudlets
  4680.  
  4681. var cloud_fractional_lifetime = 0.0;
  4682. var cloud_evolution_timestamp = 0.0;
  4683.  
  4684. # globals propagating gust information inside the interpolation loop
  4685.  
  4686. var windspeed_multiplier = 1.0;
  4687. var winddir_change = 0.0;
  4688.  
  4689. # global flags mirroring property tree menu settings
  4690.  
  4691. var generate_thermal_lift_flag = 0;
  4692. var thread_flag = 1;
  4693. var dynamics_flag = 1;
  4694. var presampling_flag = 1;
  4695. var detailed_clouds_flag = 1;
  4696. var dynamical_convection_flag = 1;
  4697. var debug_output_flag = 1;
  4698. var metar_flag = 0;
  4699. var local_weather_running_flag = 0;
  4700. var local_weather_startup_flag = 0;
  4701. var fps_control_flag = 0;
  4702. var buffer_flag = 1;
  4703. var detailed_terrain_interaction_flag = 1;
  4704. var hardcoded_clouds_flag = 1;
  4705. var realistic_visibility_flag = 0;
  4706. var scattering_shader_flag = 0;
  4707. var wxradar_support_flag = 1;
  4708.  
  4709. var ground_haze_factor = 1.0;
  4710. var max_vis_range = math.exp(getprop(lw~"config/aux-max-vis-range-m"));
  4711. var temperature_offset = 0.0;
  4712. var current_mean_alt = 0.0;
  4713. var air_pollution_norm = 0.0;
  4714.  
  4715. # globals for framerate controlled cloud management
  4716.  
  4717. var fps_average = 0.0;
  4718. var fps_samples = 0;
  4719. var fps_sum = 0.0;
  4720. var target_framerate = 25.0;
  4721.  
  4722. # set all sorts of default properties for the menu
  4723.  
  4724.  
  4725. setprop(lw~"tmp/scloud-lat",getprop("position/latitude-deg"));
  4726. setprop(lw~"tmp/scloud-lon",getprop("position/longitude-deg"));
  4727. setprop(lw~"tmp/tile-alt-median-ft",0.0);
  4728. setprop(lw~"tmp/tile-alt-min-ft",0.0);
  4729. setprop(lw~"tmp/last-reading-pos-del",0);
  4730. setprop(lw~"tmp/last-reading-pos-mod",0);
  4731. setprop(lw~"tmp/thread-status", "idle");
  4732. setprop(lw~"tmp/convective-status", "idle");
  4733. setprop(lw~"tmp/presampling-status", "idle");
  4734. setprop(lw~"tmp/buffer-status", "idle");
  4735. setprop(lw~"tmp/buffer-tile-index", 0);
  4736. setprop(lw~"tmp/ipoint-latitude-deg",getprop("position/latitude-deg"));
  4737. setprop(lw~"tmp/ipoint-longitude-deg",getprop("position/longitude-deg"));
  4738.  
  4739.  
  4740.  
  4741. # set the default loop flags to loops inactive
  4742.  
  4743.  
  4744. setprop(lw~"effect-loop-flag",0);
  4745. setprop(lw~"interpolation-loop-flag",0);
  4746. setprop(lw~"tile-loop-flag",0);
  4747. setprop(lw~"lift-loop-flag",0);
  4748. setprop(lw~"wave-loop-flag",0);
  4749. setprop(lw~"buffer-loop-flag",0);
  4750. setprop(lw~"housekeeping-loop-flag",0);
  4751. setprop(lw~"convective-loop-flag",0);
  4752.  
  4753. # create other management properties
  4754.  
  4755. #setprop(lw~"clouds/cloud-number",0);
  4756. setprop(lw~"clouds/placement-index",0);
  4757. setprop(lw~"clouds/model-placement-index",0);
  4758. setprop(lw~"effect-volumes/effect-placement-index",0);
  4759.  
  4760. # create properties for effect volume management
  4761.  
  4762. setprop(lw~"effect-volumes/number",0);
  4763. setprop(lw~"effect-volumes/number-active-vis",0);
  4764. setprop(lw~"effect-volumes/number-active-rain",0);
  4765. setprop(lw~"effect-volumes/number-active-snow",0);
  4766. setprop(lw~"effect-volumes/number-active-turb",0);
  4767. setprop(lw~"effect-volumes/number-active-lift",0);
  4768. setprop(lw~"effect-volumes/number-active-sat",0);
  4769.  
  4770. # setprop(lw~"config/max-vis-range-m", 120000.0);
  4771. setprop(lw~"config/temperature-offset-degc", 0.0);
  4772.  
  4773. setprop("/sim/rendering/eye-altitude-m", getprop("/position/altitude-ft") * ft_to_m);
  4774.  
  4775. # create properties for tile management
  4776.  
  4777. setprop(lw~"tiles/tile-counter",0);
  4778.  
  4779. # create properties for wind
  4780.  
  4781. setprop(lwi~"ipoint-number",0);
  4782.  
  4783. var updateMenu = func {
  4784. var isEnabled = getprop("/nasal/local_weather/enabled");
  4785. gui.menuEnable("local_weather", isEnabled);
  4786. }
  4787.  
  4788. _setlistener("/nasal/local_weather/enabled", updateMenu);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement