######################################################## # routines to set up, transform and manage local weather # Thorsten Renk, May 2010 # thermal model by Patrice Poly, April 2010 ######################################################## # function purpose # # calc_geo to compute the latitude to meter conversion # calc_d_sq to compute a distance square in local Cartesian approximation # volume_effect_loop to check if the aircraft has entered an effect volume # interpolation_loop to continuously interpolate weather parameters between stations # thermal_lift _loop to manage the detailed thermal lift model # thermal_lift_start to start the detailed thermal model # effect_volume_start to manage parameters when an effect volume is entered # effect_volume_stop to manage parameters when an effect volume is left # setVisibility to set the visibility to a given value # setRain to set rain to a given value # setSnow to set snow to a given value # setTemperature to set temperature to a given value # setPressure to set pressure to a given value # setDewpoint to set the dewpoint to a given value # setLift to set thermal lift to a given value - obsolete # ts_factor (helper function for thermal lift model) # tl_factor (helper function for thermal lift model) # calcLift_max to calculate the maximal available thermal lift for given altitude # calcLift to calculate the thermal lift at aircraft position # randomize_pos to randomize the position of clouds placed in a streak call # select_cloud_model to select a path to the cloud model, given the cloud type and subtype # create_cloud to place a single cloud into the scenery # create_cloud_vec to place a single cloud into an array to be written later # clear_all to remove all clouds, effect volumes and weather stations and stop loops # create_detailed_cumulus_cloud to place multiple cloudlets into a box based on a size parameter # create_cumulonimbus_cloud to place multiple cloudlets into a box # create_cumosys wrapper to place a convective cloud system based on terrain coverage # cumulus_loop to place 25 Cumulus clouds each frame # create_cumulus to place a convective cloud system based on terrain coverage # terrain_presampling to get information about terrain elevation # create_rise_clouds to create a barrier cloud system # create_streak to create a cloud streak # create_layer to create a cloud layer with optional precipitation # cloud_placement_loop to place clouds from a storage array into the scenery # create_effect_volume to create an effect volume # effect_placement_loop to place effect volumes from a storage array # create_effect_volume_vec to create effect volumes in a storage array # set_weather_station to specify a weather station for interpolation # streak_wrapper wrapper to execute streak from menu # convection wrapper wrapper to execute convective clouds from menu # barrier wrapper wrapper to execute barrier clouds from menu # single_cloud_wrapper wrapper to create single cloud from menu # layer wrapper wrapper to create layer from menu # set_tile to call a weather tile creation from menu # startup to prepare the package at startup ################################### # geospatial helper functions ################################### var calc_geo = func(clat) { lon_to_m = math.cos(clat*math.pi/180.0) * lat_to_m; m_to_lon = 1.0/lon_to_m; } var calc_d_sq = func (lat1, lon1, lat2, lon2) { var x = (lat1 - lat2) * lat_to_m; var y = (lon1 - lon2) * lon_to_m; return (x*x + y*y); } ################################### # effect volume management loop ################################### var effect_volume_loop = func (index, n_active) { var n = 25; var evNode = props.globals.getNode("local-weather/effect-volumes", 1).getChildren("effect-volume"); var esize = size(evNode); var viewpos = geo.aircraft_position(); var active_counter = n_active; var i_max = index + 25; if (i_max > esize) {i_max = esize;} for (var i = index; i < i_max; i = i+1) { e = evNode[i]; var flag = 0; #default assumption is that we're not in the volume var ealt_min = e.getNode("position/min-altitude-ft").getValue() * ft_to_m; var ealt_max = e.getNode("position/max-altitude-ft").getValue() * ft_to_m; if ((viewpos.alt() > ealt_min) and (viewpos.alt() < ealt_max)) # we are in the correct alt range { # so we load geometry next var geometry = e.getNode("geometry").getValue(); var elat = e.getNode("position/latitude-deg").getValue(); var elon = e.getNode("position/longitude-deg").getValue(); var rx = e.getNode("volume/size-x").getValue(); if (geometry == 1) # we have a cylinder { var d_sq = calc_d_sq(viewpos.lat(), viewpos.lon(), elat, elon); if (d_sq < (rx*rx)) {flag =1;} } else if (geometry == 2) # we have an elliptic shape { # get orientation var ry = e.getNode("volume/size-y").getValue(); var phi = e.getNode("volume/orientation-deg").getValue(); phi = phi * math.pi/180.0; # first get unrotated coordinates var xx = (viewpos.lon() - elon) * lon_to_m; var yy = (viewpos.lat() - elat) * lat_to_m; # then rotate to align with the shape var x = xx * math.cos(phi) - yy * math.sin(phi); var y = yy * math.cos(phi) + xx * math.sin(phi); # then check elliptic condition if ((x*x)/(rx*rx) + (y*y)/(ry*ry) <1) {flag = 1;} } else if (geometry == 3) # we have a rectangular shape { # get orientation var ry = e.getNode("volume/size-y").getValue(); var phi = e.getNode("volume/orientation-deg").getValue(); phi = phi * math.pi/180.0; # first get unrotated coordinates var xx = (viewpos.lon() - elon) * lon_to_m; var yy = (viewpos.lat() - elat) * lat_to_m; # then rotate to align with the shape var x = xx * math.cos(phi) - yy * math.sin(phi); var y = yy * math.cos(phi) + xx * math.sin(phi); # then check rectangle condition if ((x>-rx) and (x-ry) and (y 80000.0) and (d<100000.0)) {s.remove();} } var vis = sum_vis/sum_norm; var p = sum_p/sum_norm; var D = sum_D/sum_norm; var T = sum_T/sum_norm; # a simple altitude model for visibility - increase it with increasing altitude vis = vis + 0.5 * getprop("position/altitude-ft"); if (vis > 0.0) {iNode.getNode("visibility-m",1).setValue(vis);} # a redundancy check iNode.getNode("temperature-degc",1).setValue(T); iNode.getNode("dewpoint-degc",1).setValue(D); if (p>0.0) {iNode.getNode("pressure-sea-level-inhg",1).setValue(p);} # a redundancy check iNode.getNode("turbulence",1).setValue(0.0); # now check if an effect volume writes the property and set only if not flag = props.globals.getNode("local-weather/effect-volumes/number-active-vis").getValue(); if ((flag ==0) and (vis > 0.0)) { cNode.getNode("visibility-m").setValue(vis); setVisibility(vis); } flag = props.globals.getNode("local-weather/effect-volumes/number-active-turb").getValue(); if ((flag ==0)) { cNode.getNode("turbulence").setValue(0.0); setTurbulence(0.0); } flag = props.globals.getNode("local-weather/effect-volumes/number-active-lift").getValue(); if (flag ==0) { cNode.getNode("thermal-lift").setValue(0.0); } # no need to check for these, as they are not modelled in effect volumes cNode.getNode("temperature-degc",1).setValue(T); setTemperature(T); cNode.getNode("dewpoint-degc",1).setValue(D); setDewpoint(D); if (p>0.0) {cNode.getNode("pressure-sea-level-inhg",1).setValue(p); setPressure(p);} if (getprop(lw~"interpolation-loop-flag") ==1) {settimer(interpolation_loop, 3.0);} } ################################### # thermal lift loop ################################### var thermal_lift_loop = func { var cNode = props.globals.getNode(lw~"current", 1); var lNode = props.globals.getNode(lw~"lift",1); var apos = geo.aircraft_position(); var tlat = lNode.getNode("latitude-deg").getValue(); var tlon = lNode.getNode("longitude-deg").getValue(); var tpos = geo.Coord.new(); tpos.set_latlon(tlat,tlon,0.0); var d = apos.distance_to(tpos); var alt = getprop("position/altitude-ft"); var R = lNode.getNode("radius").getValue(); var height = lNode.getNode("height").getValue(); var cn = lNode.getNode("cn").getValue(); var sh = lNode.getNode("sh").getValue(); var max_lift = lNode.getNode("max_lift").getValue(); var f_lift_radius = lNode.getNode("f_lift_radius").getValue(); # print(d," ", alt, " ", R, " ", height, " ", cn, " ", sh," ", max_lift," ", f_lift_radius, " ",0.0); var lift = calcLift(d, alt, R, height, cn, sh, max_lift, f_lift_radius, 0.0); # print(lift); cNode.getChild("thermal-lift").setValue(lift); if (getprop(lw~"lift-loop-flag") ==1) {settimer(thermal_lift_loop, 0);} } ################################### # thermal lift loop startup ################################### var thermal_lift_start = func (ev) { # copy the properties from effect volume to the lift folder var lNode = props.globals.getNode(lw~"lift",1); lNode.getNode("radius",1).setValue(ev.getNode("effects/radius").getValue()); lNode.getNode("height",1).setValue(ev.getNode("effects/height").getValue()); lNode.getNode("cn",1).setValue(ev.getNode("effects/cn").getValue()); lNode.getNode("sh",1).setValue(ev.getNode("effects/sh").getValue()); lNode.getNode("max_lift",1).setValue(ev.getNode("effects/max_lift").getValue()); lNode.getNode("f_lift_radius",1).setValue(ev.getNode("effects/f_lift_radius").getValue()); lNode.getNode("latitude-deg",1).setValue(ev.getNode("position/latitude-deg").getValue()); lNode.getNode("longitude-deg",1).setValue(ev.getNode("position/longitude-deg").getValue()); # and start the lift loop, unless another one is already running # so we block overlapping calls if (getprop(lw~"lift-loop-flag") == 0) {setprop(lw~"lift-loop-flag",1); thermal_lift_loop();} } ################################### # thermal lift loop stop ################################### var thermal_lift_stop = func { setprop(lw~"lift-loop-flag",0); setprop(lw~"current/thermal-lift",0.0); } #################################### # action taken when in effect volume #################################### var effect_volume_start = func (ev) { var cNode = props.globals.getNode(lw~"current"); if (ev.getNode("effects/visibility-flag", 1).getValue()==1) { # first store the current setting in case we need to restore on leaving var vis = ev.getNode("effects/visibility-m").getValue(); ev.getNode("restore/visibility-m",1).setValue(cNode.getNode("visibility-m").getValue()); # then set the new value in current and execute change cNode.getNode("visibility-m").setValue(vis); setVisibility(vis); # then count the number of active volumes on entry (we need that to determine # what to do on exit) ev.getNode("restore/number-entry-vis",1).setValue(getprop(lw~"effect-volumes/number-active-vis")); # and add to the counter setprop(lw~"effect-volumes/number-active-vis",getprop(lw~"effect-volumes/number-active-vis")+1); } if (ev.getNode("effects/rain-flag", 1).getValue()==1) { var rain = ev.getNode("effects/rain-norm").getValue(); ev.getNode("restore/rain-norm",1).setValue(cNode.getNode("rain-norm").getValue()); cNode.getNode("rain-norm").setValue(rain); setRain(rain); ev.getNode("restore/number-entry-rain",1).setValue(getprop(lw~"effect-volumes/number-active-rain")); setprop(lw~"effect-volumes/number-active-rain",getprop(lw~"effect-volumes/number-active-rain")+1); } if (ev.getNode("effects/snow-flag", 1).getValue()==1) { var snow = ev.getNode("effects/snow-norm").getValue(); ev.getNode("restore/snow-norm",1).setValue(cNode.getNode("snow-norm").getValue()); cNode.getNode("snow-norm").setValue(snow); setSnow(snow); ev.getNode("restore/number-entry-snow",1).setValue(getprop(lw~"effect-volumes/number-active-snow")); setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")+1); } if (ev.getNode("effects/turbulence-flag", 1).getValue()==1) { var turbulence = ev.getNode("effects/turbulence").getValue(); ev.getNode("restore/turbulence",1).setValue(cNode.getNode("turbulence").getValue()); cNode.getNode("turbulence").setValue(turbulence); setTurbulence(turbulence); ev.getNode("restore/number-entry-turb",1).setValue(getprop(lw~"effect-volumes/number-active-turb")); setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")+1); } if (ev.getNode("effects/thermal-lift-flag", 1).getValue()==1) { var lift = ev.getNode("effects/thermal-lift").getValue(); ev.getNode("restore/thermal-lift",1).setValue(cNode.getNode("thermal-lift").getValue()); cNode.getNode("thermal-lift").setValue(lift); #setLift(ev.getNode("position/latitude-deg").getValue(),ev.getNode("position/longitude-deg").getValue(),1); ev.getNode("restore/number-entry-lift",1).setValue(getprop(lw~"effect-volumes/number-active-lift")); setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1); } else if (ev.getNode("effects/thermal-lift-flag", 1).getValue()==2) # thermal by function { ev.getNode("restore/thermal-lift",1).setValue(cNode.getNode("thermal-lift").getValue()); ev.getNode("restore/number-entry-lift",1).setValue(getprop(lw~"effect-volumes/number-active-lift")); setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1); thermal_lift_start(ev); } } var effect_volume_stop = func (ev) { var cNode = props.globals.getNode(lw~"current"); if (ev.getNode("effects/visibility-flag", 1).getValue()==1) { var n_active = getprop(lw~"effect-volumes/number-active-vis"); var n_entry = ev.getNode("restore/number-entry-vis").getValue(); # if no other nodes affecting property are active, restore to outside # else restore settings as they have been when entering the volume when the number # of active volumes is the same as on entry (i.e. volumes are nested), otherwise # leave property at current because new definitions are already active and should not # be cancelled if (n_active ==1){var vis = props.globals.getNode(lw~"interpolation/visibility-m").getValue();} else if ((n_active -1) == n_entry) {var vis = ev.getNode("restore/visibility-m").getValue();} else {var vis = cNode.getNode("visibility-m").getValue();} cNode.getNode("visibility-m").setValue(vis); setVisibility(vis); # and subtract from the counter setprop(lw~"effect-volumes/number-active-vis",getprop(lw~"effect-volumes/number-active-vis")-1); } if (ev.getNode("effects/rain-flag", 1).getValue()==1) { var n_active = getprop(lw~"effect-volumes/number-active-rain"); var n_entry = ev.getNode("restore/number-entry-rain").getValue(); if (n_active ==1){var rain = props.globals.getNode(lw~"interpolation/rain-norm").getValue();} else if ((n_active -1) == n_entry) {var rain = ev.getNode("restore/rain-norm").getValue();} else {var rain = cNode.getNode("rain-norm").getValue();} cNode.getNode("rain-norm").setValue(rain); setRain(rain); setprop(lw~"effect-volumes/number-active-rain",getprop(lw~"effect-volumes/number-active-rain")-1); } if (ev.getNode("effects/snow-flag", 1).getValue()==1) { var n_active = getprop(lw~"effect-volumes/number-active-snow"); var n_entry = ev.getNode("restore/number-entry-snow").getValue(); if (n_active ==1){var snow = props.globals.getNode(lw~"interpolation/snow-norm").getValue();} else if ((n_active -1) == n_entry) {var snow = ev.getNode("restore/snow-norm").getValue();} else {var snow = cNode.getNode("snow-norm").getValue();} cNode.getNode("snow-norm").setValue(snow); setSnow(snow); setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")-1); } if (ev.getNode("effects/turbulence-flag", 1).getValue()==1) { var n_active = getprop(lw~"effect-volumes/number-active-turb"); var n_entry = ev.getNode("restore/number-entry-turb").getValue(); if (n_active ==1){var turbulence = props.globals.getNode(lw~"interpolation/turbulence").getValue();} else if ((n_active -1) == n_entry) {var turbulence = ev.getNode("restore/turbulence").getValue();} else {var turbulence = cNode.getNode("turbulence").getValue();} cNode.getNode("turbulence").setValue(turbulence); setTurbulence(turbulence); setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")-1); } if (ev.getNode("effects/thermal-lift-flag", 1).getValue()==1) { var n_active = getprop(lw~"effect-volumes/number-active-lift"); var n_entry = ev.getNode("restore/number-entry-lift").getValue(); if (n_active ==1){var lift = props.globals.getNode(lw~"interpolation/thermal-lift").getValue();} else if ((n_active -1) == n_entry) {var lift = ev.getNode("restore/thermal-lift").getValue();} else {var lift = cNode.getNode("thermal-lift").getValue();} cNode.getNode("thermal-lift").setValue(lift); # some cheat code # setLift(ev.getNode("position/latitude-deg").getValue(),ev.getNode("position/longitude-deg").getValue(),0); setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1); } else if (ev.getNode("effects/thermal-lift-flag", 1).getValue()==2) # thermal by function { thermal_lift_stop(); setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1); } } #################################### # set visibility to given value #################################### var setVisibility = func (vis) { # this is a rather dirty workaround till a better solution becomes available # essentially we update all entries in config and reinit environment var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry"); foreach (var e; entries_aloft) { e.getNode("visibility-m",1).setValue(vis); } var entries_boundary = props.globals.getNode("environment/config/boundary", 1).getChildren("entry"); foreach (var e; entries_boundary) { e.getNode("visibility-m",1).setValue(vis); } fgcommand("reinit", props.Node.new({subsystem:"environment"})); } #################################### # set rain to given value #################################### var setRain = func (rain) { # setting the lowest cloud layer to 30.000 ft is a workaround # as rain is only created below that layer in default setprop("environment/clouds/layer[0]/elevation-ft", 30000.0); setprop("environment/metar/rain-norm",rain); } #################################### # set snow to given value #################################### var setSnow = func (snow) { # setting the lowest cloud layer to 30.000 ft is a workaround # as snow is only created below that layer in default setprop("environment/clouds/layer[0]/elevation-ft", 30000.0); setprop("environment/metar/snow-norm",snow); } #################################### # set turbulence to given value #################################### var setTurbulence = func (turbulence) { # this is a rather dirty workaround till a better solution becomes available # essentially we update all entries in config and reinit environment var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry"); foreach (var e; entries_aloft) { e.getNode("turbulence/magnitude-norm",1).setValue(turbulence); } # turbulence is slightly reduced in boundary layers var entries_boundary = props.globals.getNode("environment/config/boundary", 1).getChildren("entry"); var i = 1; foreach (var e; entries_boundary) { e.getNode("turbulence/magnitude-norm",1).setValue(turbulence * 0.25*i); i = i + 1; } fgcommand("reinit", props.Node.new({subsystem:"environment"})); } #################################### # set temperature to given value #################################### var setTemperature = func (T) { # this is a rather dirty workaround till a better solution becomes available # essentially we update the entry in config and reinit environment setprop(ec~"boundary/entry[0]/temperature-degc",T); fgcommand("reinit", props.Node.new({subsystem:"environment"})); } #################################### # set pressure to given value #################################### var setPressure = func (p) { # this is a rather dirty workaround till a better solution becomes available # essentially we update the entry in config and reinit environment setprop(ec~"boundary/entry[0]/pressure-sea-level-inhg",p); setprop(ec~"aloft/entry[0]/pressure-sea-level-inhg",p); fgcommand("reinit", props.Node.new({subsystem:"environment"})); } #################################### # set dewpoint to given value #################################### var setDewpoint = func (D) { # this is a rather dirty workaround till a better solution becomes available # essentially we update the entry in config and reinit environment setprop(ec~"boundary/entry[0]/dewpoint-degc",D); fgcommand("reinit", props.Node.new({subsystem:"environment"})); } #################################### # set thermal lift to given value #################################### var setLift = func (lat, lon, flag) { # this is a cheat - if you have an AI thermal present, this sets its coordinates to the # current position if (flag==1) { setprop("ai/models/thermal/position/latitude-deg",lat); setprop("ai/models/thermal/position/longitude-deg",lon); } else { setprop("ai/models/thermal/position/latitude-deg",0.1); setprop("ai/models/thermal/position/longitude-deg",0.1); } #setprop("environment/thermal-lift",L); } ######################################### # compute thermal lift in detailed model ######################################### var ts_factor = func (t, alt, height) { # no time dependence modelled yet return 1.0; var t_a = t - (alt/height) * t1 -t1; if (t_a<0) {return 0.0;} else if (t_a= t1) and (t < t2)) {return 1.0;} else if (t_a >= t2) {return 0.5 - 0.5 * math.cos((1.0-(t2-t_a)/(t3-t2))*math.pi);} } var tl_factor = func (t, alt, height) { # no time dependence modelled yet return 1.0; var t_a = t - (alt/height) * t1; if (t_a<0) {return 0.0;} else if (t_a= t1) and (t < t2)) {return 1.0;} else if (t_a >= t2) {return 0.5 - 0.5 * math.cos((1.0-(t2-t_a)/(t3-t2))*math.pi);} } var calcLift_max = func (alt, max_lift, height) { # no lift below ground if (alt < 0.0) {return 0.0;} # lift ramps up to full within 200 m else if (alt < 200.0*m_to_ft) {return max_lift * 0.5 * (1.0 + math.cos((1.0-alt/(200.0*m_to_ft))*math.pi));} # constant max. lift in main body else if ((alt > 200.0*m_to_ft) and (alt < height)) {return max_lift;} # decreasing lift from cloudbase to 10% above base else if ((alt > height ) and (alt < height*1.1)) {return max_lift * 0.5 * (1.0 - math.cos((1.0-10.0*alt/height)*math.pi));} # no lift available above else {return 0.0;} } var calcLift = func (d, alt, R, height, cn, sh, max_lift, f_lift_radius, t) { # radius of slice at given altitude 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))); # print("r_total: ", r_total, "d: ",d); # print("alt: ", alt, "height: ",height); # no lift if we're outside the radius or above the thermal if ((d > r_total) or (alt > 1.1*height)) { return 0.0; } # fraction of radius providing lift var r_lift = f_lift_radius * r_total; # print("r_lift: ", r_lift); # if we are in the sink portion, get the max. sink for this time and altitude and adjust for actual position if ((d < r_total ) and (d > r_lift)) { var s_max = 0.5 * calcLift_max(alt, max_lift, height) * ts_factor(t, alt, height); # print("s_max: ", s_max); return s_max * math.sin(math.pi * (1.0 + (d-r_lift) * (1.0/(r_total - r_lift)))); } # else we are in the lift portion, get the max. lift for this time and altitude and adjust for actual position else { var l_max = calcLift_max(alt, max_lift, height) * tl_factor(t, alt, height); # print("l_max: ", l_max); return l_max * math.cos(math.pi * (d/(2.0 * r_lift))); } } ########################################################### # randomize positions of clouds created in a fixed pattern ########################################################### var randomize_pos = func (type, alt_var, pos_var_x, pos_var_y, dir) { # it is rather stupid coding to force the randomization call after the streak call, and I'll probably # restructure the whole thing soon dir = dir * math.pi/180.0; if (getprop(lw~"tmp/thread-flag") == 1) { var s = size(clouds_type); for (i=0; i 0.8) {path = "Models/Weather/cumulus_small_shader1.xml";} else if (rn > 0.6) {path = "Models/Weather/cumulus_small_shader2.xml";} else if (rn > 0.4) {path = "Models/Weather/cumulus_small_shader3.xml";} else if (rn > 0.2) {path = "Models/Weather/cumulus_small_shader4.xml";} else {path = "Models/Weather/cumulus_small_shader5.xml";} } else if (subtype == "large") { if (rn > 0.83) {path = "Models/Weather/cumulus_shader1.xml";} else if (rn > 0.664) {path = "Models/Weather/cumulus_shader2.xml";} else if (rn > 0.498) {path = "Models/Weather/cumulus_shader3.xml";} else if (rn > 0.332) {path = "Models/Weather/cumulus_shader4.xml";} else if (rn > 0.166) {path = "Models/Weather/cumulus_shader5.xml";} else {path = "Models/Weather/cumulus_shader6.xml";} } } else if (type == "Cumulus (cloudlet)"){ if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/cumulus_sl7.xml";} else if (rn > 0.6) {path = "Models/Weather/cumulus_sl8.xml";} else if (rn > 0.4) {path = "Models/Weather/cumulus_sl9.xml";} else if (rn > 0.2) {path = "Models/Weather/cumulus_sl10.xml";} #else if (rn > 0.1) {path = "Models/Weather/cumulus_hires_small1.xml";} else {path = "Models/Weather/cumulus_sl11.xml";} #if (rn > 0.83) {path = "Models/Weather/cumulus_hires_small1.xml";} #else if (rn > 0.664) {path = "Models/Weather/cumulus_hires_small2.xml";} #else if (rn > 0.5) {path = "Models/Weather/cumulus_hires_small3.xml";} #else if (rn > 0.332) {path = "Models/Weather/cumulus_hires_small4.xml";} #else if (rn > 0.166) {path = "Models/Weather/cumulus_hires_small5.xml";} #else if (rn > 0.0) {path = "Models/Weather/cumulus_hires_small6.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/cumulus_sl1.xml";} else if (rn > 0.6) {path = "Models/Weather/cumulus_sl2.xml";} else if (rn > 0.4) {path = "Models/Weather/cumulus_sl3.xml";} else if (rn > 0.2) {path = "Models/Weather/cumulus_sl4.xml";} else {path = "Models/Weather/cumulus_sl5.xml";} #if (rn > 0.75) {path = "Models/Weather/cumulus_hires1.xml";} #else if (rn > 0.5) {path = "Models/Weather/cumulus_hires2.xml";} #if (rn > 0.0) {path = "Models/Weather/cumulus_hires3a.xml";} #else if (rn > 0.0) {path = "Models/Weather/cumulus_hires4.xml";} #else if (rn > 0.0) {path = "Models/Weather/cumulus_hires5.xml";} } } else if (type == "Cumulonimbus (cloudlet)"){ if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/cumulonimbus_sl1.xml";} else if (rn > 0.6) {path = "Models/Weather/cumulonimbus_sl2.xml";} else if (rn > 0.4) {path = "Models/Weather/cumulonimbus_sl3.xml";} else if (rn > 0.2) {path = "Models/Weather/cumulonimbus_sl4.xml";} else {path = "Models/Weather/cumulonimbus_sl5.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/cumulonimbus_sl1.xml";} else if (rn > 0.6) {path = "Models/Weather/cumulonimbus_sl2.xml";} else if (rn > 0.4) {path = "Models/Weather/cumulonimbus_sl3.xml";} else if (rn > 0.2) {path = "Models/Weather/cumulonimbus_sl4.xml";} else {path = "Models/Weather/cumulonimbus_sl5.xml";} } } else if (type == "Altocumulus"){ if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/altocumulus_shader6.xml";} else if (rn > 0.6) {path = "Models/Weather/altocumulus_shader7.xml";} else if (rn > 0.4) {path = "Models/Weather/altocumulus_shader8.xml";} else if (rn > 0.2) {path = "Models/Weather/altocumulus_shader9.xml";} else {path = "Models/Weather/altocumulus_shader10.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/altocumulus_shader1.xml";} else if (rn > 0.6) {path = "Models/Weather/altocumulus_shader2.xml";} else if (rn > 0.4) {path = "Models/Weather/altocumulus_shader3.xml";} else if (rn > 0.2) {path = "Models/Weather/altocumulus_shader4.xml";} else {path = "Models/Weather/altocumulus_shader5.xml";} } } else if (type == "Stratus (structured)"){ if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/altocumulus_layer6.xml";} else if (rn > 0.6) {path = "Models/Weather/altocumulus_layer7.xml";} else if (rn > 0.4) {path = "Models/Weather/altocumulus_layer8.xml";} else if (rn > 0.2) {path = "Models/Weather/altocumulus_layer9.xml";} else {path = "Models/Weather/altocumulus_layer10.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/altocumulus_layer1.xml";} else if (rn > 0.6) {path = "Models/Weather/altocumulus_layer2.xml";} else if (rn > 0.4) {path = "Models/Weather/altocumulus_layer3.xml";} else if (rn > 0.2) {path = "Models/Weather/altocumulus_layer4.xml";} else {path = "Models/Weather/altocumulus_layer5.xml";} } } else if ((type == "Cumulonimbus") or (type == "Cumulonimbus (rain)")) { if (subtype == "small") { if (rn > 0.5) {path = "Models/Weather/cumulonimbus_small1.xml";} else {path = "Models/Weather/cumulonimbus_small2.xml";} } else if (subtype == "large") { if (rn > 0.5) {path = "Models/Weather/cumulonimbus_small1.xml";} else {path = "Models/Weather/cumulonimbus_small2.xml";} } } else if (type == "Cirrus") { if (subtype == "large") { if (rn > 0.66) {path = "Models/Weather/cirrus1.xml";} else if (rn > 0.33) {path = "Models/Weather/cirrus2.xml";} else {path = "Models/Weather/cirrus3.xml";} } else if (subtype == "small") { if (rn > 0.5) {path = "Models/Weather/cirrus_amorphous1.xml";} else {path = "Models/Weather/cirrus_amorphous2.xml";} } } else if (type == "Cirrocumulus") { if (subtype == "small") { if (rn > 0.5) {path = "Models/Weather/cirrocumulus1.xml";} else {path = "Models/Weather/cirrocumulus2.xml";} } else if (subtype == "large") { if (rn > 0.5) {path = "Models/Weather/cirrocumulus1.xml";} else {path = "Models/Weather/cirrocumulus4.xml";} } } else if (type == "Cirrocumulus (cloudlet)") { if (subtype == "small") { if (rn > 0.75) {path = "Models/Weather/cirrocumulus_hires1.xml";} else if (rn > 0.5) {path = "Models/Weather/cirrocumulus_hires2.xml";} else if (rn > 0.25) {path = "Models/Weather/cirrocumulus_hires3.xml";} else {path = "Models/Weather/cirrocumulus_hires4.xml";} } else if (subtype == "large") { if (rn > 0.75) {path = "Models/Weather/cirrocumulus_hires1.xml";} else if (rn > 0.5) {path = "Models/Weather/cirrocumulus_hires2.xml";} else if (rn > 0.25) {path = "Models/Weather/cirrocumulus_hires3.xml";} else {path = "Models/Weather/cirrocumulus_hires4.xml";} } } else if (type == "Nimbus") { if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/nimbus_sls1.xml";} else if (rn > 0.6) {path = "Models/Weather/nimbus_sls2.xml";} else if (rn > 0.4) {path = "Models/Weather/nimbus_sls3.xml";} else if (rn > 0.2) {path = "Models/Weather/nimbus_sls4.xml";} else {path = "Models/Weather/nimbus_sls5.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/nimbus_sl1.xml";} else if (rn > 0.6) {path = "Models/Weather/nimbus_sl2.xml";} else if (rn > 0.4) {path = "Models/Weather/nimbus_sl3.xml";} else if (rn > 0.2) {path = "Models/Weather/nimbus_sl4.xml";} else {path = "Models/Weather/nimbus_sl5.xml";} } } else if (type == "Stratus") { if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/stratus_layer1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_layer2.xml";} else if (rn > 0.4) {path = "Models/Weather/stratus_layer3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_layer4.xml";} else {path = "Models/Weather/stratus_layer5.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/stratus_layer1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_layer2.xml";} else if (rn > 0.4) {path = "Models/Weather/stratus_layer3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_layer4.xml";} else {path = "Models/Weather/stratus_layer5.xml";} } } else if (type == "Stratus (thin)") { if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/stratus_tlayer1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_tlayer2.xml";} else if (rn > 0.4) {path = "Models/Weather/stratus_tlayer3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_tlayer4.xml";} else {path = "Models/Weather/stratus_tlayer5.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/stratus_tlayer1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_tlayer2.xml";} else if (rn > 0.4) {path = "Models/Weather/stratus_tlayer3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_tlayer4.xml";} else {path = "Models/Weather/stratus_tlayer5.xml";} } } else if (type == "Cirrostratus") { if (subtype == "small") { if (rn > 0.75) {path = "Models/Weather/cirrostratus1.xml";} else if (rn > 0.5) {path = "Models/Weather/cirrostratus2.xml";} else if (rn > 0.25) {path = "Models/Weather/cirrostratus3.xml";} else {path = "Models/Weather/cirrostratus4.xml";} } else if (subtype == "large") { if (rn > 0.75) {path = "Models/Weather/cirrostratus1.xml";} else if (rn > 0.5) {path = "Models/Weather/cirrostratus2.xml";} else if (rn > 0.25) {path = "Models/Weather/cirrostratus3.xml";} else {path = "Models/Weather/cirrostratus4.xml";} } } else if (type == "Fog (thin)") { if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/stratus_thin1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_thin2.xml";} else if (rn > 0.4) {path = "Models/Weather/stratus_thin3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_thin4.xml";} else {path = "Models/Weather/stratus_thin5.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/stratus_thin1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_thin2.xml";} else if (rn > 0.4) {path = "Models/Weather/stratus_thin3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_thin4.xml";} else {path = "Models/Weather/stratus_thin5.xml";} } } else if (type == "Fog (thick)") { if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/stratus_thick1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_thick2.xml";} else if (rn > 0.4) {path = "Models/Weather/stratus_thick3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_thick4.xml";} else {path = "Models/Weather/stratus_thick5.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/stratus_thick1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_thick2.xml";} else if (rn > 0.4) {path = "Models/Weather/stratus_thick3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_thick4.xml";} else {path = "Models/Weather/stratus_thick5.xml";} } } else if (type == "Test") {path="Models/Weather/test.xml";} else if (type == "Box_test") { if (rn > 0.8) {path = "Models/Weather/test1.xml";} else if (rn > 0.6) {path = "Models/Weather/test2.xml";} else if (rn > 0.4) {path = "Models/Weather/test3.xml";} else if (rn > 0.2) {path = "Models/Weather/test4.xml";} else {path = "Models/Weather/test5.xml";} } else {print("Cloud type ", type, " subtype ",subtype, " not available!");} return path; } ########################################################### # place a single cloud ########################################################### var create_cloud = func(type, path, lat, long, alt, heading, rnd_flag) { var tile_counter = getprop(lw~"tiles/tile-counter"); var n = props.globals.getNode("local-weather/clouds", 1); var c = n.getChild("tile",tile_counter,1); var cloud_number = n.getNode("placement-index").getValue(); for (var i = cloud_number; 1; i += 1) if (c.getChild("cloud", i, 0) == nil) break; cl = c.getChild("cloud", i, 1); n.getNode("placement-index").setValue(i); var model_number = n.getNode("model-placement-index").getValue(); var m = props.globals.getNode("models", 1); for (var i = model_number; 1; i += 1) if (m.getChild("model", i, 0) == nil) break; model = m.getChild("model", i, 1); n.getNode("model-placement-index").setValue(i); cl.getNode("type", 1).setValue(type); var latN = cl.getNode("position/latitude-deg", 1); latN.setValue(lat); var lonN = cl.getNode("position/longitude-deg", 1); lonN.setValue(long); var altN = cl.getNode("position/altitude-ft", 1); altN.setValue(alt); var hdgN = cl.getNode("orientation/true-heading-deg", 1); hdgN.setValue(heading); var pitchN = cl.getNode("orientation/pitch-deg", 1); pitchN.setValue(0.0); var rollN = cl.getNode("orientation/roll-deg", 1);rollN.setValue(0.0); cl.getNode("tile-index",1).setValue(tile_counter); model.getNode("path", 1).setValue(path); model.getNode("legend", 1).setValue("Cloud"); model.getNode("latitude-deg-prop", 1).setValue(latN.getPath()); model.getNode("longitude-deg-prop", 1).setValue(lonN.getPath()); model.getNode("elevation-ft-prop", 1).setValue(altN.getPath()); model.getNode("heading-deg-prop", 1).setValue(hdgN.getPath()); model.getNode("pitch-deg-prop", 1).setValue(pitchN.getPath()); model.getNode("roll-deg-prop", 1).setValue(rollN.getPath()); model.getNode("tile-index",1).setValue(tile_counter); model.getNode("load", 1).remove(); n.getNode("cloud-number").setValue(n.getNode("cloud-number").getValue()+1); } ########################################################### # place a single cloud into a vector to be processed # in a split loop ########################################################### var create_cloud_vec = func(type, path, lat, long, alt, heading, rain_flag) { append(clouds_type,type); append(clouds_path,path); append(clouds_lat,lat); append(clouds_lon,long); append(clouds_alt,alt); append(clouds_orientation,heading); } ########################################################### # clear all clouds and effects ########################################################### var clear_all = func { # clear the clouds and models var cloudNode = props.globals.getNode(lw~"clouds", 1); cloudNode.removeChildren("tile"); var modelNode = props.globals.getNode("models", 1).getChildren("model"); foreach (var m; modelNode) { var l = m.getNode("legend").getValue(); if (l == "Cloud") { m.remove(); } } cloudNode.getNode("cloud-number",1).setValue(0); # clear effect volumes props.globals.getNode("local-weather/effect-volumes", 1).removeChildren("effect-volume"); # clear weather stations props.globals.getNode("local-weather/interpolation", 1).removeChildren("station"); # reset pressure continuity weather_tiles.last_pressure = 0.0; # stop the effect loop and the interpolation loop, make sure thermal generation is off setprop(lw~"effect-loop-flag",0); setprop(lw~"interpolation-loop-flag",0); setprop(lw~"tile-loop-flag",0); setprop(lw~"lift-loop-flag",0); setprop(lw~"tmp/generate-thermal-lift-flag",0); # also remove rain and snow effects setRain(0.0); setSnow(0.0); # set placement indices to zero setprop(lw~"clouds/placement-index",0); setprop(lw~"clouds/model-placement-index",0); setprop(lw~"effect-volumes/effect-placement-index",0); setprop(lw~"tiles/tile-counter",0); } ########################################################### # detailed Cumulus clouds created from multiple cloudlets ########################################################### var create_detailed_cumulus_cloud = func (lat, lon, alt, size) { #print(size); if (size > 2.0) {create_cumulonimbus_cloud(lat, lon, alt, size); return;} else if (size>1.5) { var height = 1000; var n = 30; var x = 700.0; var y = 300.0; var edge = 0.3; } else if (size>1.0) { var height = 400; var n = 8; var x = 400.0; var y = 200.0; var edge = 0.3; } else { var height = 100; var n = 4; var x = 200.0; var y = 200.0; var edge = 1.0; } var alpha = rand() * 180.0; create_streak("Cumulus (cloudlet)",lat,lon, alt+ 0.5* (height +cloud_vertical_size_map["Cumulus"] * ft_to_m),n,0.0,edge,1,0.0,0.0,alpha,1.0); randomize_pos("Cumulus (cloudlet)",height,x,y,alpha); } ########################################################### # detailed small Cumulonimbus clouds created from multiple cloudlets ########################################################### var create_cumulonimbus_cloud = func(lat, lon, alt, size) { var height = 3000.0; var alpha = rand() * 180.0; create_streak("Cumulonimbus",lat,lon, alt+ 0.5* height,8,0.0,0.0,1,0.0,0.0,alpha,1.0); randomize_pos("Cumulonimbus",height,1600.0,800.0,alpha); } ########################################################### # wrappers for convective cloud system to distribute # call across several frames if needed ########################################################### var create_cumosys = func (blat, blon, balt, nc, size) { # realistic Cumulus has somewhat larger models, so compensate to get the same coverage if (getprop(lw~"config/detailed-clouds-flag") == 1) {nc = int(0.7 * nc);} if (getprop(lw~"tmp/thread-flag") == 1) {setprop(lw~"tmp/convective-status", "computing"); cumulus_loop(blat, blon, balt, nc, size);} else {create_cumulus(blat, blon, balt, nc, size); print("Convective system done!");} } var cumulus_loop = func (blat, blon, balt, nc, size) { var n = 25; if (nc < 0) {print("Convective system done!");setprop(lw~"tmp/convective-status", "idle");return;} #print("nc is now: ",nc); create_cumulus(blat, blon, balt, n, size); settimer( func {cumulus_loop(blat, blon, balt, nc-n, size) },0); } ########################################################### # place a convective cloud system ########################################################### var create_cumulus = func (blat, blon, balt, nc, size) { var path = "Models/Weather/blank.ac"; var i = 0; var p = 0.0; var rn = 0.0; var place_lift_flag = 0; var strength = 0.0; var detail_flag = getprop(lw~"config/detailed-clouds-flag"); var alpha = getprop(lw~"tmp/tile-orientation-deg") * math.pi/180.0; # the tile orientation var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime calc_geo(blat); # get the local time of the day in seconds var t = getprop("sim/time/utc/day-seconds"); t = t + getprop("sim/time/local-offset"); # print("t is now:", t); # and make a simple sinusoidal model of thermal strength # daily variation in number of thermals, peaks at noon var t_factor1 = 0.5 * (1.0-math.cos((t * sec_to_rad))); # daily variation in strength of thermals, peaks around 15:30 var t_factor2 = 0.5 * (1.0-math.cos((t * sec_to_rad)-0.9)); #print("t-factor1 is now: ",t_factor1, " ",t_factor2); # number of possible thermals equals overall strength times daily variation times geographic variation # this is a proxy for solar thermal energy nc = t_factor1 * nc * math.cos(blat/180.0*math.pi); var thermal_conditions = getprop(lw~"config/thermal-properties"); while (i < nc) { p = 0.0; place_lift_flag = 0; strength = 0.0; # pick a trial position inside the tile and rotate by tile orientation angle var x = (2.0 * rand() - 1.0) * size; var y = (2.0 * rand() - 1.0) * size; var lat = blat + (y * math.cos(alpha) - x * math.sin(alpha)) * m_to_lat; var lon = blon + (x * math.cos(alpha) + y * math.sin(alpha)) * m_to_lon; # now check ground cover type on chosen spot var info = geodinfo(lat, lon); if (info != nil) { var elevation = info[0] * m_to_ft; if (info[1] != nil){ var landcover = info[1].names[0]; if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];} else {print(p, " ", info[1].names[0]);} }} # then decide if the thermal energy at the spot generates an updraft and a cloud if (rand() < p) # we decide to place a cloud at this spot { strength = (1.5 * rand() + (2.0 * p)) * t_factor2; # the strength of thermal activity at the spot if (strength > 1.0) { # we place a large cloud, and we generate lift path = select_cloud_model("Cumulus","large"); place_lift_flag = 1; } else {path = select_cloud_model("Cumulus","small");} # check if we have a terrain elevation analysis available and can use a # detailed placement altitude correction if (getprop(lw~"tmp/presampling-flag") == 1) { var place_alt = get_convective_altitude(balt, elevation); } else {var place_alt = balt;} if (getprop(lw~"tmp/generate-thermal-lift-flag") != 3) # no clouds if we produce blue thermals { if (getprop(lw~"tmp/thread-flag") == 1) { if (detail_flag == 0){create_cloud_vec("Cumulus",path,lat,lon, place_alt, 0.0, 0);} else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);} } else { if (detail_flag == 0){create_cloud("Cumulus", path, lat, lon, place_alt, 0.0, 0);} else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);} } } # now see if we need to create a thermal - first check the flag if (getprop(lw~"tmp/generate-thermal-lift-flag") == 1) # thermal by constant { # now check if convection is strong if (place_lift_flag == 1) { var lift = 3.0 + 10.0 * (strength -1.0); var radius = 500 + 500 * rand(); #print("Lift: ", lift * ft_to_m - 1.0); create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, -1, lift, 1); } # end if place_lift_flag } # end if generate-thermal-lift-flag else if ((getprop(lw~"tmp/generate-thermal-lift-flag") == 2) or (getprop(lw~"tmp/generate-thermal-lift-flag") == 3)) # thermal by function { if (place_lift_flag == 1) { #var lift = 3.0 + 20.0 * p * rand(); #var radius = 500 + 500 * rand(); var lift = (3.0 + 10.0 * (strength -1.0))/thermal_conditions; var radius = (500 + 500 * rand())*thermal_conditions; #print("Lift: ", lift * ft_to_m - 1.0, " strength: ",strength); create_effect_volume(1, lat, lon, 1.1*radius, 1.1*radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, lift*0.02, lift, -2); } # end if place_lift_flag } # end if generate-thermal-lift-flag } # end if rand < p i = i + 1; } # end while } ########################################################### # place a Cumulus layer with excluded regions # to avoid placing cumulus underneath a thunderstorm ########################################################### var cumulus_exclusion_layer = func (blat, blon, balt, n, size_x, size_y, alpha, s_min, s_max, n_ex, exlat, exlon, exrad) { var strength = 0; var flag = 1; var phi = alpha * math.pi/180.0; var detail_flag = getprop(lw~"config/detailed-clouds-flag"); if (detail_flag == 1) {var i_max = int(0.25*n);} else {var i_max = int(1.0*n);} for (var i =0; i< i_max; i=i+1) { var x = (2.0 * rand() - 1.0) * size_x; var y = (2.0 * rand() - 1.0) * size_y; var lat = blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat; var lon = blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon; flag = 1; for (var j=0; j 1.0) {var path = select_cloud_model("Cumulus","large"); } else {var path = select_cloud_model("Cumulus","small");} if (getprop(lw~"tmp/thread-flag") == 1) { if (detail_flag == 0){create_cloud_vec("Cumulus",path,lat,lon, balt, 0.0, 0);} else {create_detailed_cumulus_cloud(lat, lon, balt, strength);} } else { if (detail_flag == 0){create_cloud("Cumulus", path, lat, lon, balt, 0.0, 0);} else {create_detailed_cumulus_cloud(lat, lon, balt, strength);} } } # end if flag } # end for i } ########################################################### # place a barrier cloud system ########################################################### var create_rise_clouds = func (blat, blon, balt, nc, size, winddir, dist) { var path = "Models/Weather/blank.ac"; var i = 0; var p = 0.0; var rn = 0.0; var nsample = 10; var counter = 0; var elevation = 0.0; var dir = (winddir + 180.0) * math.pi/180.0; var step = dist/nsample; calc_geo(blat); while (i < nc) { counter = counter + 1; p = 0.0; elevation=0.0; var x = (2.0 * rand() - 1.0) * size; var y = (2.0 * rand() - 1.0) * size; var lat = blat + y * m_to_lat; var lon = blon + x * m_to_lon; var info = geodinfo(lat, lon); if (info != nil) {elevation = info[0] * m_to_ft;} if ((elevation < balt) and (elevation != 0.0)) { for (var j = 0; j balt) { p = 1.0 - j * (1.0/nsample); break; } } } } if (counter > 500) {print("Cannot place clouds - exiting..."); i = nc;} if (rand() < p) { path = select_cloud_model("Altocumulus","large"); #print("Cloud ",i, " after ",counter, " tries"); create_cloud("Altocumulus", path, lat, lon, balt, 0.0, 0); counter = 0; i = i+1; } } # end while } ########################################################### # place a cloud streak ########################################################### var create_streak = func (type, blat, blong, balt, nx, xoffset, edgex, ny, yoffset, edgey, direction, tri) { var flag = 0; var path = "Models/Weather/blank.ac"; calc_geo(blat); var dir = direction * math.pi/180.0; var ymin = -0.5 * ny * yoffset; var xmin = -0.5 * nx * xoffset; var xinc = xoffset * (tri-1.0) /ny; var jlow = int(nx*edgex); var ilow = int(ny*edgey); for (var i=0; i(nx-jlow-1))) and ((i(ny-ilow-1)))) # select a small or no cloud { if (rn > 2.0) {flag = 1;} else {path = select_cloud_model(type,"small");} } if ((j(nx-jlow-1)) or (i(ny-ilow-1))) { if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"small");} } else { # select a large cloud if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"large");} } #if (rand() > 0.5) {var beta = 0.0;} else {var beta = 180.0;} if (flag==0){ if (getprop(lw~"tmp/thread-flag") == 1) {create_cloud_vec("tmp_cloud", path, lat, long, balt, 0.0, 0);} else {create_cloud("tmp_cloud", path, lat, long, balt, 0.0, 0);} } } } } ########################################################### # place a cloud layer ########################################################### var create_layer = func (type, blat, blon, balt, bthick, rx, ry, phi, density, edge, rainflag, rain_density) { var i = 0; var area = math.pi * rx * ry; var circ = math.pi * (rx + ry); # that's just an approximation var n = int(area/80000000.0 * 100 * density); var m = int(circ/63000.0 * 40 * rain_density); var path = "Models/Weather/blank.ac"; phi = phi * math.pi/180.0; if (contains(cloud_vertical_size_map, type)) {var alt_offset = cloud_vertical_size_map[type]/2.0 * m_to_ft;} else {var alt_offset = 0.0;} while(i ((1.0 - edge) * (1.0- edge))) { if (rand() > 0.4) { path = select_cloud_model(type,"small"); create_cloud(type, path, lat, lon, alt, 0.0, 0); } } else { path = select_cloud_model(type,"large"); if (getprop(lw~"tmp/thread-flag") == 1) {create_cloud_vec(type, path, lat, lon, alt, 0.0, 0);} else {create_cloud(type, path, lat, lon, alt, 0.0, 0);} } i = i + 1; } } i = 0; if (rainflag ==1){ while(i int(0.5 *n_tot)) {alt_med = i * 500.0; break;} } sum = 0; for (var i=0; i<20;i=i+1) { sum = sum + terrain_n[i]; if (sum > int(0.3 *n_tot)) {alt_20 = i * 500.0; break;} } for (var i=0; i<20;i=i+1) {alt_mean = alt_mean + terrain_n[i] * i * 500.0;} alt_mean = alt_mean/n_tot; for (var i=0; i<20;i=i+1) {if (terrain_n[i] > 0) {alt_min = i * 500.0; break;}} var n_max = 0; sum = 0; for (var i=0; i<19;i=i+1) { sum = sum + terrain_n[i]; if (terrain_n[i] > n_max) {n_max = terrain_n[i];} if ((n_max > terrain_n[i+1]) and (sum > int(0.3*n_tot))) {alt_low_min = i * 500; break;} } print("Terrain presampling analysis results:"); print("total: ",n_tot," mean: ",alt_mean," median: ",alt_med," min: ",alt_min, " alt_20: ", alt_20); #if (alt_low_min < alt_med) {alt_offset = alt_low_min;} #else {alt_offset = alt_med;} setprop(lw~"tmp/tile-alt-offset-ft",alt_20); setprop(lw~"tmp/tile-alt-median-ft",alt_med); setprop(lw~"tmp/tile-alt-min-ft",alt_min); setprop(lw~"tmp/tile-alt-layered-ft",0.5 * (alt_min + alt_offset)); } ########################################################### # detailed altitude determination for convective calls # clouds follow the terrain to some degree, but not excessively so ########################################################### var get_convective_altitude = func (balt, elevation) { var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft"); var alt_median = getprop(lw~"tmp/tile-alt-median-ft"); # get the maximal shift var alt_variation = alt_median - alt_offset; # get the difference between offset and foot point var alt_diff = elevation - alt_offset; # now get the elevation-induced shift var fraction = alt_diff / alt_variation; if (fraction > 1.0) {fraction = 1.0;} # no placement above maximum shift if (fraction < 0.0) {fraction = 0.0;} # no downward shift # get the cloud base var cloudbase = balt - alt_offset; var alt_above_terrain = balt - elevation; # the shift strength is weakened if the layer is high above base elevation # the reference altitude is 1000 ft, anything higher has less sensitivity to terrain var shift_strength = 1000.0/alt_above_terrain; if (shift_strength > 1.0) {shift_strength = 1.0;} # no enhancement for very low layers if (shift_strength < 0.0) {shift_strength = 0.0;} # this shouldn't happen, but just in case... return balt + shift_strength * alt_diff * fraction; } ########################################################### # terrain presampling listener dispatcher ########################################################### var manage_presampling = func { var status = getprop(lw~"tmp/presampling-status"); # we only take action when the analysis is done if (status != "finished") {return;} if (getprop(lw~"tiles/tile-counter") == 0) # we deal with a tile setup call from the menu { set_tile(); } else # the tile setup call came from weather_tile_management { var lat = getprop(lw~"tiles/tmp/latitude-deg"); var lon = getprop(lw~"tiles/tmp/longitude-deg"); var code = getprop(lw~"tiles/tmp/code"); weather_tile_management.generate_tile(code, lat, lon, 0); } # set status to idle again setprop(lw~"tmp/presampling-status", "idle"); } ########################################################### # create an effect volume ########################################################### var create_effect_volume = func (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag) { var flag = 0; var index = getprop(lw~"effect-volumes/effect-placement-index"); var n = props.globals.getNode("local-weather/effect-volumes", 1); for (var i = index; 1; i += 1) if (n.getChild("effect-volume", i, 0) == nil) break; setprop(lw~"effect-volumes/effect-placement-index",i); ev = n.getChild("effect-volume", i, 1); ev.getNode("geometry", 1).setValue(geometry); ev.getNode("active-flag", 1).setValue(0); ev.getNode("position/latitude-deg", 1).setValue(lat); ev.getNode("position/longitude-deg", 1).setValue(lon); ev.getNode("position/min-altitude-ft", 1).setValue(alt_low); ev.getNode("position/max-altitude-ft", 1).setValue(alt_high); ev.getNode("volume/size-x", 1).setValue(r1); ev.getNode("volume/size-y", 1).setValue(r2); ev.getNode("volume/orientation-deg", 1).setValue(phi); ev.getNode("tile-index",1).setValue(getprop(lw~"tiles/tile-counter")); var flag = 1; if (vis < 0.0) {flag = 0;} ev.getNode("effects/visibility-flag", 1).setValue(flag); ev.getNode("effects/visibility-m", 1).setValue(vis); flag = 1; if (rain < 0.0) {flag = 0;} ev.getNode("effects/rain-flag", 1).setValue(flag); ev.getNode("effects/rain-norm", 1).setValue(rain); flag = 1; if (snow < 0.0) {flag = 0;} ev.getNode("effects/snow-flag", 1).setValue(flag); ev.getNode("effects/snow-norm", 1).setValue(snow); flag = 1; if (snow < 0.0) {flag = 0;} ev.getNode("effects/snow-flag", 1).setValue(flag); ev.getNode("effects/snow-norm", 1).setValue(snow); flag = 1; if (turb < 0.0) {flag = 0;} ev.getNode("effects/turbulence-flag", 1).setValue(flag); ev.getNode("effects/turbulence", 1).setValue(turb); flag = 1; if (lift_flag == 0) {flag = 0;} ev.getNode("effects/thermal-lift-flag", 1).setValue(flag); ev.getNode("effects/thermal-lift", 1).setValue(lift); flag = 1; if (lift_flag == -2) # we create a thermal by function { ev.getNode("effects/thermal-lift-flag", 1).setValue(2); ev.getNode("effects/radius",1 ).setValue(0.8*r1); ev.getNode("effects/height",1).setValue(alt_high); ev.getNode("effects/cn",1).setValue(0.8); ev.getNode("effects/sh",1).setValue(0.8); ev.getNode("effects/max_lift",1).setValue(lift); ev.getNode("effects/f_lift_radius",1).setValue(0.8); } # and add to the counter setprop(lw~"effect-volumes/number",getprop(lw~"effect-volumes/number")+1); } ########################################################### # place an effect volume from arrays # only 20 volumes/frame ########################################################### var effect_placement_loop = func (i) { if (getprop(lw~"tmp/effect-thread-status") != "placing") {return;} if (getprop(lw~"tmp/convective-status") != "idle") {return;} if ((i < 0) or (i==0)) { print("Effect placement from array finished!"); setprop(lw~"tmp/effect-thread-status", "idle"); return; } var k_max = 20; var s = size(effects_geo); if (s < k_max) {k_max = s;} for (var k = 0; k < k_max; k = k+1) { create_effect_volume(effects_geo[s-k-1], effects_lat[s-k-1], effects_lon[s-k-1], effects_r1[s-k-1], effects_r2[s-k-1], effects_phi[s-k-1], effects_alt_low[s-k-1], effects_alt_high[s-k-1], effects_vis[s-k-1], effects_rain[s-k-1], effects_snow[s-k-1], effects_turb[s-k-1], effects_lift[s-k-1], effects_lift_flag[s-k-1]); } setsize(effects_geo, s-k_max); setsize(effects_lat, s-k_max); setsize(effects_lon, s-k_max); setsize(effects_r1, s-k_max); setsize(effects_r2, s-k_max); setsize(effects_phi, s-k_max); setsize(effects_alt_low, s-k_max); setsize(effects_alt_high, s-k_max); setsize(effects_vis, s-k_max); setsize(effects_rain, s-k_max); setsize(effects_snow, s-k_max); setsize(effects_turb, s-k_max); setsize(effects_lift, s-k_max); setsize(effects_lift_flag, s-k_max); settimer( func {effect_placement_loop(i - k ) }, 0 ); }; ########################################################### # create an effect volume vector for use in split # frame loops ########################################################### var create_effect_volume_vec = func (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag) { append(effects_geo,geometry); append(effects_lat,lat); append(effects_lon,lon); append(effects_r1,r1); append(effects_r2,r2); append(effects_phi,phi); append(effects_alt_low,alt_low); append(effects_alt_high,alt_high); append(effects_vis,vis); append(effects_rain,rain); append(effects_snow,snow); append(effects_turb,turb); append(effects_lift,lift); append(effects_lift_flag,lift_flag); } ########################################################### # set a weather station for interpolation ########################################################### var set_weather_station = func (lat, lon, vis, T, D, p) { var n = props.globals.getNode(lwi, 1); for (var i = 0; 1; i += 1) if (n.getChild("station", i, 0) == nil) break; s = n.getChild("station", i, 1); s.getNode("latitude-deg",1).setValue(lat); s.getNode("longitude-deg",1).setValue(lon); s.getNode("visibility-m",1).setValue(vis); s.getNode("temperature-degc",1).setValue(T); s.getNode("dewpoint-degc",1).setValue(D); s.getNode("pressure-sea-level-inhg",1).setValue(p); } ########################################################### # wrappers to call functions from the local weather menu bar ########################################################### var streak_wrapper = func { setprop(lw~"tmp/thread-flag", 0); var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); var type = getprop("/local-weather/tmp/cloud-type"); var alt = getprop("/local-weather/tmp/alt"); var nx = getprop("/local-weather/tmp/nx"); var xoffset = getprop("/local-weather/tmp/xoffset"); var xedge = getprop("/local-weather/tmp/xedge"); var ny = getprop("/local-weather/tmp/ny"); var yoffset = getprop("/local-weather/tmp/yoffset"); var yedge = getprop("/local-weather/tmp/yedge"); var dir = getprop("/local-weather/tmp/dir"); var tri = getprop("/local-weather/tmp/tri"); var rnd_alt = getprop("/local-weather/tmp/rnd-alt"); var rnd_pos_x = getprop("/local-weather/tmp/rnd-pos-x"); var rnd_pos_y = getprop("/local-weather/tmp/rnd-pos-y"); create_streak(type,lat,lon,alt,nx,xoffset,xedge,ny,yoffset,yedge,dir,tri); randomize_pos(type,rnd_alt,rnd_pos_x, rnd_pos_y, dir); } var convection_wrapper = func { setprop(lw~"tmp/thread-flag", 0); var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); var alt = getprop("/local-weather/tmp/conv-alt"); var size = getprop("/local-weather/tmp/conv-size"); var strength = getprop("/local-weather/tmp/conv-strength"); var n = int(10 * size * size * strength); create_cumosys(lat,lon,alt,n, size*1000.0); } var barrier_wrapper = func { setprop(lw~"tmp/thread-flag", 0); var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); var alt = getprop("/local-weather/tmp/bar-alt"); var n = getprop("/local-weather/tmp/bar-n"); var dir = getprop("/local-weather/tmp/bar-dir"); var dist = getprop("/local-weather/tmp/bar-dist") * 1000.0; var size = getprop("/local-weather/tmp/bar-size") * 1000.0; create_rise_clouds(lat, lon, alt, n, size, dir, dist); } var single_cloud_wrapper = func { var type = getprop("/local-weather/tmp/scloud-type"); var subtype = getprop("/local-weather/tmp/scloud-subtype"); var lat = getprop("/local-weather/tmp/scloud-lat"); var lon = getprop("/local-weather/tmp/scloud-lon"); var alt = getprop("/local-weather/tmp/scloud-alt"); var heading = getprop("/local-weather/tmp/scloud-dir"); var path = select_cloud_model(type,subtype); create_cloud(type, path, lat, lon, alt, heading, 0); } var layer_wrapper = func { setprop(lw~"tmp/thread-flag", 0); var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); var type = getprop(lw~"tmp/layer-type"); var rx = getprop(lw~"tmp/layer-rx") * 1000.0; var ry = getprop(lw~"tmp/layer-ry") * 1000.0; var phi = getprop(lw~"tmp/layer-phi"); var alt = getprop(lw~"tmp/layer-alt"); var thick = getprop(lw~"tmp/layer-thickness"); var density = getprop(lw~"tmp/layer-density"); var edge = getprop(lw~"tmp/layer-edge"); var rain_flag = getprop(lw~"tmp/layer-rain-flag"); var rain_density = getprop(lw~"tmp/layer-rain-density"); create_layer(type, lat, lon, alt, thick, rx, ry, phi, density, edge, rain_flag, rain_density); } var box_wrapper = func { setprop(lw~"tmp/thread-flag", 0); var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); var alt = getprop("position/altitude-ft"); var x = getprop(lw~"tmp/box-x-m"); var y = getprop(lw~"tmp/box-y-m"); var z = getprop(lw~"tmp/box-alt-ft"); var n = getprop(lw~"tmp/box-n"); var type = "Box_test"; var subtype = "unspecified"; create_cloudbox(type,subtype,lat, lon, alt, x,y,z,n); } #################################### # tile setup call wrapper #################################### var set_tile = func { var type = getprop("/local-weather/tmp/tile-type"); # set tile center coordinates to current position setprop(lw~"tiles/tmp/latitude-deg",getprop("position/latitude-deg")); setprop(lw~"tiles/tmp/longitude-deg",getprop("position/longitude-deg")); weather_tile_management.create_neighbours(getprop("position/latitude-deg"),getprop("position/longitude-deg"),getprop(lw~"tmp/tile-orientation-deg")); # now see if we need to presample the terrain if ((getprop(lw~"tmp/presampling-flag") == 1) and (getprop(lw~"tmp/presampling-status") == "idle")) { terrain_presampling_start(getprop("position/latitude-deg"), getprop("position/longitude-deg"), 1000, 40000, getprop(lw~"tmp/tile-orientation-deg")); return; } # see if we use METAR for weather setup if ((getprop(lw~"METAR/available-flag") == 1) and (getprop(lw~"tmp/tile-management") == "METAR")) {type = "METAR";} setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); if (type == "High-pressure-core") {weather_tiles.set_high_pressure_core_tile();} else if (type == "High-pressure") {weather_tiles.set_high_pressure_tile();} else if (type == "High-pressure-border") {weather_tiles.set_high_pressure_border_tile();} else if (type == "Low-pressure-border") {weather_tiles.set_low_pressure_border_tile();} else if (type == "Low-pressure") {weather_tiles.set_low_pressure_tile();} else if (type == "Low-pressure-core") {weather_tiles.set_low_pressure_core_tile();} else if (type == "Cold-sector") {weather_tiles.set_cold_sector_tile();} else if (type == "Tropical") {weather_tiles.set_tropical_weather_tile();} else if (type == "Coldfront") {weather_tiles.set_coldfront_tile();} else if (type == "Warmfront-1") {weather_tiles.set_warmfront1_tile();} else if (type == "Warmfront-2") {weather_tiles.set_warmfront2_tile();} else if (type == "Warmfront-3") {weather_tiles.set_warmfront3_tile();} else if (type == "Warmfront-4") {weather_tiles.set_warmfront4_tile();} else if (type == "METAR") {weather_tiles.set_METAR_tile();} else if (type == "Altocumulus sky") {weather_tiles.set_altocumulus_tile();setprop(lw~"tiles/code","altocumulus_sky");} else if (type == "Broken layers") {weather_tiles.set_broken_layers_tile();setprop(lw~"tiles/code","broken_layers");} else if (type == "Cold front") {weather_tiles.set_coldfront_tile();setprop(lw~"tiles/code","coldfront");} else if (type == "Cirrus sky") {weather_tiles.set_cirrus_sky_tile();setprop(lw~"tiles/code","cirrus_sky");} else if (type == "Fair weather") {setprop(lw~"tiles/code","cumulus_sky");weather_tiles.set_fair_weather_tile();} else if (type == "Glider's sky") {setprop(lw~"tiles/code","gliders_sky");weather_tiles.set_gliders_sky_tile();} else if (type == "Blue thermals") {setprop(lw~"tiles/code","blue_thermals");weather_tiles.set_blue_thermals_tile();} else if (type == "Incoming rainfront") {weather_tiles.set_rainfront_tile();setprop(lw~"tiles/code","rainfront");} else if (type == "8/8 stratus sky") {weather_tiles.set_overcast_stratus_tile();setprop(lw~"tiles/code","overcast_stratus");} else if (type == "Test tile") {weather_tiles.set_4_8_stratus_tile();setprop(lw~"tiles/code","test");} else if (type == "Summer rain") {weather_tiles.set_summer_rain_tile();setprop(lw~"tiles/code","summer_rain");} else {print("Tile not implemented.");setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")-1);return();} # start tile management loop if needed if (getprop(lw~"tmp/tile-management") != "single tile") { if (getprop(lw~"tile-loop-flag") == 0) { setprop(lw~"tiles/tile[4]/code",getprop(lw~"tiles/code")); setprop(lw~"tile-loop-flag",1); weather_tile_management.tile_management_loop();} } # start the interpolation loop if (getprop(lw~"interpolation-loop-flag") == 0) {setprop(lw~"interpolation-loop-flag",1); local_weather.interpolation_loop();} # start the effect volume loop if (getprop(lw~"effect-loop-flag") == 0) {setprop(lw~"effect-loop-flag",1); local_weather.effect_volume_loop(0,0);} } ################################################# # Anything that needs to run at startup goes here ################################################# var startup = func { print("Loading local weather routines..."); # get local Cartesian geometry var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); calc_geo(lat); # copy weather properties at startup to local weather setprop(lw~"interpolation/visibility-m",getprop(ec~"boundary/entry[0]/visibility-m")); setprop(lw~"interpolation/pressure-sea-level-inhg",getprop(ec~"boundary/entry[0]/pressure-sea-level-inhg")); setprop(lw~"interpolation/temperature-degc",getprop(ec~"boundary/entry[0]/temperature-degc")); setprop(lw~"interpolation/wind-from-heading-deg",getprop(ec~"boundary/entry[0]/wind-from-heading-deg")); setprop(lw~"interpolation/wind-speed-kt",getprop(ec~"boundary/entry[0]/wind-speed-kt")); setprop(lw~"interpolation/turbulence",getprop(ec~"boundary/entry[0]/turbulence/magnitude-norm")); setprop(lw~"interpolation/rain-norm",0.0); setprop(lw~"interpolation/snow-norm",0.0); setprop(lw~"interpolation/thermal-lift",0.0); # before interpolation starts, these are also initially current setprop(lw~"current/visibility-m",getprop(lwi~"visibility-m")); setprop(lw~"current/pressure-sea-level-inhg",getprop(lw~"interpolation/pressure-sea-level-inhg")); setprop(lw~"current/temperature-degc",getprop(lw~"interpolation/temperature-degc")); setprop(lw~"current/wind-from-heading-deg",getprop(lw~"interpolation/wind-from-heading-deg")); setprop(lw~"current/wind-speed-kt",getprop(lw~"interpolation/wind-speed-kt")); setprop(lw~"current/rain-norm",getprop(lw~"interpolation/rain-norm")); setprop(lw~"current/snow-norm",getprop(lw~"interpolation/snow-norm")); setprop(lw~"current/thermal-lift",getprop(lw~"interpolation/thermal-lift")); setprop(lw~"current/turbulence",getprop(lwi~"turbulence")); # create default properties for METAR system, should be overwritten by real-weather-fetch setprop(lw~"METAR/latitude-deg",lat); setprop(lw~"METAR/longitude-deg",lon); setprop(lw~"METAR/altitude-ft",0.0); setprop(lw~"METAR/wind-direction-deg",0.0); setprop(lw~"METAR/wind-strength-kt",10.0); setprop(lw~"METAR/visibility-m",17000.0); setprop(lw~"METAR/rain-norm",0.0); setprop(lw~"METAR/snow-norm",0.0); setprop(lw~"METAR/temperature-degc",10.0); setprop(lw~"METAR/dewpoint-degc",7.0); setprop(lw~"METAR/pressure-inhg",29.92); setprop(lw~"METAR/thunderstorm-flag",0); setprop(lw~"METAR/layer[0]/cover-oct",4); setprop(lw~"METAR/layer[0]/alt-agl-ft", 3000.0); setprop(lw~"METAR/layer[1]/cover-oct",0); setprop(lw~"METAR/layer[1]/alt-agl-ft", 20000.0); setprop(lw~"METAR/layer[2]/cover-oct",0); setprop(lw~"METAR/layer[2]/alt-agl-ft", 20000.0); setprop(lw~"METAR/layer[3]/cover-oct",0); setprop(lw~"METAR/layer[3]/alt-agl-ft", 20000.0); setprop(lw~"METAR/available-flag",1); # set listener for worker threads setlistener(lw~"tmp/thread-status", func {var s = size(clouds_type); cloud_placement_loop(s); }); setlistener(lw~"tmp/convective-status", func {var s = size(clouds_type); cloud_placement_loop(s); }); setlistener(lw~"tmp/effect-thread-status", func {var s = size(effects_geo); effect_placement_loop(s); }); setlistener(lw~"tmp/convective-status", func {var s = size(effects_geo); effect_placement_loop(s); }); setlistener(lw~"tmp/presampling-status", func {manage_presampling(); }); } ##################################################### # Standard test call (for development and debug only) ##################################################### var test = func { var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); # terrain_presampling_start(lat, lon, 1000, 20000, 0.0); # test: 8 identical position tuples for KSFO var p=[ 37.6189722, -122.3748889, 37.6189722, -122.3748889, 37.6289722, -122.3748889, 37.6189722, -122.3648889, 37.6389722, -122.3748889, 37.6189722, -122.3548889, 37.6489722, -122.3748889, 37.6189722, -122.3448889 ]; var x=geodinfo(p, 10000); # passing in vector with position tuples foreach(var e;x) { print("Elevation:",e); # showing results } } ################################################################# # global variable, property creation and the startup listener ################################################################# var rad_E = 6378138.12; # earth radius var lat_to_m = 110952.0; # latitude degrees to meters var m_to_lat = 9.01290648208234e-06; # meters to latitude degrees var ft_to_m = 0.30480; var m_to_ft = 1.0/ft_to_m; var lon_to_m = 0.0; # needs to be calculated dynamically var m_to_lon = 0.0; # we do this on startup # some common abbreviations var lw = "/local-weather/"; var lwi = "/local-weather/interpolation/"; var ec = "/environment/config/"; # a hash map of the strength for convection associated with terrain types var landcover_map = {BuiltUpCover: 0.35, Town: 0.35, Freeway:0.35, BarrenCover:0.3, HerbTundraCover: 0.25, GrassCover: 0.2, CropGrassCover: 0.2, Sand: 0.25, Grass: 0.2, Ocean: 0.01, Marsh: 0.05, Lake: 0.01, ShrubCover: 0.15, Landmass: 0.2, CropWoodCover: 0.15, MixedForestCover: 0.1, DryCropPastureCover: 0.25, MixedCropPastureCover: 0.2, IrrCropPastureCover: 0.15, DeciduousBroadCover: 0.1, pa_taxiway : 0.35, pa_tiedown: 0.35, pc_taxiway: 0.35, pc_tiedown: 0.35, Glacier: 0.01, DryLake: 0.3, IntermittentStream: 0.2}; # a hash map of average vertical cloud model sizes var cloud_vertical_size_map = {Altocumulus: 700.0, Cumulus: 600.0, Nimbus: 1000.0, Stratus: 800.0, Stratus_structured: 600.0, Stratus_thin: 400.0}; # storage arrays for cloud generation var clouds_type = []; var clouds_path = []; var clouds_lat = []; var clouds_lon = []; var clouds_alt = []; var clouds_orientation = []; # storage arrays for effect volume generation var effects_geo = []; var effects_lat = []; var effects_lon = []; var effects_r1 = []; var effects_r2 = []; var effects_phi = []; var effects_alt_low = []; var effects_alt_high = []; var effects_vis = []; var effects_rain = []; var effects_snow = []; var effects_turb = []; var effects_lift = []; var effects_lift_flag = []; # storage array for terrain presampling var terrain_n = []; # set all sorts of default properties for the menu setprop(lw~"tmp/cloud-type", "Altocumulus"); setprop(lw~"tmp/alt", 12000.0); setprop(lw~"tmp/nx",5); setprop(lw~"tmp/xoffset",800.0); setprop(lw~"tmp/xedge", 0.2); setprop(lw~"tmp/ny",15); setprop(lw~"tmp/yoffset",800.0); setprop(lw~"tmp/yedge", 0.2); setprop(lw~"tmp/dir",0.0); setprop(lw~"tmp/tri", 1.0); setprop(lw~"tmp/rnd-pos-x",400.0); setprop(lw~"tmp/rnd-pos-y",400.0); setprop(lw~"tmp/rnd-alt", 300.0); setprop(lw~"tmp/conv-strength", 1); setprop(lw~"tmp/conv-size", 15.0); setprop(lw~"tmp/conv-alt", 2000.0); setprop(lw~"tmp/bar-alt", 3500.0); setprop(lw~"tmp/bar-n", 150.0); setprop(lw~"tmp/bar-dir", 0.0); setprop(lw~"tmp/bar-dist", 5.0); setprop(lw~"tmp/bar-size", 10.0); setprop(lw~"tmp/scloud-type", "Altocumulus"); setprop(lw~"tmp/scloud-subtype", "small"); setprop(lw~"tmp/scloud-lat",getprop("position/latitude-deg")); setprop(lw~"tmp/scloud-lon",getprop("position/longitude-deg")); setprop(lw~"tmp/scloud-alt", 5000.0); setprop(lw~"tmp/scloud-dir", 0.0); setprop(lw~"tmp/layer-type","Nimbus"); setprop(lw~"tmp/layer-rx",10.0); setprop(lw~"tmp/layer-ry",10.0); setprop(lw~"tmp/layer-phi",0.0); setprop(lw~"tmp/layer-alt",3000.0); setprop(lw~"tmp/layer-thickness",500.0); setprop(lw~"tmp/layer-density",1.0); setprop(lw~"tmp/layer-edge",0.2); setprop(lw~"tmp/layer-rain-flag",1); setprop(lw~"tmp/layer-rain-density",1.0); setprop(lw~"tmp/box-x-m",300.0); setprop(lw~"tmp/box-y-m",300.0); setprop(lw~"tmp/box-alt-ft",300.0); setprop(lw~"tmp/box-n",4); setprop(lw~"tmp/tile-type", "High-pressure"); setprop(lw~"tmp/tile-orientation-deg", 0.0); setprop(lw~"tmp/tile-alt-offset-ft", 0.0); setprop(lw~"tmp/tile-alt-median-ft",0.0); setprop(lw~"tmp/tile-alt-min-ft",0.0); setprop(lw~"tmp/tile-management", "single tile"); setprop(lw~"tmp/generate-thermal-lift-flag", 0); setprop(lw~"tmp/presampling-flag", 1); setprop(lw~"tmp/asymmetric-tile-loading-flag", 0); setprop(lw~"tmp/thread-flag", 1); setprop(lw~"tmp/last-reading-pos-del",0); setprop(lw~"tmp/last-reading-pos-mod",0); setprop(lw~"tmp/thread-status", "idle"); setprop(lw~"tmp/convective-status", "idle"); setprop(lw~"tmp/presampling-status", "idle"); # set config values setprop(lw~"config/distance-to-load-tile-m",35000.0); setprop(lw~"config/distance-to-remove-tile-m",37000.0); setprop(lw~"config/detailed-clouds-flag",1); setprop(lw~"config/thermal-properties",1.0); # set the default loop flags to loops inactive setprop(lw~"effect-loop-flag",0); setprop(lw~"interpolation-loop-flag",0); setprop(lw~"tile-loop-flag",0); setprop(lw~"lift-loop-flag",0); # create other management properties setprop(lw~"clouds/cloud-number",0); setprop(lw~"clouds/placement-index",0); setprop(lw~"clouds/model-placement-index",0); setprop(lw~"effect-volumes/effect-placement-index",0); # create properties for effect volume management setprop(lw~"effect-volumes/number",0); setprop(lw~"effect-volumes/number-active-vis",0); setprop(lw~"effect-volumes/number-active-rain",0); setprop(lw~"effect-volumes/number-active-snow",0); setprop(lw~"effect-volumes/number-active-turb",0); setprop(lw~"effect-volumes/number-active-lift",0); # create properties for tile management setprop(lw~"tiles/tile-counter",0); # wait for Nasal to be available and do what is in startup() _setlistener("/sim/signals/nasal-dir-initialized", func { startup(); });