1
0
Fork 0
fgdata/Nasal/local_weather.nas

4300 lines
128 KiB
Text
Raw Normal View History

2010-04-12 20:51:46 +00:00
2010-06-04 21:05:32 +00:00
########################################################
2010-04-12 20:51:46 +00:00
# routines to set up, transform and manage local weather
2011-03-19 13:03:28 +00:00
# Thorsten Renk, March 2011
2010-06-04 21:05:32 +00:00
# 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
2010-08-03 06:18:14 +00:00
# effect_volume_loop to check if the aircraft has entered an effect volume
# assemble_effect_array to store the size of the effect volume array
2010-08-03 06:18:14 +00:00
# add_vectors to add two vectors in polar coordinates
# wind_altitude_interpolation to interpolate aloft winds in altitude
# wind_interpolation to interpolate aloft winds in altitude and position
2011-01-05 12:36:10 +00:00
# get_slowdown_fraction to compute the effect of boundary layer wind slowdown
2010-06-04 21:05:32 +00:00
# interpolation_loop to continuously interpolate weather parameters between stations
# thermal_lift_start to start the detailed thermal model
# thermal_lift_loop to manage the detailed thermal lift model
# thermal_lift_stop to end the detailed thermal lift model
# wave_lift_start to start the detailed wave lift model
# wave_lift_loop to manage the detailed wave lift model
# wave_lift_stop to end the detailed wave lift model
2010-06-04 21:05:32 +00:00
# effect_volume_start to manage parameters when an effect volume is entered
# effect_volume_stop to manage parameters when an effect volume is left
# 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
# calcWaveLift to calculate wave lift at aircraft position
2010-06-04 21:05:32 +00:00
# select_cloud_model to select a path to the cloud model, given the cloud type and subtype
# 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
2010-06-15 18:54:44 +00:00
# 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
2010-06-04 21:05:32 +00:00
# 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
# recreate_cumulus to respawn convective clouds as part of the convective dynamics algorithm
2010-08-03 06:18:14 +00:00
# cumulus_exclusion_layer to create a layer with 'holes' left for thunderstorm placement
2010-06-04 21:05:32 +00:00
# create_rise_clouds to create a barrier cloud system
# create_streak to create a cloud streak
2011-03-19 13:03:28 +00:00
# create_undulatus to create an undulating cloud pattern
2010-06-04 21:05:32 +00:00
# create_layer to create a cloud layer with optional precipitation
2010-08-03 06:18:14 +00:00
# create_hollow_layer to create a cloud layer in a hollow cylinder (better for performance)
# create_cloudbox to create a sophisticated cumulus cloud with different textures (experimental)
# terrain_presampling_start to initialize terrain presampling
# terrain_presampling_loop to sample 25 terrain points per frame
# terrain_presampling to sample terrain elevation at a random point within specified area
# terrain_presampling_analysis to analyze terrain presampling results
# wave_detection_loop to detect if and where wave lift should be placed (currently unfinished)
2010-08-03 06:18:14 +00:00
# get_convective_altitude to determine the altitude at which a Cumulus cloud is placed
# manage presampling to take proper action when a presampling call has been finished
# set_wind_model_flag to convert the wind model string into an integer flag
# set_texture_mix to determine the texture mix between smooth and rough cloud appearance
2010-06-04 21:05:32 +00:00
# create_effect_volume to create an effect volume
# set_weather_station to specify a weather station for interpolation
2010-08-03 06:18:14 +00:00
# set_wind_ipoint to set an aloft wind interpolation point
2011-03-19 13:03:28 +00:00
# set_wind_ipoint_metar to set a wind interpolation point from available ground METAR info where aloft is modelled
2010-08-03 06:18:14 +00:00
# showDialog to pop up a dialog window
# readFlags to read configuration flags from the property tree into Nasal variables at startup
2010-06-04 21:05:32 +00:00
# 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
2010-08-03 06:18:14 +00:00
# box wrapper wrapper to create a cloudbox (experimental)
# set_aloft wrapper wrapper to create aloft winds from menu
2010-06-04 21:05:32 +00:00
# set_tile to call a weather tile creation from menu
# startup to prepare the package at startup
# test to serve as a testbed for new functions
# object purpose
# weatherStation to store info about weather conditions
2011-03-19 13:03:28 +00:00
# windIpoint to store an interpolation point of the windfield
# effectVolume to store effect volume info and provide methods to move and time-evolve effect volumes
# thermalLift to store thermal info and provide methods to move and time-evolve a thermal
# waveLift to store wave info
2010-06-04 21:05:32 +00:00
###################################
# geospatial helper functions
###################################
2010-04-12 20:51:46 +00:00
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;
2010-08-03 06:18:14 +00:00
weather_dynamics.lon_to_m = lon_to_m;
weather_dynamics.m_to_lon = m_to_lon;
2010-04-12 20:51:46 +00:00
}
2010-06-04 21:05:32 +00:00
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);
}
2010-04-12 20:51:46 +00:00
###################################
# effect volume management loop
###################################
2010-06-04 21:05:32 +00:00
var effect_volume_loop = func (index, n_active) {
2010-04-12 20:51:46 +00:00
2010-06-04 21:05:32 +00:00
var n = 25;
2010-08-03 06:18:14 +00:00
var esize = n_effectVolumeArray;
2010-04-12 20:51:46 +00:00
2010-06-04 21:05:32 +00:00
var viewpos = geo.aircraft_position();
var active_counter = n_active;
2010-08-03 06:18:14 +00:00
var i_max = index + n;
2010-06-04 21:05:32 +00:00
if (i_max > esize) {i_max = esize;}
for (var i = index; i < i_max; i = i+1)
{
var e = effectVolumeArray[i];
var flag = 0; # default assumption is that we're not in the volume
2010-04-12 20:51:46 +00:00
var ealt_min = e.alt_low * ft_to_m;
var ealt_max = e.alt_high * ft_to_m;
2010-04-12 20:51:46 +00:00
if ((viewpos.alt() > ealt_min) and (viewpos.alt() < ealt_max)) # we are in the correct alt range
{
2010-06-04 21:05:32 +00:00
# so we load geometry next
var geometry = e.geometry;
var elat = e.lat;
var elon = e.lon;
var rx = e.r1;
2010-06-04 21:05:32 +00:00
2010-04-12 20:51:46 +00:00
if (geometry == 1) # we have a cylinder
{
2010-06-04 21:05:32 +00:00
var d_sq = calc_d_sq(viewpos.lat(), viewpos.lon(), elat, elon);
if (d_sq < (rx*rx)) {flag =1;}
2010-04-12 20:51:46 +00:00
}
2010-06-04 21:05:32 +00:00
else if (geometry == 2) # we have an elliptic shape
2010-04-12 20:51:46 +00:00
{
2010-06-04 21:05:32 +00:00
# get orientation
var ry = e.r2;
var phi = e.phi;
2010-06-04 21:05:32 +00:00
phi = phi * math.pi/180.0;
2010-04-12 20:51:46 +00:00
# first get unrotated coordinates
2010-06-04 21:05:32 +00:00
var xx = (viewpos.lon() - elon) * lon_to_m;
var yy = (viewpos.lat() - elat) * lat_to_m;
2010-04-12 20:51:46 +00:00
# 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);
2010-04-12 20:51:46 +00:00
# then check elliptic condition
if ((x*x)/(rx*rx) + (y*y)/(ry*ry) <1) {flag = 1;}
}
2010-06-04 21:05:32 +00:00
else if (geometry == 3) # we have a rectangular shape
2010-04-12 20:51:46 +00:00
{
2010-06-04 21:05:32 +00:00
# get orientation
var ry = e.r2;
var phi = e.phi;
2010-06-04 21:05:32 +00:00
phi = phi * math.pi/180.0;
2010-04-12 20:51:46 +00:00
# first get unrotated coordinates
2010-06-04 21:05:32 +00:00
var xx = (viewpos.lon() - elon) * lon_to_m;
var yy = (viewpos.lat() - elat) * lat_to_m;
2010-04-12 20:51:46 +00:00
# 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<rx) and (y>-ry) and (y<ry)) {flag = 1;}
}
2010-06-04 21:05:32 +00:00
} # end if altitude
2010-04-12 20:51:46 +00:00
# if flag ==1 at this point, we are inside the effect volume
# but we only need to take action on entering and leaving, so we check also active_flag
# if (flag==1) {print("Inside volume");}
2010-04-12 20:51:46 +00:00
var active_flag = e.active_flag;
2010-06-04 21:05:32 +00:00
2010-04-12 20:51:46 +00:00
if ((flag==1) and (active_flag ==0)) # we just entered the node
{
#print("Entered volume");
e.active_flag = 1;
2010-04-12 20:51:46 +00:00
effect_volume_start(e);
}
2010-06-04 21:05:32 +00:00
else if ((flag==0) and (active_flag ==1)) # we left an active node
2010-04-12 20:51:46 +00:00
{
#print("Left volume!");
e.active_flag = 0;
2010-04-12 20:51:46 +00:00
effect_volume_stop(e);
}
2010-06-04 21:05:32 +00:00
if (flag==1) {active_counter = active_counter + 1;} # we still count the active volumes
2010-04-12 20:51:46 +00:00
} # end foreach
2010-06-04 21:05:32 +00:00
# at this point, all active effect counters should have been set to zero if we're outside all volumes
# however there seem to be rare configurations of overlapping volumes for which this doesn't happen
# therefore we zero them for redundancy here so that the interpolation loop can take over
# and set the properties correctly for outside
if (i == esize) # we check the number of actives and reset all counters
{
if (active_counter == 0)
{
var vNode = props.globals.getNode("local-weather/effect-volumes", 1);
vNode.getChild("number-active-vis").setValue(0);
vNode.getChild("number-active-snow").setValue(0);
vNode.getChild("number-active-rain").setValue(0);
vNode.getChild("number-active-lift").setValue(0);
2010-06-15 18:54:44 +00:00
vNode.getChild("number-active-turb").setValue(0);
vNode.getChild("number-active-sat").setValue(0);
2010-06-04 21:05:32 +00:00
}
#print("n_active: ", active_counter);
active_counter = 0; i = 0;
}
# and we repeat the loop as long as the control flag is set
if (getprop(lw~"effect-loop-flag") ==1) {settimer( func {effect_volume_loop(i, active_counter); },0);}
2010-04-12 20:51:46 +00:00
}
2010-08-03 06:18:14 +00:00
###################################
# assemble effect volume array
###################################
var assemble_effect_array = func {
n_effectVolumeArray = size(effectVolumeArray);
}
###################################
# vector addition
###################################
var add_vectors = func (phi1, r1, phi2, r2) {
phi1 = phi1 * math.pi/180.0;
phi2 = phi2 * math.pi/180.0;
var x1 = r1 * math.sin(phi1);
var x2 = r2 * math.sin(phi2);
var y1 = r1 * math.cos(phi1);
var y2 = r2 * math.cos(phi2);
var x = x1+x2;
var y = y1+y2;
var phi = math.atan2(x,y) * 180.0/math.pi;
var r = math.sqrt(x*x + y*y);
var vec = [];
append(vec, phi);
append(vec,r);
return vec;
}
###################################
# windfield altitude interpolation
###################################
var wind_altitude_interpolation = func (altitude, w) {
if (altitude < wind_altitude_array[0]) {var alt_wind = wind_altitude_array[0];}
else if (altitude > wind_altitude_array[8]) {var alt_wind = 0.99* wind_altitude_array[8];}
2011-03-19 13:03:28 +00:00
else {var alt_wind = altitude;}
2010-08-03 06:18:14 +00:00
for (var i = 0; i<9; i=i+1)
{if (alt_wind < wind_altitude_array[i]) {break;}}
2011-03-19 13:03:28 +00:00
#var altNodeMin = w.getChild("altitude",i-1);
#var altNodeMax = w.getChild("altitude",i);
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
#var vmin = altNodeMin.getNode("windspeed-kt").getValue();
#var vmax = altNodeMax.getNode("windspeed-kt").getValue();
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
var vmin = w.alt[i-1].v;
var vmax = w.alt[i].v;
#var dir_min = altNodeMin.getNode("wind-from-heading-deg").getValue();
#var dir_max = altNodeMax.getNode("wind-from-heading-deg").getValue();
var dir_min = w.alt[i-1].d;
var dir_max = w.alt[i].d;
2010-08-03 06:18:14 +00:00
var f = (alt_wind - wind_altitude_array[i-1])/(wind_altitude_array[i] - wind_altitude_array[i-1]);
var res = add_vectors(dir_min, (1-f) * vmin, dir_max, f * vmax);
return res;
}
2011-03-19 13:03:28 +00:00
###################################
# windfield spatial interpolation
###################################
2010-08-03 06:18:14 +00:00
var wind_interpolation = func (lat, lon, alt) {
2011-03-19 13:03:28 +00:00
# var windNodes = props.globals.getNode(lw~"interpolation").getChildren("wind");
2010-08-03 06:18:14 +00:00
var sum_norm = 0;
var sum_wind = [0,0];
2011-03-19 13:03:28 +00:00
var wsize = size(windIpointArray);
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
for (var i = 0; i < wsize; i=i+1) {
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
#var wlat = w.getNode("latitude-deg").getValue();
#var wlon = w.getNode("longitude-deg").getValue();
var w = windIpointArray[i];
2010-08-03 06:18:14 +00:00
var wpos = geo.Coord.new();
2011-03-19 13:03:28 +00:00
wpos.set_latlon(w.lat,w.lon,1000.0);
2010-08-03 06:18:14 +00:00
var ppos = geo.Coord.new();
ppos.set_latlon(lat,lon,1000.0);
var d = ppos.distance_to(wpos);
if (d <100.0) {d = 100.0;} # to prevent singularity at zero
2011-03-19 13:03:28 +00:00
sum_norm = sum_norm + (1./d) * w.weight;
2010-08-03 06:18:14 +00:00
var res = wind_altitude_interpolation(alt,w);
2011-03-19 13:03:28 +00:00
sum_wind = add_vectors(sum_wind[0], sum_wind[1], res[0], (res[1]/d) * w.weight);
# gradually fade in the interpolation weight of newly added points to
# avoid sudden jumps
if (w.weight < 1.0) {w.weight = w.weight + 0.02;}
2010-08-03 06:18:14 +00:00
}
sum_wind[1] = sum_wind[1] /sum_norm;
return sum_wind;
}
2011-01-05 12:36:10 +00:00
###################################
# boundary layer computations
###################################
var get_slowdown_fraction = func {
var tile_index = getprop(lw~"tiles/tile[4]/tile-index");
var altitude_agl = getprop("/position/altitude-agl-ft");
var altitude = getprop("/position/altitude-ft");
if (presampling_flag == 0)
{
var base_layer_thickness = 600.0;
var f_slow = 1.0/3.0;
}
else
{
var alt_median = alt_50_array[tile_index - 1];
var alt_difference = alt_median - (altitude - altitude_agl);
var base_layer_thickness = 150.0;
# get the boundary layer size dependent on terrain altitude above terrain median
if (alt_difference > 0.0) # we're low and the boundary layer grows
{var boundary_alt = base_layer_thickness + 0.3 * alt_difference;}
else # the boundary layer shrinks
{var boundary_alt = base_layer_thickness + 0.1 * alt_difference;}
if (boundary_alt < 50.0){boundary_alt = 50.0;}
if (boundary_alt > 3000.0) {boundary_alt = 3000.0;}
# get the boundary effect as a function of bounday layer size
var f_slow = 1.0 - (0.2 + 0.17 * math.ln(boundary_alt/base_layer_thickness));
}
print("Boundary layer thickness: ",base_layer_thickness);
print("Boundary layer slowdown: ", f_slow);
return f_slow;
}
2010-04-12 20:51:46 +00:00
###################################
# interpolation management loop
###################################
var interpolation_loop = func {
var iNode = props.globals.getNode(lw~"interpolation", 1);
var cNode = props.globals.getNode(lw~"current", 1);
2010-06-04 21:05:32 +00:00
var viewpos = geo.aircraft_position();
2010-04-12 20:51:46 +00:00
2011-03-19 13:03:28 +00:00
var sum_alt = 0.0;
2010-04-12 20:51:46 +00:00
var sum_vis = 0.0;
var sum_T = 0.0;
var sum_p = 0.0;
var sum_D = 0.0;
var sum_norm = 0.0;
2011-03-19 13:03:28 +00:00
var vis_before = getprop(lwi~"visibility-m");
# determine at which distance we no longer keep an interpolation point, needs to be larger for METAR since points are more scarce
if (metar_flag == 1)
{var distance_to_unload = 250000.0;}
else
{var distance_to_unload = 120000.0;}
# if we can set environment without a reset, the loop can run a bit faster for smoother interpolation
# so determine the suitable timing
if (compat_layer.features.can_disable_environment == 1)
{var interpolation_loop_time = 0.2; var vlimit = 1.01;}
else
{var interpolation_loop_time = 1.0; var vlimit = 1.05;}
2010-04-12 20:51:46 +00:00
# get an inverse distance weighted average from all defined weather stations
var n_stations = size(weatherStationArray);
for (var i = 0; i < n_stations; i=i+1) {
2010-04-12 20:51:46 +00:00
s = weatherStationArray[i];
2011-01-05 12:36:10 +00:00
2010-04-12 20:51:46 +00:00
var stpos = geo.Coord.new();
stpos.set_latlon(s.lat,s.lon,0.0);
2010-04-12 20:51:46 +00:00
var d = viewpos.distance_to(stpos);
if (d <100.0) {d = 100.0;} # to prevent singularity at zero
sum_norm = sum_norm + 1./d * s.weight;
2011-03-19 13:03:28 +00:00
sum_alt = sum_alt + (s.alt/d) * s.weight;
sum_vis = sum_vis + (s.vis/d) * s.weight;
sum_T = sum_T + (s.T/d) * s.weight;
sum_D = sum_D + (s.D/d) * s.weight;
sum_p = sum_p + (s.p/d) * s.weight;
# gradually fade in the interpolation weight of newly added stations to
# avoid sudden jumps
2011-03-19 13:03:28 +00:00
if (s.weight < 1.0) {s.weight = s.weight + 0.02;}
2010-06-04 21:05:32 +00:00
# automatically delete stations out of range
# take care not to unload if weird values appear for a moment
2011-01-05 12:36:10 +00:00
# never unload if only one station left
2011-03-19 13:03:28 +00:00
if ((d > distance_to_unload) and (d < (distance_to_unload + 20000.0)) and (n_stations > 1))
{
if (debug_output_flag == 1)
{print("Distance to weather station ", d, " m, unloading ...", i);}
weatherStationArray = weather_tile_management.delete_from_vector(weatherStationArray,i);
i = i-1; n_stations = n_stations -1;
}
2010-04-12 20:51:46 +00:00
}
setprop(lwi~"station-number", i);
2011-03-19 13:03:28 +00:00
var ialt = sum_alt/sum_norm;
2010-04-12 20:51:46 +00:00
var vis = sum_vis/sum_norm;
var p = sum_p/sum_norm;
var D = sum_D/sum_norm;
var T = sum_T/sum_norm;
2011-03-19 13:03:28 +00:00
# altitude model for visibility - increase above the lowest inversion layer to simulate ground haze
2010-06-04 21:05:32 +00:00
2010-08-03 06:18:14 +00:00
var altitude = getprop("position/altitude-ft");
2011-03-19 13:03:28 +00:00
var current_tile_index = getprop(lw~"tiles/tile[4]/tile-index");
#if (presampling_flag == 1)
# {var current_mean_terrain_elevation = alt_20_array[current_tile_index -1];}
#else
# {var current_mean_terrain_elevation = getprop(lw~"tmp/tile-alt-offset-ft");}
current_mean_terrain_elevation = ialt;
var alt1 = weather_dynamics.tile_convective_altitude[current_tile_index -1];
var alt2 = alt1 + 1500.0;
var inc1 = 0.2;
var inc2 = 5.0;
var inc3 = 1.0;
2010-06-04 21:05:32 +00:00
2011-03-19 13:03:28 +00:00
var alt_above_mean = altitude - current_mean_terrain_elevation;
if (alt_above_mean < alt1)
{vis = vis + inc1 * alt_above_mean;}
else if (alt_above_mean < alt2)
{vis = vis + inc1 * alt1 + inc2 * (alt_above_mean - alt1);}
else if (alt_above_mean > alt2)
{vis = vis + inc1 * alt1 + inc2 * (alt2-alt1) + inc3 * (alt_above_mean - alt2);}
# limit relative changes of the visibility, will make for gradual transitions
if (vis/vis_before > vlimit)
{vis = vlimit * vis_before;}
else if (vis/vis_before < (2.0-vlimit))
{vis = (2.0-vlimit) * vis_before;}
# write all properties into the weather interpolation record in the property tree
setprop(lwi~"mean-terrain-altitude-ft",ialt);
if (vis > 0.0) {setprop(lwi~"visibility-m",vis);} # a redundancy check
setprop(lwi~"temperature-degc",T);
setprop(lwi~"dewpoint-degc",D);
if (p > 10.0) {setprop(lwi~"pressure-sea-level-inhg",p);}
setprop(lwi~"turbulence",0.0);
2010-04-12 20:51:46 +00:00
# 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();
2010-06-04 21:05:32 +00:00
if ((flag ==0) and (vis > 0.0))
2010-04-12 20:51:46 +00:00
{
cNode.getNode("visibility-m").setValue(vis);
2010-08-03 06:18:14 +00:00
compat_layer.setVisibility(vis);
2010-04-12 20:51:46 +00:00
}
2010-06-15 18:54:44 +00:00
flag = props.globals.getNode("local-weather/effect-volumes/number-active-turb").getValue();
if ((flag ==0))
{
cNode.getNode("turbulence").setValue(0.0);
2010-08-03 06:18:14 +00:00
compat_layer.setTurbulence(0.0);
2010-06-15 18:54:44 +00:00
}
2010-06-04 21:05:32 +00:00
flag = props.globals.getNode("local-weather/effect-volumes/number-active-lift").getValue();
if (flag ==0)
{
cNode.getNode("thermal-lift").setValue(0.0);
}
2010-04-12 20:51:46 +00:00
# no need to check for these, as they are not modelled in effect volumes
cNode.getNode("temperature-degc",1).setValue(T);
2010-08-03 06:18:14 +00:00
compat_layer.setTemperature(T);
2010-04-12 20:51:46 +00:00
cNode.getNode("dewpoint-degc",1).setValue(D);
2010-08-03 06:18:14 +00:00
compat_layer.setDewpoint(D);
if (p>0.0) {cNode.getNode("pressure-sea-level-inhg",1).setValue(p); compat_layer.setPressure(p);}
# now determine the local wind
var tile_index = props.globals.getNode(lw~"tiles").getChild("tile",4).getNode("tile-index").getValue();
if (wind_model_flag ==1) # constant
{
var winddir = weather_dynamics.tile_wind_direction[0];
var windspeed = weather_dynamics.tile_wind_speed[0];
}
else if (wind_model_flag ==2) # constant in tile
{
var winddir = weather_dynamics.tile_wind_direction[tile_index-1];
var windspeed = weather_dynamics.tile_wind_speed[tile_index-1];
}
else if (wind_model_flag ==3) # aloft interpolated, constant in tiles
{
2011-03-19 13:03:28 +00:00
var w = windIpointArray[0];
2010-08-03 06:18:14 +00:00
var res = wind_altitude_interpolation(altitude,w);
var winddir = res[0];
var windspeed = res[1];
}
else if (wind_model_flag == 5) # aloft waypoint interpolated
{
2011-03-19 13:03:28 +00:00
var res = wind_interpolation(viewpos.lat(), viewpos.lon(), altitude);
2010-08-03 06:18:14 +00:00
var winddir = res[0];
var windspeed = res[1];
}
# now do the boundary layer computations
var altitude_agl = getprop("/position/altitude-agl-ft");
2010-09-10 06:52:21 +00:00
if (presampling_flag == 0)
2010-08-03 06:18:14 +00:00
{
var boundary_alt = 600.0;
var windspeed_ground = windspeed/3.0;
2011-03-19 13:03:28 +00:00
var f_min = 2.0/3.0;
2010-08-03 06:18:14 +00:00
if (altitude_agl < boundary_alt)
2010-09-10 06:52:21 +00:00
{var windspeed_current = windspeed_ground + 2.0 * windspeed_ground * (altitude_agl/boundary_alt);}
2010-08-03 06:18:14 +00:00
else
{var windspeed_current = windspeed;}
}
2010-09-10 06:52:21 +00:00
else
2010-08-03 06:18:14 +00:00
{
var alt_median = alt_50_array[tile_index - 1];
var alt_difference = alt_median - (altitude - altitude_agl);
var base_layer_thickness = 150.0;
# get the boundary layer size dependent on terrain altitude above terrain median
if (alt_difference > 0.0) # we're low and the boundary layer grows
{var boundary_alt = base_layer_thickness + 0.3 * alt_difference;}
else # the boundary layer shrinks
{var boundary_alt = base_layer_thickness + 0.1 * alt_difference;}
if (boundary_alt < 50.0){boundary_alt = 50.0;}
if (boundary_alt > 3000.0) {boundary_alt = 3000.0;}
# get the boundary effect as a function of bounday layer size
var f_min = 0.2 + 0.17 * math.ln(boundary_alt/base_layer_thickness);
if (altitude_agl < boundary_alt)
{
var windspeed_current = (1-f_min) * windspeed + f_min * windspeed * (altitude_agl/boundary_alt);
}
else
{var windspeed_current = windspeed;}
}
2010-04-12 20:51:46 +00:00
2011-03-19 13:03:28 +00:00
# determine gusts and turbulence in the bounday layer
var gust_frequency = getprop(lw~"tmp/gust-frequency-hz");
if (gust_frequency > 0.0)
{
var gust_relative_strength = getprop(lw~"tmp/gust-relative-strength");
var gust_angvar = getprop(lw~"tmp/gust-angular-variation-deg");
var alt_scaling_factor = 1.2 * windspeed / 10.0;
if (alt_scaling_factor < 1.0) {alt_scaling_factor = 1.0;}
# expected mean number of gusts in time interval (should be < 1)
var p_gust = gust_frequency * interpolation_loop_time;
if (rand() < p_gust) # we change the offsets for windspeed and direction
{
var alt_fact = 1.0 - altitude_agl/(boundary_alt * alt_scaling_factor);
if (alt_fact < 0.0) {alt_fact = 0.0};
windspeed_multiplier = (1.0 + ((rand()) * gust_relative_strength * alt_fact));
winddir_change = alt_fact * (1.0 - 2.0 * rand() * gust_angvar);
}
windspeed_current = windspeed_current * windspeed_multiplier;
winddir = winddir + winddir_change;
}
2010-04-12 20:51:46 +00:00
2010-08-03 06:18:14 +00:00
compat_layer.setWindSmoothly(winddir, windspeed_current);
iNode.getNode("wind-from-heading-deg").setValue(winddir);
iNode.getNode("wind-speed-kt").setValue(windspeed_current);
cNode.getNode("wind-from-heading-deg").setValue(winddir);
cNode.getNode("wind-speed-kt").setValue(windspeed_current);
2011-03-19 13:03:28 +00:00
if (getprop(lw~"interpolation-loop-flag") ==1) {settimer(interpolation_loop, interpolation_loop_time);}
2010-04-12 20:51:46 +00:00
}
###################################
# thermal lift loop startup
###################################
var thermal_lift_start = func (ev) {
# copy the properties from effect volume to the lift object
l = thermalLift.new(ev.lat, ev.lon, ev.radius, ev.height, ev.cn, ev.sh, ev.max_lift, ev.f_lift_radius);
l.index = ev.index;
if (dynamics_flag == 1)
{
l.timestamp = weather_dynamics.time_lw;
if (dynamical_convection_flag == 1)
{
l.flt = ev.flt;
l.evolution_timestamp = ev.evolution_timestamp;
}
}
thermal = l;
if (debug_output_flag == 1)
{
print("Entering thermal lift...");
print("strength: ", thermal.max_lift, " radius: ", thermal.radius);
if (dynamical_convection_flag ==1)
{print("fractional lifetime: ", thermal.flt);}
}
# 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); settimer(thermal_lift_loop,0);}
}
2010-06-04 21:05:32 +00:00
###################################
# thermal lift loop
###################################
var thermal_lift_loop = func {
var apos = geo.aircraft_position();
var tlat = thermal.lat;
var tlon = thermal.lon;
2010-06-04 21:05:32 +00:00
var tpos = geo.Coord.new();
tpos.set_latlon(tlat,tlon,0.0);
var d = apos.distance_to(tpos);
var alt = getprop("position/altitude-ft");
if (dynamical_convection_flag == 1)
{var flt = thermal.flt;}
else
{var flt = 0.5;}
var lift = calcLift(d, alt, thermal.radius, thermal.height, thermal.cn, thermal.sh, thermal.max_lift, thermal.f_lift_radius, flt);
2010-06-04 21:05:32 +00:00
if (getprop(lw~"wave-loop-flag") ==1)
{
lift = lift + getprop(lw~"current/wave-lift");
}
2010-06-04 21:05:32 +00:00
setprop(lw~"current/thermal-lift",lift);
compat_layer.setLift(lift);
# if dynamics is on, move the thermal and occasionally compute altitude and age
if (dynamics_flag == 1)
{
thermal.move();
if ((rand() < 0.01) and (presampling_flag == 1)) # check every 100 frames
{
if (dynamical_convection_flag == 1)
{
thermal.correct_altitude_and_age();
if (thermal.flt > 1.1)
{thermal_lift_stop();}
}
else
{
thermal.correct_altitude();
}
}
}
2010-06-04 21:05:32 +00:00
if (getprop(lw~"lift-loop-flag") ==1) {settimer(thermal_lift_loop, 0);}
}
2010-06-04 21:05:32 +00:00
###################################
# thermal lift loop stop
2010-06-04 21:05:32 +00:00
###################################
var thermal_lift_stop = func {
setprop(lw~"lift-loop-flag",0);
setprop(lw~"current/thermal-lift",0.0);
compat_layer.setLift(0.0);
if (debug_output_flag == 1)
{
print("Leaving thermal lift...");
}
}
2010-06-04 21:05:32 +00:00
###################################
# wave lift loop startup
###################################
var wave_lift_start = func (ev) {
# copy the properties from effect volume to the wave object
2010-06-04 21:05:32 +00:00
2010-08-03 06:18:14 +00:00
w = waveLift.new (ev.lat, ev.lon, ev.r1, ev.r2, ev.phi, ev.height, ev.max_lift);
w.index = ev.index;
wave = w;
2010-06-04 21:05:32 +00:00
# and start the lift loop, unless another one is already running
# so we block overlapping calls
if (getprop(lw~"wave-loop-flag") == 0)
{setprop(lw~"wave-loop-flag",1); settimer(wave_lift_loop,0);}
2010-06-04 21:05:32 +00:00
}
###################################
# wave lift loop
2010-06-04 21:05:32 +00:00
###################################
var wave_lift_loop = func {
2010-06-04 21:05:32 +00:00
var lat = getprop("position/latitude-deg");
var lon = getprop("position/longitude-deg");
var alt = getprop("position/altitude-ft");
2010-08-03 06:18:14 +00:00
var phi = wave.phi * math.pi/180.0;
var xx = (lon - wave.lon) * lon_to_m;
var yy = (lat - wave.lat) * lat_to_m;
var x = xx * math.cos(phi) - yy * math.sin(phi);
var y = yy * math.cos(phi) + xx * math.sin(phi);
var lift = calcWaveLift(x,y,alt);
# check if we are in a thermal, if so set wave lift and let the thermal lift loop add that
if (getprop(lw~"lift-loop-flag") ==1)
{
setprop(lw~"current/wave-lift",lift);
}
else
{
setprop(lw~"current/thermal-lift",lift);
}
if (getprop(lw~"wave-loop-flag") ==1) {settimer(wave_lift_loop, 0);}
}
###################################
# wave lift loop stop
###################################
var wave_lift_stop = func {
setprop(lw~"wave-loop-flag",0);
2010-06-04 21:05:32 +00:00
setprop(lw~"current/thermal-lift",0.0);
}
2010-04-12 20:51:46 +00:00
####################################
# action taken when in effect volume
####################################
var effect_volume_start = func (ev) {
var cNode = props.globals.getNode(lw~"current");
if (ev.vis_flag ==1)
2010-04-12 20:51:46 +00:00
{
# first store the current setting in case we need to restore on leaving
var vis = ev.vis;
ev.vis_r = cNode.getNode("visibility-m").getValue();
2010-04-12 20:51:46 +00:00
# then set the new value in current and execute change
cNode.getNode("visibility-m").setValue(vis);
2010-08-03 06:18:14 +00:00
compat_layer.setVisibility(vis);
2010-04-12 20:51:46 +00:00
# then count the number of active volumes on entry (we need that to determine
# what to do on exit)
ev.n_entry_vis = getprop(lw~"effect-volumes/number-active-vis");
2010-04-12 20:51:46 +00:00
# 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)
if (ev.rain_flag == 1)
2010-04-12 20:51:46 +00:00
{
var rain = ev.rain;
ev.rain_r = cNode.getNode("rain-norm").getValue();
2010-04-12 20:51:46 +00:00
cNode.getNode("rain-norm").setValue(rain);
2010-08-03 06:18:14 +00:00
compat_layer.setRain(rain);
ev.n_entry_rain = getprop(lw~"effect-volumes/number-active-rain");
2010-04-12 20:51:46 +00:00
setprop(lw~"effect-volumes/number-active-rain",getprop(lw~"effect-volumes/number-active-rain")+1);
}
if (ev.snow_flag == 1)
2010-04-12 20:51:46 +00:00
{
var snow = ev.snow;
ev.snow_r = cNode.getNode("snow-norm").getValue();
2010-04-12 20:51:46 +00:00
cNode.getNode("snow-norm").setValue(snow);
2010-08-03 06:18:14 +00:00
compat_layer.setSnow(snow);
ev.n_entry_snow = getprop(lw~"effect-volumes/number-active-snow");
2010-04-12 20:51:46 +00:00
setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")+1);
}
if (ev.turb_flag == 1)
2010-06-15 18:54:44 +00:00
{
var turbulence = ev.turb;
ev.turb_r = cNode.getNode("turbulence").getValue();
2010-06-15 18:54:44 +00:00
cNode.getNode("turbulence").setValue(turbulence);
2010-08-03 06:18:14 +00:00
compat_layer.setTurbulence(turbulence);
ev.n_entry_turb = getprop(lw~"effect-volumes/number-active-turb");
2010-06-15 18:54:44 +00:00
setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")+1);
}
if (ev.sat_flag == 1)
{
var saturation = ev.sat;
ev.sat_r = getprop("/rendering/scene/saturation");
compat_layer.setLight(saturation);
ev.n_entry_sat = getprop(lw~"effect-volumes/number-active-sat");
setprop(lw~"effect-volumes/number-active-sat",getprop(lw~"effect-volumes/number-active-sat")+1);
}
2010-06-15 18:54:44 +00:00
if (ev.lift_flag == 1)
2010-04-12 20:51:46 +00:00
{
var lift = ev.lift;
ev.lift_r = cNode.getNode("thermal-lift").getValue();
2010-04-12 20:51:46 +00:00
cNode.getNode("thermal-lift").setValue(lift);
compat_layer.setLift(lift);
ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift");
2010-04-12 20:51:46 +00:00
setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1);
}
else if (ev.lift_flag == 2)
2010-06-04 21:05:32 +00:00
{
ev.lift_r = cNode.getNode("thermal-lift").getValue();
ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift");
2010-06-04 21:05:32 +00:00
setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1);
thermal_lift_start(ev);
}
else if (ev.lift_flag == 3)
{
ev.lift_r = cNode.getNode("thermal-lift").getValue();
ev.n_entry_lift = getprop(lw~"effect-volumes/number-active-lift");
setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")+1);
wave_lift_start(ev);
}
2010-04-12 20:51:46 +00:00
}
var effect_volume_stop = func (ev) {
var cNode = props.globals.getNode(lw~"current");
if (ev.vis_flag == 1)
2010-04-12 20:51:46 +00:00
{
var n_active = getprop(lw~"effect-volumes/number-active-vis");
2010-04-12 20:51:46 +00:00
var n_entry = ev.n_entry_vis;
2010-04-12 20:51:46 +00:00
# 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();}
{var vis = ev.vis_r;}
2010-04-12 20:51:46 +00:00
else {var vis = cNode.getNode("visibility-m").getValue();}
cNode.getNode("visibility-m").setValue(vis);
2010-08-03 06:18:14 +00:00
compat_layer.setVisibility(vis);
2010-04-12 20:51:46 +00:00
# and subtract from the counter
setprop(lw~"effect-volumes/number-active-vis",getprop(lw~"effect-volumes/number-active-vis")-1);
}
if (ev.rain_flag == 1)
2010-04-12 20:51:46 +00:00
{
var n_active = getprop(lw~"effect-volumes/number-active-rain");
var n_entry = ev.n_entry_rain;
2010-04-12 20:51:46 +00:00
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();}
{var rain = ev.rain_r;}
2010-04-12 20:51:46 +00:00
else {var rain = cNode.getNode("rain-norm").getValue();}
cNode.getNode("rain-norm").setValue(rain);
2010-08-03 06:18:14 +00:00
compat_layer.setRain(rain);
2010-04-12 20:51:46 +00:00
setprop(lw~"effect-volumes/number-active-rain",getprop(lw~"effect-volumes/number-active-rain")-1);
}
if (ev.snow_flag == 1)
2010-04-12 20:51:46 +00:00
{
var n_active = getprop(lw~"effect-volumes/number-active-snow");
var n_entry = ev.n_entry_snow;
2010-04-12 20:51:46 +00:00
if (n_active ==1){var snow = props.globals.getNode(lw~"interpolation/snow-norm").getValue();}
else if ((n_active -1) == n_entry)
{var snow = ev.snow_r;}
2010-04-12 20:51:46 +00:00
else {var snow = cNode.getNode("snow-norm").getValue();}
cNode.getNode("snow-norm").setValue(snow);
2010-08-03 06:18:14 +00:00
compat_layer.setSnow(snow);
2010-04-12 20:51:46 +00:00
setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")-1);
}
if (ev.turb_flag == 1)
2010-06-15 18:54:44 +00:00
{
var n_active = getprop(lw~"effect-volumes/number-active-turb");
var n_entry = ev.n_entry_turb;
2010-06-15 18:54:44 +00:00
if (n_active ==1){var turbulence = props.globals.getNode(lw~"interpolation/turbulence").getValue();}
else if ((n_active -1) == n_entry)
{var turbulence = ev.turb_r;}
2010-06-15 18:54:44 +00:00
else {var turbulence = cNode.getNode("turbulence").getValue();}
cNode.getNode("turbulence").setValue(turbulence);
2010-08-03 06:18:14 +00:00
compat_layer.setTurbulence(turbulence);
2010-06-15 18:54:44 +00:00
setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")-1);
}
if (ev.sat_flag == 1)
{
var n_active = getprop(lw~"effect-volumes/number-active-sat");
var n_entry = ev.n_entry_sat;
if (n_active ==1){var saturation = 1.0;}
else if ((n_active -1) == n_entry)
{var saturation = ev.sat_r;}
else {var saturation = getprop("/rendering/scene/saturation");}
compat_layer.setLight(saturation);
setprop(lw~"effect-volumes/number-active-sat",getprop(lw~"effect-volumes/number-active-sat")-1);
}
if (ev.lift_flag == 1)
2010-04-12 20:51:46 +00:00
{
var n_active = getprop(lw~"effect-volumes/number-active-lift");
var n_entry = ev.n_entry_lift;
2010-04-12 20:51:46 +00:00
if (n_active ==1){var lift = props.globals.getNode(lw~"interpolation/thermal-lift").getValue();}
else if ((n_active -1) == n_entry)
{var lift = ev.lift_r;}
2010-04-12 20:51:46 +00:00
else {var lift = cNode.getNode("thermal-lift").getValue();}
cNode.getNode("thermal-lift").setValue(lift);
compat_layer.setLift(lift);
2010-06-04 21:05:32 +00:00
setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1);
}
else if (ev.lift_flag == 2)
2010-06-04 21:05:32 +00:00
{
thermal_lift_stop();
2010-04-12 20:51:46 +00:00
setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1);
}
else if (ev.lift_flag == 3)
2010-06-04 21:05:32 +00:00
{
wave_lift_stop();
setprop(lw~"effect-volumes/number-active-lift",getprop(lw~"effect-volumes/number-active-lift")-1);
2010-06-04 21:05:32 +00:00
}
}
2010-06-04 21:05:32 +00:00
#########################################
# compute thermal lift in detailed model
#########################################
var ts_factor = func (t, alt, height) {
var t1 = 0.1; # fractional time at which lift is fully developed
var t2 = 0.9; # fractional time at which lift starts to decay
var t3 = 1.0; # fractional time at which lift is gone
2010-06-04 21:05:32 +00:00
# no time dependence modelled yet
# return 1.0;
2010-06-04 21:05:32 +00:00
var t_a = t - (alt/height) * t1 - t1;
2010-06-04 21:05:32 +00:00
if (t_a<0) {return 0.0;}
else if (t_a<t1) {return 0.5 + 0.5 * math.cos((1.0-t_a/t1)* math.pi);}
else if (t_a < t2) {return 1.0;}
else {return 0.5 - 0.5 * math.cos((1.0-(t2-t_a)/(t3-t2))*math.pi);}
2010-06-04 21:05:32 +00:00
}
var tl_factor = func (t, alt, height) {
var t1 = 0.1; # fractional time at which lift is fully developed
var t2 = 0.9; # fractional time at which lift starts to decay
var t3 = 1.0; # fractional time at which lift is gone
2010-06-04 21:05:32 +00:00
# no time dependence modelled yet
# return 1.0;
2010-06-04 21:05:32 +00:00
var t_a = t - (alt/height) * t1;
if (t_a<0) {return 0.0;}
else if (t_a<t1) {return 0.5 + 0.5 * math.cos((1.0-t_a/t1)* math.pi);}
else if (t_a < t2) {return 1.0;}
else {return 0.5 - 0.5 * math.cos((1.0-(t2-t_a)/(t3-t2))*math.pi);}
2010-06-04 21:05:32 +00:00
}
2010-04-12 20:51:46 +00:00
2010-06-04 21:05:32 +00:00
var calcLift_max = func (alt, max_lift, height) {
alt_agl = getprop("/position/altitude-agl-ft");
2010-06-04 21:05:32 +00:00
# no lift below ground
if (alt_agl < 0.0) {return 0.0;}
2010-06-04 21:05:32 +00:00
# lift ramps up to full within 200 m
else if (alt_agl < 200.0*m_to_ft)
{return max_lift * 0.5 * (1.0 + math.cos((1.0-alt_agl/(200.0*m_to_ft))*math.pi));}
2010-06-04 21:05:32 +00:00
# constant max. lift in main body
else if ((alt_agl > 200.0*m_to_ft) and (alt < height))
2010-06-04 21:05:32 +00:00
{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)/height)*math.pi));}
2010-06-04 21:05:32 +00:00
# no lift available above
else {return 0.0;}
2010-04-12 20:51:46 +00:00
}
2010-06-04 21:05:32 +00:00
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)));
}
}
#########################################
# compute wave lift in detailed model
#########################################
var calcWaveLift = func (x,y, alt) {
var lift = wave.max_lift * math.cos((y/wave.y) * 1.5 * math.pi);
if (abs(x)/wave.x > 0.9)
{
lift = lift * (abs(x) - 0.9 * wave.x)/(0.1 * wave.x);
}
lift = lift * 2.71828 * math.exp(-alt/wave.height) * alt/wave.height;
var alt_agl = getprop("/position/altitude-agl-ft");
if (alt_agl < 1000.0)
{
lift = lift * (alt_agl/1000.0) * (alt_agl/1000.0);
}
2010-06-04 21:05:32 +00:00
return lift;
}
2010-04-12 20:51:46 +00:00
###########################################################
# select a cloud model
###########################################################
var select_cloud_model = func(type, subtype) {
var rn = rand();
var path="Models/Weather/blank.ac";
if (type == "Cumulus"){
if (subtype == "small") {
if (rn > 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";}
}
}
2010-06-04 21:05:32 +00:00
else if (type == "Cumulus (cloudlet)"){
if (subtype == "small") {
2010-08-03 06:18:14 +00:00
if (rn > 0.875) {path = "Models/Weather/cumulus_small_sl1.xml";}
else if (rn > 0.750) {path = "Models/Weather/cumulus_small_sl2.xml";}
else if (rn > 0.625) {path = "Models/Weather/cumulus_small_sl3.xml";}
else if (rn > 0.500) {path = "Models/Weather/cumulus_small_sl4.xml";}
else if (rn > 0.375) {path = "Models/Weather/cumulus_small_sl5.xml";}
else if (rn > 0.250) {path = "Models/Weather/cumulus_small_sl6.xml";}
else if (rn > 0.125) {path = "Models/Weather/cumulus_small_sl7.xml";}
else {path = "Models/Weather/cumulus_small_sl8.xml";}
}
else if (subtype == "large") {
if (rn > 0.9) {path = "Models/Weather/cumulus_sl1.xml";}
else if (rn > 0.8) {path = "Models/Weather/cumulus_sl2.xml";}
else if (rn > 0.7) {path = "Models/Weather/cumulus_sl3.xml";}
else if (rn > 0.6) {path = "Models/Weather/cumulus_sl4.xml";}
else if (rn > 0.5) {path = "Models/Weather/cumulus_sl5.xml";}
else if (rn > 0.4) {path = "Models/Weather/cumulus_sl6.xml";}
else if (rn > 0.3) {path = "Models/Weather/cumulus_sl7.xml";}
else if (rn > 0.2) {path = "Models/Weather/cumulus_sl8.xml";}
else if (rn > 0.1) {path = "Models/Weather/cumulus_sl9.xml";}
else {path = "Models/Weather/cumulus_sl10.xml";}
}
}
else if (type == "Congestus"){
if (subtype == "small") {
if (rn > 0.9) {path = "Models/Weather/cumulus_sl1.xml";}
else if (rn > 0.8) {path = "Models/Weather/cumulus_sl2.xml";}
else if (rn > 0.7) {path = "Models/Weather/cumulus_sl3.xml";}
else if (rn > 0.6) {path = "Models/Weather/cumulus_sl4.xml";}
else if (rn > 0.5) {path = "Models/Weather/cumulus_sl5.xml";}
else if (rn > 0.4) {path = "Models/Weather/cumulus_small_sl4.xml";}
else if (rn > 0.3) {path = "Models/Weather/cumulus_small_sl5.xml";}
else if (rn > 0.2) {path = "Models/Weather/cumulus_small_sl6.xml";}
else if (rn > 0.1) {path = "Models/Weather/cumulus_small_sl7.xml";}
else {path = "Models/Weather/cumulus_small_sl8.xml";}
}
else if (subtype == "large") {
if (rn > 0.8) {path = "Models/Weather/congestus_sl1.xml";}
else if (rn > 0.6) {path = "Models/Weather/congestus_sl2.xml";}
else if (rn > 0.4) {path = "Models/Weather/congestus_sl3.xml";}
else if (rn > 0.2) {path = "Models/Weather/congestus_sl4.xml";}
else {path = "Models/Weather/congestus_sl5.xml";}
}
}
else if (type == "Stratocumulus"){
if (subtype == "small") {
if (rn > 0.8) {path = "Models/Weather/stratocumulus_small1.xml";}
else if (rn > 0.6) {path = "Models/Weather/stratocumulus_small2.xml";}
else if (rn > 0.4) {path = "Models/Weather/stratocumulus_small3.xml";}
else if (rn > 0.2) {path = "Models/Weather/stratocumulus_small4.xml";}
else {path = "Models/Weather/stratocumulus_small5.xml";}
}
else if (subtype == "large") {
if (rn > 0.8) {path = "Models/Weather/stratocumulus_sl1.xml";}
else if (rn > 0.6) {path = "Models/Weather/stratocumulus_sl2.xml";}
else if (rn > 0.4) {path = "Models/Weather/stratocumulus_sl3.xml";}
else if (rn > 0.2) {path = "Models/Weather/stratocumulus_sl4.xml";}
else {path = "Models/Weather/stratocumulus_sl5.xml";}
}
}
else if (type == "Cumulus (whisp)"){
if (subtype == "small") {
if (rn > 0.8) {path = "Models/Weather/cumulus_whisp1.xml";}
else if (rn > 0.6) {path = "Models/Weather/cumulus_whisp2.xml";}
else if (rn > 0.4) {path = "Models/Weather/cumulus_whisp3.xml";}
else if (rn > 0.2) {path = "Models/Weather/cumulus_whisp4.xml";}
else {path = "Models/Weather/cumulus_whisp5.xml";}
}
else if (subtype == "large") {
if (rn > 0.8) {path = "Models/Weather/cumulus_whisp1.xml";}
else if (rn > 0.6) {path = "Models/Weather/cumulus_whisp2.xml";}
else if (rn > 0.4) {path = "Models/Weather/cumulus_whisp3.xml";}
else if (rn > 0.2) {path = "Models/Weather/cumulus_whisp4.xml";}
else {path = "Models/Weather/cumulus_whisp5.xml";}
}
2010-08-03 06:18:14 +00:00
}
else if (type == "Cumulus bottom"){
if (subtype == "small") {
if (rn > 0.0) {path = "Models/Weather/cumulus_bottom1.xml";}
}
else if (subtype == "large") {
if (rn > 0.0) {path = "Models/Weather/cumulus_bottom1.xml";}
}
}
else if (type == "Congestus bottom"){
if (subtype == "small") {
if (rn > 0.0) {path = "Models/Weather/congestus_bottom1.xml";}
2010-06-04 21:05:32 +00:00
}
else if (subtype == "large") {
2010-08-03 06:18:14 +00:00
if (rn > 0.0) {path = "Models/Weather/congestus_bottom1.xml";}
2010-06-15 18:54:44 +00:00
}
}
else if (type == "Stratocumulus bottom"){
if (subtype == "small") {
if (rn > 0.0) {path = "Models/Weather/stratocumulus_bottom1.xml";}
}
else if (subtype == "large") {
if (rn > 0.0) {path = "Models/Weather/stratocumulus_bottom1.xml";}
}
}
2010-06-15 18:54:44 +00:00
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";}
2010-06-04 21:05:32 +00:00
}
}
2010-04-12 20:51:46 +00:00
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";}
}
}
2010-08-03 06:18:14 +00:00
2010-04-12 20:51:46 +00:00
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 == "Altocumulus perlucidus"){
if (subtype == "small") {
if (rn > 0.8) {path = "Models/Weather/altocumulus_thinlayer6.xml";}
else if (rn > 0.6) {path = "Models/Weather/altocumulus_thinlayer7.xml";}
else if (rn > 0.4) {path = "Models/Weather/altocumulus_thinlayer8.xml";}
else if (rn > 0.2) {path = "Models/Weather/altocumulus_thinlayer9.xml";}
else {path = "Models/Weather/altocumulus_thinlayer10.xml";}
}
else if (subtype == "large") {
if (rn > 0.8) {path = "Models/Weather/altocumulus_thinlayer1.xml";}
else if (rn > 0.6) {path = "Models/Weather/altocumulus_thinlayer2.xml";}
else if (rn > 0.4) {path = "Models/Weather/altocumulus_thinlayer3.xml";}
else if (rn > 0.2) {path = "Models/Weather/altocumulus_thinlayer4.xml";}
else {path = "Models/Weather/altocumulus_thinlayer5.xml";}
}
}
2010-04-12 20:51:46 +00:00
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") {
2010-06-04 21:05:32 +00:00
if (subtype == "large") {
2010-09-10 06:52:21 +00:00
if (rn > 0.833) {path = "Models/Weather/cirrus1.xml";}
else if (rn > 0.666) {path = "Models/Weather/cirrus2.xml";}
else if (rn > 0.5) {path = "Models/Weather/cirrus3.xml";}
else if (rn > 0.333) {path = "Models/Weather/cirrus4.xml";}
else if (rn > 0.166) {path = "Models/Weather/cirrus5.xml";}
else {path = "Models/Weather/cirrus6.xml";}
2010-04-12 20:51:46 +00:00
}
2010-06-04 21:05:32 +00:00
else if (subtype == "small") {
2010-09-10 06:52:21 +00:00
if (rn > 0.75) {path = "Models/Weather/cirrus_amorphous1.xml";}
else if (rn > 0.5) {path = "Models/Weather/cirrus_amorphous2.xml";}
else if (rn > 0.25) {path = "Models/Weather/cirrus_amorphous3.xml";}
else {path = "Models/Weather/cirrus_amorphous4.xml";}
2010-04-12 20:51:46 +00:00
}
}
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") {
2010-09-10 06:52:21 +00:00
if (rn > 0.875) {path = "Models/Weather/cirrocumulus1.xml";}
else if (rn > 0.750){path = "Models/Weather/cirrocumulus4.xml";}
else if (rn > 0.625){path = "Models/Weather/cirrocumulus5.xml";}
else if (rn > 0.500){path = "Models/Weather/cirrocumulus6.xml";}
else if (rn > 0.385){path = "Models/Weather/cirrocumulus7.xml";}
else if (rn > 0.250){path = "Models/Weather/cirrocumulus8.xml";}
else if (rn > 0.125){path = "Models/Weather/cirrocumulus9.xml";}
else {path = "Models/Weather/cirrocumulus10.xml";}
2010-04-12 20:51:46 +00:00
}
}
2010-06-15 18:54:44 +00:00
else if (type == "Cirrocumulus (cloudlet)") {
if (subtype == "small") {
2010-08-03 06:18:14 +00:00
if (rn > 0.8) {path = "Models/Weather/cirrocumulus_cloudlet6.xml";}
else if (rn > 0.6) {path = "Models/Weather/cirrocumulus_cloudlet7.xml";}
else if (rn > 0.4) {path = "Models/Weather/cirrocumulus_cloudlet8.xml";}
else if (rn > 0.2) {path = "Models/Weather/cirrocumulus_cloudlet9.xml";}
else {path = "Models/Weather/cirrocumulus_cloudlet10.xml";}
2010-06-15 18:54:44 +00:00
}
else if (subtype == "large") {
2010-08-03 06:18:14 +00:00
if (rn > 0.8) {path = "Models/Weather/cirrocumulus_cloudlet1.xml";}
else if (rn > 0.6) {path = "Models/Weather/cirrocumulus_cloudlet2.xml";}
else if (rn > 0.4) {path = "Models/Weather/cirrocumulus_cloudlet3.xml";}
else if (rn > 0.2) {path = "Models/Weather/cirrocumulus_cloudlet4.xml";}
else {path = "Models/Weather/cirrocumulus_cloudlet5.xml";}
2010-06-15 18:54:44 +00:00
}
}
2010-04-12 20:51:46 +00:00
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";}
}
}
2010-06-04 21:05:32 +00:00
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/single_cloud.xml";}
2010-06-15 18:54:44 +00:00
else if (type == "Box_test") {
2010-08-03 06:18:14 +00:00
if (subtype == "standard") {
if (rn > 0.8) {path = "Models/Weather/cloudbox1.xml";}
else if (rn > 0.6) {path = "Models/Weather/cloudbox2.xml";}
else if (rn > 0.4) {path = "Models/Weather/cloudbox3.xml";}
else if (rn > 0.2) {path = "Models/Weather/cloudbox4.xml";}
else {path = "Models/Weather/cloudbox5.xml";}
2010-08-03 06:18:14 +00:00
}
else if (subtype == "core") {
if (rn > 0.8) {path = "Models/Weather/cloudbox_core1.xml";}
else if (rn > 0.6) {path = "Models/Weather/cloudbox_core2.xml";}
else if (rn > 0.4) {path = "Models/Weather/cloudbox_core3.xml";}
else if (rn > 0.2) {path = "Models/Weather/cloudbox_core4.xml";}
else {path = "Models/Weather/cloudbox_core5.xml";}
2010-08-03 06:18:14 +00:00
}
else if (subtype == "bottom") {
if (rn > 0.66) {path = "Models/Weather/cloudbox_bottom1.xml";}
else if (rn > 0.33) {path = "Models/Weather/cloudbox_bottom2.xml";}
else if (rn > 0.0) {path = "Models/Weather/cloudbox_bottom3.xml";}
2010-08-03 06:18:14 +00:00
}
2010-06-15 18:54:44 +00:00
}
2010-04-12 20:51:46 +00:00
else {print("Cloud type ", type, " subtype ",subtype, " not available!");}
return path;
}
2010-06-04 21:05:32 +00:00
###########################################################
# place a single cloud into a vector to be processed
2010-08-03 06:18:14 +00:00
# separately
2010-06-04 21:05:32 +00:00
###########################################################
2010-08-03 06:18:14 +00:00
var create_cloud_vec = func(path, lat, long, alt, heading) {
2010-06-04 21:05:32 +00:00
append(clouds_path,path);
append(clouds_lat,lat);
append(clouds_lon,long);
append(clouds_alt,alt);
append(clouds_orientation,heading);
# globals (needed for Cumulus clouds) should be set if needed by the main cloud generating call
if (dynamics_flag ==1)
{
append(clouds_mean_alt, cloud_mean_altitude);
append(clouds_flt, cloud_fractional_lifetime);
append(clouds_evolution_timestamp,cloud_evolution_timestamp);
}
2010-06-04 21:05:32 +00:00
}
2010-04-12 20:51:46 +00:00
###########################################################
# clear all clouds and effects
###########################################################
var clear_all = func {
# clear the clouds and models
2010-06-04 21:05:32 +00:00
var cloudNode = props.globals.getNode(lw~"clouds", 1);
cloudNode.removeChildren("tile");
2010-04-12 20:51:46 +00:00
var modelNode = props.globals.getNode("models", 1).getChildren("model");
foreach (var m; modelNode)
{
2010-08-03 06:18:14 +00:00
var l = m.getNode("tile-index",1).getValue();
if (l != nil)
2010-04-12 20:51:46 +00:00
{
m.remove();
}
}
# clear effect volumes
2010-06-15 18:54:44 +00:00
#props.globals.getNode("local-weather/effect-volumes", 1).removeChildren("effect-volume");
2010-06-15 18:54:44 +00:00
2010-08-03 06:18:14 +00:00
2010-06-15 18:54:44 +00:00
# reset pressure continuity
weather_tiles.last_pressure = 0.0;
# stop all loops
2010-04-12 20:51:46 +00:00
setprop(lw~"effect-loop-flag",0);
setprop(lw~"interpolation-loop-flag",0);
2010-06-04 21:05:32 +00:00
setprop(lw~"tile-loop-flag",0);
setprop(lw~"lift-loop-flag",0);
setprop(lw~"wave-loop-flag",0);
2010-08-03 06:18:14 +00:00
setprop(lw~"dynamics-loop-flag",0);
setprop(lw~"timing-loop-flag",0);
2010-09-10 06:52:21 +00:00
setprop(lw~"buffer-loop-flag",0);
setprop(lw~"housekeeping-loop-flag",0);
setprop(lw~"convective-loop-flag",0);
weather_dynamics.convective_loop_kill_flag = 1; # long-running loop needs a different scheme to end
2010-04-12 20:51:46 +00:00
2011-03-19 13:03:28 +00:00
# also remove rain snow and saturation effects
2010-04-12 20:51:46 +00:00
2010-08-03 06:18:14 +00:00
compat_layer.setRain(0.0);
compat_layer.setSnow(0.0);
2011-03-19 13:03:28 +00:00
compat_layer.setLight(1.0);
2010-04-12 20:51:46 +00:00
2010-06-04 21:05:32 +00:00
# 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);
2010-08-03 06:18:14 +00:00
setprop(lw~"effect-volumes/number",0);
2010-06-04 21:05:32 +00:00
setprop(lw~"tiles/tile-counter",0);
2010-09-10 06:52:21 +00:00
2010-08-03 06:18:14 +00:00
# remove any quadtrees and arrays
settimer ( func { setsize(weather_dynamics.cloudQuadtrees,0);},0.1); # to avoid error generation in this frame
setsize(effectVolumeArray,0);
n_effectVolumeArray = 0;
2010-06-04 21:05:32 +00:00
2011-03-19 13:03:28 +00:00
# if we have used METAR, we may no longer want to do so
metar_flag = 0;
2010-09-10 06:52:21 +00:00
settimer ( func {
setsize(weather_tile_management.modelArrays,0);
setsize(weather_dynamics.tile_wind_direction,0);
setsize(weather_dynamics.tile_wind_speed,0);
setsize(weather_tile_management.cloudBufferArray,0);
setsize(weather_tile_management.cloudSceneryArray,0);
setsize(alt_20_array,0);
setsize(alt_50_array,0);
setsize(weather_dynamics.tile_convective_altitude,0);
setsize(weather_dynamics.tile_convective_strength,0);
setsize(weatherStationArray,0);
2011-03-19 13:03:28 +00:00
setsize(windIpointArray,0);
2010-09-10 06:52:21 +00:00
setprop(lw~"clouds/buffer-count",0);
setprop(lw~"clouds/cloud-scenery-count",0);
weather_tile_management.n_cloudSceneryArray = 0;
2011-03-19 13:03:28 +00:00
#props.globals.getNode("local-weather/interpolation", 1).removeChildren("wind");
setprop(lwi~"ipoint-number",0);
2010-09-10 06:52:21 +00:00
},1.1);
2010-06-15 18:54:44 +00:00
setprop(lw~"tmp/presampling-status", "idle");
2011-03-19 13:03:28 +00:00
# indicate that we are no longer running
local_weather_running_flag = 0;
2010-04-12 20:51:46 +00:00
}
2010-06-04 21:05:32 +00:00
2010-04-12 20:51:46 +00:00
###########################################################
2010-06-15 18:54:44 +00:00
# detailed Cumulus clouds created from multiple cloudlets
2010-06-04 21:05:32 +00:00
###########################################################
var create_detailed_cumulus_cloud = func (lat, lon, alt, size) {
2010-09-10 06:52:21 +00:00
var edge_bias = convective_texture_mix;
2010-08-03 06:18:14 +00:00
var size_bias = 0.0;
2010-06-15 18:54:44 +00:00
if (size > 2.0)
{create_cumulonimbus_cloud(lat, lon, alt, size); return;}
else if (size>1.5)
2010-06-04 21:05:32 +00:00
{
2010-08-03 06:18:14 +00:00
var type = "Congestus";
var btype = "Congestus bottom";
var height = 400;
var n = 8;
var n_b = 4;
var x = 1000.0;
2010-06-04 21:05:32 +00:00
var y = 300.0;
var edge = 0.3;
}
2010-08-03 06:18:14 +00:00
else if (size>1.1)
2010-06-04 21:05:32 +00:00
{
2010-08-03 06:18:14 +00:00
var type = "Cumulus (cloudlet)";
var btype = "Cumulus bottom";
var height = 200;
2010-06-04 21:05:32 +00:00
var n = 8;
2010-08-03 06:18:14 +00:00
var n_b = 1;
2010-06-04 21:05:32 +00:00
var x = 400.0;
var y = 200.0;
var edge = 0.3;
}
2010-08-03 06:18:14 +00:00
else if (size>0.8)
{
var type = "Cumulus (cloudlet)";
var height = 150;
var n = 6;
var x = 300.0;
var y = 200.0;
var edge = 0.3;
}
else if (size>0.4)
2010-06-04 21:05:32 +00:00
{
2010-08-03 06:18:14 +00:00
var type = "Cumulus (cloudlet)";
var btype = "Cumulus bottom";
2010-06-04 21:05:32 +00:00
var height = 100;
var n = 4;
var x = 200.0;
var y = 200.0;
var edge = 1.0;
}
else
{
var type = "Cumulus (whisp)";
var btype = "Cumulus bottom";
var height = 100;
var n = 1;
var x = 100.0;
var y = 100.0;
var edge = 1.0;
}
2010-06-04 21:05:32 +00:00
var alpha = rand() * 180.0;
2010-08-03 06:18:14 +00:00
edge = edge + edge_bias;
2010-06-04 21:05:32 +00:00
2010-08-03 06:18:14 +00:00
create_streak(type,lat,lon, alt+ 0.5* (height +cloud_vertical_size_map["Cumulus"] * ft_to_m), height,n,0.0,edge,x,1,0.0,0.0,y,alpha,1.0);
2010-06-04 21:05:32 +00:00
2010-08-03 06:18:14 +00:00
# for large clouds, add a bottom
if ((size > 1.1) and (edge < 0.4))
{
create_streak(btype,lat,lon, alt, 100.0,n_b,0.0,edge,0.3*x,1,0.0,0.0,0.3*y,alpha,1.0);
}
2010-06-04 21:05:32 +00:00
}
2010-06-15 18:54:44 +00:00
###########################################################
# 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;
2010-08-03 06:18:14 +00:00
create_streak("Cumulonimbus",lat,lon, alt+ 0.5* height, height,8,0.0,0.0,1600.0,1,0.0,0.0,800.0,alpha,1.0);
2010-06-15 18:54:44 +00:00
}
2010-06-04 21:05:32 +00:00
###########################################################
# wrappers for convective cloud system to distribute
# call across several frames if needed
2010-04-12 20:51:46 +00:00
###########################################################
var create_cumosys = func (blat, blon, balt, nc, size) {
2010-06-04 21:05:32 +00:00
# realistic Cumulus has somewhat larger models, so compensate to get the same coverage
if (detailed_clouds_flag == 1)
2010-06-15 18:54:44 +00:00
{nc = int(0.7 * nc);}
2010-06-04 21:05:32 +00:00
if (thread_flag == 1)
2010-06-04 21:05:32 +00:00
{setprop(lw~"tmp/convective-status", "computing");
cumulus_loop(blat, blon, balt, nc, size);}
else
{create_cumulus(blat, blon, balt, nc, size);
if (debug_output_flag == 1)
{print("Convective system done!");}
}
2010-06-04 21:05:32 +00:00
}
var cumulus_loop = func (blat, blon, balt, nc, size) {
var n = 25;
2010-08-03 06:18:14 +00:00
if (nc < 0)
{
if (debug_output_flag == 1)
{print("Convective system done!");}
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/convective-status", "idle");
assemble_effect_array();
return;
}
2010-06-04 21:05:32 +00:00
#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) {
2010-04-12 20:51:46 +00:00
var path = "Models/Weather/blank.ac";
var i = 0;
var p = 0.0;
var rn = 0.0;
var place_lift_flag = 0;
2010-06-04 21:05:32 +00:00
var strength = 0.0;
var detail_flag = detailed_clouds_flag;
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
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
2010-04-12 20:51:46 +00:00
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
2010-06-15 18:54:44 +00:00
# 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));
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
# 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");
2010-04-12 20:51:46 +00:00
while (i < nc) {
p = 0.0;
place_lift_flag = 0;
2010-06-04 21:05:32 +00:00
strength = 0.0;
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
# pick a trial position inside the tile and rotate by tile orientation angle
2010-04-12 20:51:46 +00:00
var x = (2.0 * rand() - 1.0) * size;
var y = (2.0 * rand() - 1.0) * size;
2010-06-15 18:54:44 +00:00
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
2010-04-12 20:51:46 +00:00
var info = geodinfo(lat, lon);
if (info != nil) {
2010-06-15 18:54:44 +00:00
var elevation = info[0] * m_to_ft;
2010-04-12 20:51:46 +00:00
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]);}
}}
else {continue;}
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
# then decide if the thermal energy at the spot generates an updraft and a cloud
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
if (rand() < p) # we decide to place a cloud at this spot
2010-04-12 20:51:46 +00:00
{
2010-06-15 18:54:44 +00:00
strength = (1.5 * rand() + (2.0 * p)) * t_factor2; # the strength of thermal activity at the spot
if (strength > 1.0)
2010-04-12 20:51:46 +00:00
{
2010-06-15 18:54:44 +00:00
# we place a large cloud, and we generate lift
path = select_cloud_model("Cumulus","large"); place_lift_flag = 1;
2010-04-12 20:51:46 +00:00
}
else {path = select_cloud_model("Cumulus","small");}
2010-06-15 18:54:44 +00:00
# check if we have a terrain elevation analysis available and can use a
# detailed placement altitude correction
if (presampling_flag == 1)
2010-06-15 18:54:44 +00:00
{
var place_alt = get_convective_altitude(balt, elevation, getprop(lw~"tiles/tile-counter"));
2010-06-15 18:54:44 +00:00
}
else {var place_alt = balt;}
cloud_mean_altitude = place_alt;
cloud_fractional_lifetime = rand();
cloud_evolution_timestamp = weather_dynamics.time_lw;
2010-06-15 18:54:44 +00:00
if (generate_thermal_lift_flag != 3) # no clouds if we produce blue thermals
2010-06-04 21:05:32 +00:00
{
if (thread_flag == 1)
2010-06-04 21:05:32 +00:00
{
2010-08-03 06:18:14 +00:00
if (detail_flag == 0){create_cloud_vec(path,lat,lon, place_alt, 0.0);}
2010-06-15 18:54:44 +00:00
else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);}
2010-06-04 21:05:32 +00:00
}
else
{
2010-08-03 06:18:14 +00:00
if (detail_flag == 0){compat_layer.create_cloud(path, lat, lon, place_alt, 0.0);}
2010-06-15 18:54:44 +00:00
else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);}
2010-06-04 21:05:32 +00:00
}
}
2010-04-12 20:51:46 +00:00
# now see if we need to create a thermal - first check the flag
if (generate_thermal_lift_flag == 1) # thermal by constant
2010-04-12 20:51:46 +00:00
{
# now check if convection is strong
if (place_lift_flag == 1)
2010-06-04 21:05:32 +00:00
{
2010-06-15 18:54:44 +00:00
var lift = 3.0 + 10.0 * (strength -1.0);
2010-06-04 21:05:32 +00:00
var radius = 500 + 500 * rand();
2010-06-15 18:54:44 +00:00
#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,-1);
2010-04-12 20:51:46 +00:00
} # end if place_lift_flag
} # end if generate-thermal-lift-flag
else if ((generate_thermal_lift_flag == 2) or (generate_thermal_lift_flag == 3)) # thermal by function
2010-06-04 21:05:32 +00:00
{
if (place_lift_flag == 1)
{
2010-06-15 18:54:44 +00:00
var lift = (3.0 + 10.0 * (strength -1.0))/thermal_conditions;
var radius = (500 + 500 * rand())*thermal_conditions;
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.04, lift, -2,-1);
2010-06-04 21:05:32 +00:00
} # end if place_lift_flag
} # end if generate-thermal-lift-flag
2010-04-12 20:51:46 +00:00
} # end if rand < p
i = i + 1;
} # end while
}
#################################################################
# respawn convective clouds to compensate for decay
# the difference being that new clouds get zero fractional
# lifetime and are placed based on terrain with a different weight
##################################################################
var recreate_cumulus = func (blat, blon, balt, alpha, nc, size, tile_index) {
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 = detailed_clouds_flag;
alpha = alpha * math.pi/180.0; # the tile orientation
var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime
# current aircraft position
var alat = getprop("position/latitude-deg");
var alon = getprop("position/longitude-deg");
# get the local time of the day in seconds
var t = getprop("sim/time/utc/day-seconds");
t = t + getprop("sim/time/local-offset");
# 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));
# 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;
# check if the cloud would be spawned in visual range, if not don't bother
var d_sq = calc_d_sq(alat, alon, lat, lon);
if (math.sqrt(d_sq) > weather_tile_management.cloud_view_distance)
{i = i+1; continue;}
# 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]);}
}}
else {continue;}
# check if to place a cloud with weight sqrt(p), the lifetime gets another sqrt(p) factor
if (rand() > math.sqrt(p))
{i=i+1; continue;}
# then calculate the strength of the updraft
strength = (1.5 * rand() + (2.0 * p)) * t_factor2; # the strength of thermal activity at the spot
if (strength > 1.0)
{
path = select_cloud_model("Cumulus","large"); place_lift_flag = 1;
}
else {path = select_cloud_model("Cumulus","small");}
if (presampling_flag == 1)
{
var place_alt = get_convective_altitude(balt, elevation, tile_index);
}
else {var place_alt = balt;}
cloud_mean_altitude = place_alt;
cloud_fractional_lifetime = 0.0;
cloud_evolution_timestamp = weather_dynamics.time_lw;
compat_layer.cloud_mean_altitude = place_alt;
compat_layer.cloud_flt = cloud_fractional_lifetime;
compat_layer.cloud_evolution_timestamp = cloud_evolution_timestamp;
if (generate_thermal_lift_flag != 3) # no clouds if we produce blue thermals
{
if (thread_flag == 1)
{
thread_flag = 0; # create clouds immediately
if (detail_flag == 0){compat_layer.create_cloud(path,lat,lon, place_alt, 0.0);}
else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);}
thread_flag = 1; # and restore threading
}
else
{
if (detail_flag == 0){compat_layer.create_cloud(path, lat, lon, place_alt, 0.0);}
else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);}
}
}
if (generate_thermal_lift_flag == 1) # thermal by constant
{
if (place_lift_flag == 1)
{
var lift = 3.0 + 10.0 * (strength -1.0);
var radius = 500 + 500 * rand();
create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, -1, lift, 1,-1);
} # end if place_lift_flag
} # end if generate-thermal-lift-flag
else if ((generate_thermal_lift_flag == 2) or (generate_thermal_lift_flag == 3)) # thermal by function
{
if (place_lift_flag == 1)
{
var lift = (3.0 + 10.0 * (strength -1.0))/thermal_conditions;
var radius = (500 + 500 * rand())*thermal_conditions;
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.04, lift, -2,-1);
} # end if place_lift_flag
} # end if generate-thermal-lift-flag
i = i + 1;
} # end while
}
2010-04-12 20:51:46 +00:00
###########################################################
2010-06-15 18:54:44 +00:00
# place a Cumulus layer with excluded regions
# to avoid placing cumulus underneath a thunderstorm
2010-04-12 20:51:46 +00:00
###########################################################
2010-06-15 18:54:44 +00:00
var cumulus_exclusion_layer = func (blat, blon, balt, n, size_x, size_y, alpha, s_min, s_max, n_ex, exlat, exlon, exrad) {
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
var strength = 0;
var flag = 1;
var phi = alpha * math.pi/180.0;
var detail_flag = detailed_clouds_flag;
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
if (detail_flag == 1) {var i_max = int(0.25*n);} else {var i_max = int(1.0*n);}
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
for (var i =0; i< i_max; i=i+1)
2010-04-12 20:51:46 +00:00
{
2010-06-15 18:54:44 +00:00
var x = (2.0 * rand() - 1.0) * size_x;
var y = (2.0 * rand() - 1.0) * size_y;
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
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;
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
flag = 1;
for (var j=0; j<n_ex; j=j+1)
{
if (calc_d_sq(lat, lon, exlat[j], exlon[j]) < (exrad[j] * exrad[j])) {flag = 0;}
}
if (flag == 1)
{
strength = s_min + rand() * (s_max - s_min);
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
if (strength > 1.0) {var path = select_cloud_model("Cumulus","large"); }
else {var path = select_cloud_model("Cumulus","small");}
2010-04-12 20:51:46 +00:00
if (thread_flag == 1)
2010-06-15 18:54:44 +00:00
{
2010-08-03 06:18:14 +00:00
if (detail_flag == 0){create_cloud_vec(path,lat,lon, balt, 0.0);}
2010-06-15 18:54:44 +00:00
else {create_detailed_cumulus_cloud(lat, lon, balt, strength);}
}
else
{
2010-08-03 06:18:14 +00:00
if (detail_flag == 0){compat_layer.create_cloud(path, lat, lon, balt, 0.0);}
2010-06-15 18:54:44 +00:00
else {create_detailed_cumulus_cloud(lat, lon, balt, strength);}
}
} # end if flag
2010-04-12 20:51:46 +00:00
2010-06-15 18:54:44 +00:00
} # end for i
}
2010-04-12 20:51:46 +00:00
###########################################################
# 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 dir = (winddir + 180.0) * math.pi/180.0;
var step = dist/nsample;
calc_geo(blat);
while (i < nc) {
counter = counter + 1;
p = 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 elevation = compat_layer.get_elevation(lat, lon);
2010-04-12 20:51:46 +00:00
#print("elevation: ", elevation, "balt: ", balt);
2010-04-12 20:51:46 +00:00
if ((elevation < balt) and (elevation != -1.0))
2010-04-12 20:51:46 +00:00
{
for (var j = 0; j<nsample; j=j+1)
{
d = j * step;
x = d * math.sin(dir);
y = d * math.cos(dir);
var tlat = lat + y * m_to_lat;
var tlon = lon + x * m_to_lon;
#print("x: ", x, "y: ", y);
var elevation1 = compat_layer.get_elevation(tlat,tlon);
#print("elevation1: ", elevation1, "balt: ", balt);
if (elevation1 > balt)
2010-08-03 06:18:14 +00:00
{
p = 1.0 - j * (1.0/nsample);
#p = 1.0;
2010-08-03 06:18:14 +00:00
break;
2010-04-12 20:51:46 +00:00
}
}
}
if (counter > 500) {print("Cannot place clouds - exiting..."); i = nc;}
if (rand() < p)
{
path = select_cloud_model("Stratus (structured)","large");
2010-08-03 06:18:14 +00:00
compat_layer.create_cloud(path, lat, lon, balt, 0.0);
counter = 0;
i = i+1;
}
2010-04-12 20:51:46 +00:00
} # end while
}
###########################################################
# place a cloud streak
###########################################################
2010-08-03 06:18:14 +00:00
var create_streak = func (type, blat, blong, balt, alt_var, nx, xoffset, edgex, x_var, ny, yoffset, edgey, y_var, direction, tri) {
2010-04-12 20:51:46 +00:00
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<ny; i=i+1)
{
2010-08-03 06:18:14 +00:00
var y = ymin + i * yoffset;
2010-04-12 20:51:46 +00:00
for (var j=0; j<nx; j=j+1)
{
2010-08-03 06:18:14 +00:00
var y0 = y + y_var * 2.0 * (rand() -0.5);
var x = xmin + j * (xoffset + i * xinc) + x_var * 2.0 * (rand() -0.5);
var lat = blat + m_to_lat * (y0 * math.cos(dir) - x * math.sin(dir));
var long = blong + m_to_lon * (x * math.cos(dir) + y0 * math.sin(dir));
var alt = balt + alt_var * 2 * (rand() - 0.5);
2010-04-12 20:51:46 +00:00
flag = 0;
var rn = 6.0 * rand();
if (((j<jlow) or (j>(nx-jlow-1))) and ((i<ilow) or (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<jlow) or (j>(nx-jlow-1)) or (i<ilow) 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");}
}
2010-06-04 21:05:32 +00:00
if (flag==0){
if (thread_flag == 1)
2010-08-03 06:18:14 +00:00
{create_cloud_vec(path, lat, long, alt, 0.0);}
2010-06-04 21:05:32 +00:00
else
2010-08-03 06:18:14 +00:00
{compat_layer.create_cloud(path, lat, long, alt, 0.0);}
2010-06-04 21:05:32 +00:00
}
2010-04-12 20:51:46 +00:00
}
}
}
2011-03-19 13:03:28 +00:00
###########################################################
# place an undulatus pattern
###########################################################
var create_undulatus = func (type, blat, blong, balt, alt_var, nx, xoffset, edgex, x_var, ny, yoffset, edgey, y_var, und_strength, 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);
var und = 0.0;
var und_array = [];
for (var i=0; i<ny; i=i+1)
{
und = und + 2.0 * (rand() -0.5) * und_strength;
append(und_array,und);
}
for (var i=0; i<ny; i=i+1)
{
var y = ymin + i * yoffset;
for (var j=0; j<nx; j=j+1)
{
var y0 = y + y_var * 2.0 * (rand() -0.5);
var x = xmin + j * (xoffset + i * xinc) + x_var * 2.0 * (rand() -0.5) + und_array[i];
var lat = blat + m_to_lat * (y0 * math.cos(dir) - x * math.sin(dir));
var long = blong + m_to_lon * (x * math.cos(dir) + y0 * math.sin(dir));
var alt = balt + alt_var * 2 * (rand() - 0.5);
flag = 0;
var rn = 6.0 * rand();
if (((j<jlow) or (j>(nx-jlow-1))) and ((i<ilow) or (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<jlow) or (j>(nx-jlow-1)) or (i<ilow) 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 (flag==0){
if (thread_flag == 1)
{create_cloud_vec(path, lat, long, alt, 0.0);}
else
{compat_layer.create_cloud(path, lat, long, alt, 0.0);}
}
}
}
}
2010-04-12 20:51:46 +00:00
###########################################################
# 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<n)
{
var x = rx * (2.0 * rand() - 1.0);
var y = ry * (2.0 * rand() - 1.0);
var alt = balt + bthick * rand() + 0.8 * alt_offset;
var res = (x*x)/(rx*rx) + (y*y)/(ry*ry);
if (res < 1.0)
{
var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
if (res > ((1.0 - edge) * (1.0- edge)))
{
if (rand() > 0.4) {
path = select_cloud_model(type,"small");
2010-08-03 06:18:14 +00:00
compat_layer.create_cloud(path, lat, lon, alt, 0.0);
2010-04-12 20:51:46 +00:00
}
}
else {
path = select_cloud_model(type,"large");
if (thread_flag == 1)
2010-08-03 06:18:14 +00:00
{create_cloud_vec(path, lat, lon, alt, 0.0);}
2010-06-04 21:05:32 +00:00
else
2010-08-03 06:18:14 +00:00
{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
2010-04-12 20:51:46 +00:00
}
i = i + 1;
}
}
i = 0;
if (rainflag ==1){
while(i<m)
{
var alpha = rand() * 2.0 * math.pi;
x = 0.8 * (1.0 - edge) * (1.0-edge) * rx * math.cos(alpha);
y = 0.8 * (1.0 - edge) * (1.0-edge) * ry * math.sin(alpha);
lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
path = "Models/Weather/rain1.xml";
2010-06-04 21:05:32 +00:00
if (contains(cloud_vertical_size_map,type)) {var alt_shift = cloud_vertical_size_map[type];}
else {var alt_shift = 0.0;}
if (thread_flag == 1)
2010-08-03 06:18:14 +00:00
{create_cloud_vec(path, lat, lon,balt +0.5*bthick+ alt_shift, 0.0);}
2010-06-04 21:05:32 +00:00
else
2010-08-03 06:18:14 +00:00
{compat_layer.create_cloud(path, lat, lon, balt + 0.5 * bthick + alt_shift, 0.0);}
2010-04-12 20:51:46 +00:00
i = i + 1;
} # end while
} # end if (rainflag ==1)
}
2010-06-15 18:54:44 +00:00
2010-08-03 06:18:14 +00:00
###########################################################
# place a cloud layer with a gap in the middle
# (useful to reduce cloud count in large thunderstorms)
###########################################################
var create_hollow_layer = func (type, blat, blon, balt, bthick, rx, ry, phi, density, edge, gap_fraction) {
var i = 0;
var area = math.pi * rx * ry;
var n = int(area/80000000.0 * 100 * 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<n)
{
var x = rx * (2.0 * rand() - 1.0);
var y = ry * (2.0 * rand() - 1.0);
var alt = balt + bthick * rand() + 0.8 * alt_offset;
var res = (x*x)/(rx*rx) + (y*y)/(ry*ry);
if ((res < 1.0) and (res > (gap_fraction * gap_fraction)))
{
var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
if (res > ((1.0 - edge) * (1.0- edge)))
{
if (rand() > 0.4) {
path = select_cloud_model(type,"small");
compat_layer.create_cloud(path, lat, lon, alt, 0.0);
}
}
else {
path = select_cloud_model(type,"large");
if (thread_flag == 1)
2010-08-03 06:18:14 +00:00
{create_cloud_vec(path, lat, lon, alt, 0.0);}
else
{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
}
i = i + 1;
}
else # we are in the central gap region
{
i = i + 1;
}
}
i = 0;
}
2010-06-15 18:54:44 +00:00
###########################################################
# place a cloud box
###########################################################
2010-08-03 06:18:14 +00:00
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) {
2010-06-15 18:54:44 +00:00
var phi = 0;
2010-08-03 06:18:14 +00:00
# first get core coordinates
var core_dx = dx * f_core;
var core_dy = dy * f_core;
var core_dz = dz * h_core;
var core_x_offset = (1.0 * rand() - 0.5) * ((dx - core_dx) * r_core);
var core_y_offset = (1.0 * rand() - 0.5) * ((dy - core_dy) * r_core);
# get the bottom geometry
var bottom_dx = dx * f_bottom;
var bottom_dy = dy * f_bottom;
var bottom_dz = dz * h_bottom;
var bottom_offset = 400.0; # in practice, need a small shift
# fill the main body of the box
2010-06-15 18:54:44 +00:00
for (var i=0; i<n; i=i+1)
{
var x = 0.5 * dx * (2.0 * rand() - 1.0);
2010-08-03 06:18:14 +00:00
var y = 0.5 * dy * (2.0 * rand() - 1.0);
# veto in core region
if ((x > core_x_offset - 0.5 * core_dx) and (x < core_x_offset + 0.5 * core_dx))
{
if ((y > core_y_offset - 0.5 * core_dy) and (y < core_y_offset + 0.5 * core_dy))
{
i = i -1;
continue;
}
}
var alt = balt + bottom_dz + bottom_offset + dz * rand();
2010-06-15 18:54:44 +00:00
var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
2010-08-03 06:18:14 +00:00
var path = select_cloud_model(type,"standard");
2010-06-15 18:54:44 +00:00
if (thread_flag == 1)
2010-08-03 06:18:14 +00:00
{create_cloud_vec(path, lat, lon, alt, 0.0);}
2010-06-15 18:54:44 +00:00
else
2010-08-03 06:18:14 +00:00
{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
2010-06-15 18:54:44 +00:00
}
2010-08-03 06:18:14 +00:00
# fill the core region
2010-06-15 18:54:44 +00:00
2010-08-03 06:18:14 +00:00
for (var i=0; i<n_core; i=i+1)
{
var x = 0.5 * core_dx * (2.0 * rand() - 1.0);
var y = 0.5 * core_dy * (2.0 * rand() - 1.0);
var alt = balt + bottom_dz + bottom_offset + core_dz * rand();
2010-06-04 21:05:32 +00:00
2010-08-03 06:18:14 +00:00
var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
2010-06-04 21:05:32 +00:00
2010-08-03 06:18:14 +00:00
var path = select_cloud_model(type,"core");
2010-06-04 21:05:32 +00:00
if (thread_flag == 1)
2010-08-03 06:18:14 +00:00
{create_cloud_vec(path, lat, lon, alt, 0.0);}
else
{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
}
# fill the bottom region
2010-06-04 21:05:32 +00:00
2010-08-03 06:18:14 +00:00
for (var i=0; i<n_bottom; i=i+1)
2010-06-04 21:05:32 +00:00
{
2010-08-03 06:18:14 +00:00
var x = 0.5 * bottom_dx * (2.0 * rand() - 1.0);
var y = 0.5 * bottom_dy * (2.0 * rand() - 1.0);
var alt = balt + bottom_dz * rand();
var lat = blat + m_to_lat * (y * math.cos(phi) - x * math.sin(phi));
var lon = blon + m_to_lon * (x * math.cos(phi) + y * math.sin(phi));
var path = select_cloud_model(type,"bottom");
if (thread_flag == 1)
2010-08-03 06:18:14 +00:00
{create_cloud_vec(path, lat, lon, alt, 0.0);}
else
{compat_layer.create_cloud(path, lat, lon, alt, 0.0);}
2010-06-04 21:05:32 +00:00
}
2010-08-03 06:18:14 +00:00
}
2010-06-04 21:05:32 +00:00
2010-06-15 18:54:44 +00:00
###########################################################
# terrain presampling initialization
###########################################################
var terrain_presampling_start = func (blat, blon, nc, size, alpha) {
# initialize the result vector
setsize(terrain_n,20);
for(var j=0;j<20;j=j+1){terrain_n[j]=0;}
if (thread_flag == 1)
{
var status = getprop(lw~"tmp/presampling-status");
if (status != "idle") # we try a second later
{
settimer( func {terrain_presampling_start(blat, blon, nc, size, alpha);},1.00);
return;
}
else
{
setprop(lw~"tmp/presampling-status", "sampling");
terrain_presampling_loop (blat, blon, nc, size, alpha);
}
}
else
{
terrain_presampling(blat, blon, nc, size, alpha);
terrain_presampling_analysis();
setprop(lw~"tmp/presampling-status", "finished");
}
}
###########################################################
# terrain presampling loop
###########################################################
var terrain_presampling_loop = func (blat, blon, nc, size, alpha) {
var n = 25;
2010-09-10 06:52:21 +00:00
var n_out = 25;
# dynamically drop accuracy if framerate is low
var dt = getprop("/sim/time/delta-sec");
if (dt > 0.2) # we have below 20 fps
{n = 5;}
else if (dt > 0.1) # we have below 10 fps
{n = 10;}
else if (dt > 0.05) # we have below 5 fps
{n = 15;}
2010-06-15 18:54:44 +00:00
if (nc <= 0) # we're done and may analyze the result
{
terrain_presampling_analysis();
if (debug_output_flag == 1)
{print("Presampling done!");}
2010-06-15 18:54:44 +00:00
setprop(lw~"tmp/presampling-status", "finished");
return;
}
terrain_presampling(blat, blon, n, size, alpha);
2010-09-10 06:52:21 +00:00
settimer( func {terrain_presampling_loop(blat, blon, nc-n_out, size, alpha) },0);
2010-06-15 18:54:44 +00:00
}
###########################################################
# terrain presampling routine
###########################################################
var terrain_presampling = func (blat, blon, ntries, size, alpha) {
var phi = alpha * math.pi/180.0;
var elevation = 0.0;
2010-08-03 06:18:14 +00:00
var lat_vec = [];
var lon_vec = [];
2010-09-10 06:52:21 +00:00
var lat_lon_vec = [];
2010-06-15 18:54:44 +00:00
for (var i=0; i<ntries; i=i+1)
{
var x = (2.0 * rand() - 1.0) * size;
var y = (2.0 * rand() - 1.0) * size;
2010-09-10 06:52:21 +00:00
append(lat_vec, blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat);
append(lon_vec, blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon);
2010-08-03 06:18:14 +00:00
}
var elevation_vec = compat_layer.get_elevation_array(lat_vec, lon_vec);
2010-08-03 06:18:14 +00:00
for (i=0; i<ntries;i=i+1)
{
2010-06-15 18:54:44 +00:00
for(j=0;j<20;j=j+1)
{
2010-08-03 06:18:14 +00:00
if ((elevation_vec[i] != -1.0) and (elevation_vec[i] < 500.0 * (j+1)))
2010-06-15 18:54:44 +00:00
{terrain_n[j] = terrain_n[j]+1; break;}
}
2010-08-03 06:18:14 +00:00
2010-06-15 18:54:44 +00:00
}
2010-08-03 06:18:14 +00:00
2010-06-15 18:54:44 +00:00
}
###########################################################
# terrain presampling analysis
###########################################################
var terrain_presampling_analysis = func {
var sum = 0;
var alt_mean = 0;
var alt_med = 0;
var alt_20 = 0;
var alt_min = 0;
var alt_low_min = 0;
var alt_offset = 0;
# for (var i=0;i<20;i=i+1){print(500.0*i," ",terrain_n[i]);}
for (var i=0; i<20;i=i+1)
{sum = sum + terrain_n[i];}
var n_tot = sum;
sum = 0;
for (var i=0; i<20;i=i+1)
{
sum = sum + terrain_n[i];
if (sum > 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;}
}
if (debug_output_flag == 1)
{print("Terrain presampling analysis results:");
print("total: ",n_tot," mean: ",alt_mean," median: ",alt_med," min: ",alt_min, " alt_20: ", alt_20);}
2010-06-15 18:54:44 +00:00
#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));
2010-08-03 06:18:14 +00:00
append(alt_50_array, alt_med);
2010-09-10 06:52:21 +00:00
append(alt_20_array, alt_20);
2010-06-15 18:54:44 +00:00
}
###########################################################
# wave conditions search
###########################################################
var wave_detection_loop = func (blat, blon, nx, alpha) {
var phi = alpha * math.pi/180.0;
var elevation = 0.0;
var ny = 20;
for (var i=0; i<ny; i=i+1)
{
var x = 5000.0;
var y = -20000.0 + i * 2000.0;
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;
elevation = compat_layer.get_elevation(lat, lon);
print(elevation);
}
}
2010-06-15 18:54:44 +00:00
###########################################################
# detailed altitude determination for convective calls
# clouds follow the terrain to some degree, but not excessively so
###########################################################
var get_convective_altitude = func (balt, elevation, tile_index) {
2010-06-15 18:54:44 +00:00
var alt_offset = alt_20_array[tile_index - 1];
var alt_median = alt_50_array[tile_index - 1];
2010-06-15 18:54:44 +00:00
# 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 = 1.0;} # this shouldn't happen, but just in case...
if (alt_diff > alt_variation) {alt_diff = alt_variation;} # maximal shift is given by alt_variation
2010-06-15 18:54:44 +00:00
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");
2010-08-03 06:18:14 +00:00
var dir_index = getprop(lw~"tiles/tmp/dir-index");
weather_tile_management.generate_tile(code, lat, lon, dir_index);
2010-06-15 18:54:44 +00:00
}
# set status to idle again
setprop(lw~"tmp/presampling-status", "idle");
}
2010-08-03 06:18:14 +00:00
###########################################################
# set wind model flag
###########################################################
2010-06-15 18:54:44 +00:00
2010-08-03 06:18:14 +00:00
var set_wind_model_flag = func {
var wind_model = getprop(lw~"config/wind-model");
if (wind_model == "constant") {wind_model_flag = 1;}
else if (wind_model == "constant in tile") {wind_model_flag =2;}
else if (wind_model == "aloft interpolated") {wind_model_flag =3; }
else if (wind_model == "airmass interpolated") {wind_model_flag =4;}
else if (wind_model == "aloft waypoints") {wind_model_flag =5;}
else {print("Wind model not implemented!"); wind_model_flag =1;}
}
2010-06-15 18:54:44 +00:00
2010-06-04 21:05:32 +00:00
2010-09-10 06:52:21 +00:00
###########################################################
# set texture mix for convective clouds
###########################################################
var set_texture_mix = func {
var thermal_properties = getprop(lw~"config/thermal-properties");
convective_texture_mix = -(thermal_properties - 1.0) * 0.4;
if (convective_texture_mix < -0.2) {convective_texture_mix = -0.2;}
if (convective_texture_mix > 0.2) {convective_texture_mix = 0.2;}
}
2010-04-12 20:51:46 +00:00
###########################################################
# 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, sat) {
2010-04-12 20:51:46 +00:00
2010-06-04 21:05:32 +00:00
var ev = effectVolume.new (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat);
ev.index = getprop(lw~"tiles/tile-counter");
ev.active_flag = 0;
2010-06-04 21:05:32 +00:00
2010-04-12 20:51:46 +00:00
if (vis < 0.0) {ev.vis_flag = 0;} else {ev.vis_flag = 1;}
if (rain < 0.0) {ev.rain_flag = 0;} else {ev.rain_flag = 1;}
if (snow < 0.0) {ev.snow_flag = 0;} else {ev.snow_flag = 1;}
if (turb < 0.0) {ev.turb_flag = 0;} else {ev.turb_flag = 1;}
if (lift_flag == 0.0) {ev.lift_flag = 0;} else {ev.lift_flag = 1;}
if (sat < 0.0) {ev.sat_flag = 0;} else {ev.sat_flag = 1;}
if (sat > 1.0) {sat = 1.0;}
2010-04-12 20:51:46 +00:00
2010-06-04 21:05:32 +00:00
if (lift_flag == -2) # we create a thermal by function
{
ev.lift_flag = 2;
ev.radius = 0.8 * r1;
ev.height = alt_high * 0.87;
ev.cn = 0.7 + rand() * 0.2;
ev.sh = 0.7 + rand() * 0.2;
ev.max_lift = lift;
ev.f_lift_radius = 0.7 + rand() * 0.2;
if (dynamics_flag == 1) # globals set by the convective system
{
ev.flt = cloud_fractional_lifetime;
ev.evolution_timestamp = cloud_evolution_timestamp;
}
}
if (lift_flag == -3) # we create a wave lift
{
ev.lift_flag = 3;
ev.height = 10000.0; # scale height in ft
ev.max_lift = lift;
ev.index = 0; # static objects are assigned tile id zero
2010-06-04 21:05:32 +00:00
}
2010-08-03 06:18:14 +00:00
# set a timestamp if needed
2010-06-04 21:05:32 +00:00
if (dynamics_flag == 1)
2010-08-03 06:18:14 +00:00
{
ev.timestamp = weather_dynamics.time_lw;
2010-08-03 06:18:14 +00:00
}
2010-04-12 20:51:46 +00:00
# and add to the counter
setprop(lw~"effect-volumes/number",getprop(lw~"effect-volumes/number")+1);
append(effectVolumeArray,ev);
2010-04-12 20:51:46 +00:00
}
2010-06-04 21:05:32 +00:00
2010-08-03 06:18:14 +00:00
2010-06-04 21:05:32 +00:00
###########################################################
2010-08-03 06:18:14 +00:00
# set a weather station for interpolation
2010-06-04 21:05:32 +00:00
###########################################################
2011-03-19 13:03:28 +00:00
var set_weather_station = func (lat, lon, alt, vis, T, D, p) {
2010-06-04 21:05:32 +00:00
2011-03-19 13:03:28 +00:00
var s = weatherStation.new (lat, lon, alt, vis, T, D, p);
s.index = getprop(lw~"tiles/tile-counter");
2011-03-19 13:03:28 +00:00
s.weight = 0.02;
2010-06-04 21:05:32 +00:00
2010-08-03 06:18:14 +00:00
# set a timestamp if needed
2010-06-04 21:05:32 +00:00
if (dynamics_flag == 1)
2010-06-04 21:05:32 +00:00
{
s.timestamp = weather_dynamics.time_lw;
2010-06-04 21:05:32 +00:00
}
append(weatherStationArray,s);
2010-06-04 21:05:32 +00:00
}
2010-08-03 06:18:14 +00:00
2010-04-12 20:51:46 +00:00
###########################################################
2010-08-03 06:18:14 +00:00
# set a wind interpolation point
2010-04-12 20:51:46 +00:00
###########################################################
2010-08-03 06:18:14 +00:00
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) {
2010-04-12 20:51:46 +00:00
2011-03-19 13:03:28 +00:00
var w = windIpoint.new(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8);
2010-04-12 20:51:46 +00:00
2011-03-19 13:03:28 +00:00
append(windIpointArray, w);
2010-04-12 20:51:46 +00:00
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
}
###########################################################
# set a wind interpolation point from ground METAR data
###########################################################
var set_wind_ipoint_metar = func (lat, lon, d0, v0) {
# insert a plausible pattern of aloft winds based on ground info
# direction of Coriolis deflection depends on hemisphere
if (lat >0.0) {var dsign = -1.0;} else {var dsign = 1.0;}
var v1 = v0 * (1.0 + rand() * 0.2);
var d1 = d0 + dsign * 3.0 * rand();
var v2 = v0 * (1.2 + rand() * 0.2);
var d2 = d0 + dsign * (3.0 * rand() + 2.0);
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
var v3 = v0 * (1.3 + rand() * 0.4) + 5.0;
var d3 = d0 + dsign * (3.0 * rand() + dsign * 4.0);
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
var v4 = v0 * (1.7 + rand() * 0.5) + 10.0;
var d4 = d0 + dsign * (4.0 * rand() + dsign * 8.0);
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
var v5 = v0 * (1.7 + rand() * 0.5) + 20.0;
var d5 = d0 + dsign * (4.0 * rand() + dsign * 10.0);
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
var v6 = v0 * (1.7 + rand() * 0.5) + 40.0;
var d6 = d0 + dsign * (4.0 * rand() + dsign * 12.0);
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
var v7 = v0 * (2.0 + rand() * 0.7) + 50.0;
var d7 = d0 + dsign * (4.0 * rand() + dsign * 13.0);
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
var v8 = v0 * (2.0 + rand() * 0.7) + 55.0;;
var d8 = d0 + dsign * (5.0 * rand() + dsign * 14.0);
var w = windIpoint.new(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8);
append(windIpointArray, w);
2010-08-03 06:18:14 +00:00
2010-04-12 20:51:46 +00:00
}
2010-08-03 06:18:14 +00:00
###########################################################
# helper to show additional dialogs
###########################################################
var showDialog = func (name) {
fgcommand("dialog-show", props.Node.new({"dialog-name":name}));
}
###########################################################
# helper to transfer configuration flags in menu to Nasal
###########################################################
var readFlags = func {
# thermal lift must be 1 for constant thermals (obsolete), 2 for thermals by model (menu default)
# and 3 for blue thermals (set internally inside the tile only)
if (getprop(lw~"config/generate-thermal-lift-flag") ==1) {generate_thermal_lift_flag = 2;}
else {generate_thermal_lift_flag = 0};
thread_flag = getprop(lw~"config/thread-flag");
dynamics_flag = getprop(lw~"config/dynamics-flag");
presampling_flag = getprop(lw~"tmp/presampling-flag");
detailed_clouds_flag = getprop(lw~"config/detailed-clouds-flag");
dynamical_convection_flag = getprop(lw~"config/dynamical-convection-flag");
debug_output_flag = getprop(lw~"config/debug-output-flag");
2010-08-03 06:18:14 +00:00
}
2010-04-12 20:51:46 +00:00
###########################################################
# wrappers to call functions from the local weather menu bar
###########################################################
var streak_wrapper = func {
thread_flag = 0;
dynamics_flag = 0;
presampling_flag = 0;
2010-08-03 06:18:14 +00:00
var array = [];
append(weather_tile_management.modelArrays,array);
setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
2010-06-15 18:54:44 +00:00
2010-04-12 20:51:46 +00:00
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");
2010-08-03 06:18:14 +00:00
create_streak(type,lat,lon,alt,rnd_alt,nx,xoffset,xedge,rnd_pos_x,ny,yoffset,yedge,rnd_pos_y,dir,tri);
2010-04-12 20:51:46 +00:00
}
var convection_wrapper = func {
thread_flag = 0;
dynamics_flag = 0;
presampling_flag = 0;
2010-08-03 06:18:14 +00:00
var array = [];
append(weather_tile_management.modelArrays,array);
setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
2010-06-15 18:54:44 +00:00
2010-04-12 20:51:46 +00:00
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 {
thread_flag = 0;
dynamics_flag = 0;
presampling_flag = 0;
2010-08-03 06:18:14 +00:00
var array = [];
append(weather_tile_management.modelArrays,array);
setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
2010-06-15 18:54:44 +00:00
2010-04-12 20:51:46 +00:00
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 {
thread_flag = 0;
dynamics_flag = 0;
presampling_flag = 0;
2010-08-03 06:18:14 +00:00
var array = [];
append(weather_tile_management.modelArrays,array);
setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
2010-04-12 20:51:46 +00:00
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);
2010-08-03 06:18:14 +00:00
compat_layer.create_cloud(path, lat, lon, alt, heading);
2010-04-12 20:51:46 +00:00
}
var layer_wrapper = func {
thread_flag = 0;
dynamics_flag = 0;
presampling_flag = 0;
2010-06-15 18:54:44 +00:00
2010-08-03 06:18:14 +00:00
var array = [];
append(weather_tile_management.modelArrays,array);
setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
2010-04-12 20:51:46 +00:00
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);
}
2010-06-15 18:54:44 +00:00
var box_wrapper = func {
thread_flag = 0;
dynamics_flag = 0;
presampling_flag = 0;
2010-08-03 06:18:14 +00:00
setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
2010-06-15 18:54:44 +00:00
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");
2010-08-03 06:18:14 +00:00
var f_core = getprop(lw~"tmp/box-core-fraction");
var r_core = getprop(lw~"tmp/box-core-offset");
var h_core = getprop(lw~"tmp/box-core-height");
var n_core = getprop(lw~"tmp/box-core-n");
var f_bottom = getprop(lw~"tmp/box-bottom-fraction");
var h_bottom = getprop(lw~"tmp/box-bottom-thickness");
var n_bottom = getprop(lw~"tmp/box-bottom-n");
2010-06-15 18:54:44 +00:00
var type = "Box_test";
2010-08-03 06:18:14 +00:00
#create_cloudbox(type,subtype,lat, lon, alt, x,y,z,n);
create_cloudbox(type, lat, lon, alt, x,y,z,n, f_core, r_core, h_core, n_core, f_bottom, h_bottom, n_bottom);
2010-06-15 18:54:44 +00:00
}
2010-04-12 20:51:46 +00:00
2010-08-03 06:18:14 +00:00
var set_aloft_wrapper = func {
var lat = getprop(lw~"tmp/ipoint-latitude-deg");
var lon = getprop(lw~"tmp/ipoint-longitude-deg");
var d0 = getprop(lw~"tmp/FL0-wind-from-heading-deg");
var v0 = getprop(lw~"tmp/FL0-windspeed-kt");
var d1 = getprop(lw~"tmp/FL50-wind-from-heading-deg");
var v1 = getprop(lw~"tmp/FL50-windspeed-kt");
var d2 = getprop(lw~"tmp/FL100-wind-from-heading-deg");
var v2 = getprop(lw~"tmp/FL100-windspeed-kt");
var d3 = getprop(lw~"tmp/FL180-wind-from-heading-deg");
var v3 = getprop(lw~"tmp/FL180-windspeed-kt");
var d4 = getprop(lw~"tmp/FL240-wind-from-heading-deg");
var v4 = getprop(lw~"tmp/FL240-windspeed-kt");
var d5 = getprop(lw~"tmp/FL300-wind-from-heading-deg");
var v5 = getprop(lw~"tmp/FL300-windspeed-kt");
var d6 = getprop(lw~"tmp/FL340-wind-from-heading-deg");
var v6 = getprop(lw~"tmp/FL340-windspeed-kt");
var d7 = getprop(lw~"tmp/FL390-wind-from-heading-deg");
var v7 = getprop(lw~"tmp/FL390-windspeed-kt");
var d8 = getprop(lw~"tmp/FL450-wind-from-heading-deg");
var v8 = getprop(lw~"tmp/FL450-windspeed-kt");
set_wind_ipoint(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8);
if (wind_model_flag == 5)
{setprop(lwi~"ipoint-number", getprop(lwi~"ipoint-number") + 1);}
}
2010-04-12 20:51:46 +00:00
####################################
# tile setup call wrapper
####################################
var set_tile = func {
2011-03-19 13:03:28 +00:00
# check if another instance of local weather is running already
if (local_weather_running_flag == 1)
{
setprop("/sim/messages/pilot", "Local weather: Local weather is already running, use Clear/End before restarting. Aborting...");
return;
}
2010-04-12 20:51:46 +00:00
var type = getprop("/local-weather/tmp/tile-type");
2010-06-04 21:05:32 +00:00
# set tile center coordinates to current position
2010-08-03 06:18:14 +00:00
var lat = getprop("position/latitude-deg");
var lon = getprop("position/longitude-deg");
setprop(lw~"tiles/tmp/latitude-deg",lat);
setprop(lw~"tiles/tmp/longitude-deg",lon);
2010-09-10 06:52:21 +00:00
setprop(lw~"tiles/tmp/dir-index",4);
2010-06-04 21:05:32 +00:00
readFlags();
# check consistency of flags
if (dynamical_convection_flag == 1)
{
if (dynamics_flag == 0)
{
print("Dynamical convection needs weather dynamics to run! Aborting...");
setprop("/sim/messages/pilot", "Local weather: dynamical convection needs weather dynamics to run! Aborting...");
return;
}
if (presampling_flag == 0)
{
print("Dynamical convection needs terrain presampling to run! Aborting...");
setprop("/sim/messages/pilot", "Local weather: dynamical convection needs terrain presampling to run! Aborting...");
return;
}
}
2011-01-05 12:36:10 +00:00
# if we can do so, we switch global weather and METAR parsing in environment off at this point
if (compat_layer.features.can_disable_environment ==1)
{
props.globals.getNode("/environment/config/enabled").setBoolValue(0);
2011-01-05 12:36:10 +00:00
props.globals.getNode("/environment/params/metar-updates-environment").setBoolValue(0);
}
2011-03-19 13:03:28 +00:00
# switch off normal 3d clouds
compat_layer.setDefaultCloudsOff();
2010-06-15 18:54:44 +00:00
# now see if we need to presample the terrain
if ((presampling_flag == 1) and (getprop(lw~"tmp/presampling-status") == "idle"))
2010-06-15 18:54:44 +00:00
{
2010-08-03 06:18:14 +00:00
terrain_presampling_start(lat, lon, 1000, 40000, getprop(lw~"tmp/tile-orientation-deg"));
2010-06-15 18:54:44 +00:00
return;
}
2010-08-03 06:18:14 +00:00
2011-01-05 12:36:10 +00:00
# see if we use METAR for weather setup
if ((getprop("/environment/metar/valid") == 1) and (getprop(lw~"tmp/tile-management") == "METAR"))
{
type = "METAR";
metar_flag = 1;
setprop(lw~"METAR/station-id","METAR");
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
2011-01-05 12:36:10 +00:00
}
else if ((getprop("/environment/metar/valid") == 0) and (getprop(lw~"tmp/tile-management") == "METAR"))
{
print("No METAR available, aborting...");
setprop("/sim/messages/pilot", "Local weather: No METAR available! Aborting...");
return;
}
2010-08-03 06:18:14 +00:00
# see if we need to create an aloft wind interpolation structure
if ((wind_model_flag == 3) or ((wind_model_flag ==5) and (getprop(lwi~"ipoint-number") == 0)))
2011-03-19 13:03:28 +00:00
{
if (metar_flag != 1)
{set_aloft_wrapper();}
}
2010-08-03 06:18:14 +00:00
# prepare the first tile wind field
2011-01-05 12:36:10 +00:00
if (metar_flag == 1) # the winds from current METAR are used
2010-08-03 06:18:14 +00:00
{
2011-03-19 13:03:28 +00:00
# METAR reports ground winds, we want to set aloft, so we need to compute the local boundary layer
# need to set the tile index for this
setprop(lw~"tiles/tile[4]/tile-index",1);
var boundary_correction = 1.0/get_slowdown_fraction();
var metar_base_wind_deg = getprop("environment/metar/base-wind-dir-deg");
var metar_base_wind_speed = boundary_correction * getprop("environment/metar/base-wind-speed-kt");
2011-01-05 12:36:10 +00:00
if ((wind_model_flag == 1) or (wind_model_flag == 2))
{
2011-03-19 13:03:28 +00:00
append(weather_dynamics.tile_wind_direction, metar_base_wind_deg);
append(weather_dynamics.tile_wind_speed, metar_base_wind_speed);
setprop(lw~"tmp/tile-orientation-deg",metar_base_wind_deg);
}
else if (wind_model_flag == 5)
{
var station_lat = getprop("/environment/metar/station-latitude-deg");
var station_lon = getprop("/environment/metar/station-longitude-deg");
set_wind_ipoint_metar(station_lat, station_lon, metar_base_wind_deg, metar_base_wind_speed);
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
var res = wind_interpolation(lat,lon,0.0);
2010-08-03 06:18:14 +00:00
2011-03-19 13:03:28 +00:00
append(weather_dynamics.tile_wind_direction,res[0]);
append(weather_dynamics.tile_wind_speed,res[1]);
setprop(lw~"tmp/tile-orientation-deg", weather_dynamics.tile_wind_direction[0]);
2011-01-05 12:36:10 +00:00
}
else
{
print("Wind model currently not supported with live data!");
setprop("/sim/messages/pilot", "Local weather: Wind model currently not supported with live data! Aborting...");
return;
}
2010-08-03 06:18:14 +00:00
}
2011-01-05 12:36:10 +00:00
else
2010-08-03 06:18:14 +00:00
{
2011-01-05 12:36:10 +00:00
if (wind_model_flag == 5) # it needs to be interpolated
{
var res = wind_interpolation(lat,lon,0.0);
2010-08-03 06:18:14 +00:00
2011-01-05 12:36:10 +00:00
append(weather_dynamics.tile_wind_direction,res[0]);
append(weather_dynamics.tile_wind_speed,res[1]);
}
else if (wind_model_flag == 3) # it comes from a different menu
{
append(weather_dynamics.tile_wind_direction, getprop(lw~"tmp/FL0-wind-from-heading-deg"));
append(weather_dynamics.tile_wind_speed, getprop(lw~"tmp/FL0-windspeed-kt"));
}
else # it comes from the standard menu
{
append(weather_dynamics.tile_wind_direction, getprop(lw~"tmp/tile-orientation-deg"));
append(weather_dynamics.tile_wind_speed, getprop(lw~"tmp/windspeed-kt"));
}
2010-08-03 06:18:14 +00:00
2011-01-05 12:36:10 +00:00
# when the aloft wind menu is used, the lowest winds should be taken from there
# so we need to overwrite the setting from the tile generating menu in this case
# otherwise the wrong orientation is built
2010-08-03 06:18:14 +00:00
2011-01-05 12:36:10 +00:00
if (wind_model_flag ==3)
{
setprop(lw~"tmp/tile-orientation-deg", getprop(lw~"tmp/FL0-wind-from-heading-deg"));
}
else if (wind_model_flag == 5)
{
setprop(lw~"tmp/tile-orientation-deg", weather_dynamics.tile_wind_direction[0]);
}
}
2010-08-03 06:18:14 +00:00
# create all the neighbouring tile coordinate sets
weather_tile_management.create_neighbours(lat,lon,getprop(lw~"tmp/tile-orientation-deg"));
2010-06-15 18:54:44 +00:00
2010-06-04 21:05:32 +00:00
setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
2010-08-03 06:18:14 +00:00
# see if we need to generate a quadtree structure for clouds
if (dynamics_flag ==1)
2010-08-03 06:18:14 +00:00
{
var quadtree = [];
weather_dynamics.generate_quadtree_structure(0, quadtree);
append(weather_dynamics.cloudQuadtrees,quadtree);
}
2010-06-04 21:05:32 +00:00
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();}
2010-06-15 18:54:44 +00:00
else if (type == "Cold-sector")
{weather_tiles.set_cold_sector_tile();}
2010-08-03 06:18:14 +00:00
else if (type == "Warm-sector")
{weather_tiles.set_warm_sector_tile();}
2010-06-15 18:54:44 +00:00
else if (type == "Tropical")
{weather_tiles.set_tropical_weather_tile();}
else if (type == "Coldfront")
{weather_tiles.set_coldfront_tile();}
2010-08-03 06:18:14 +00:00
else if (type == "Warmfront")
2010-06-15 18:54:44 +00:00
{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();}
2010-06-04 21:05:32 +00:00
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();}
# mark tile as active
append(weather_tile_management.active_tile_list,1);
2010-06-04 21:05:32 +00:00
# start tile management loop if needed
if (getprop(lw~"tmp/tile-management") != "single tile") {
if (getprop(lw~"tile-loop-flag") == 0)
2010-06-15 18:54:44 +00:00
{
setprop(lw~"tiles/tile[4]/code",getprop(lw~"tiles/code"));
setprop(lw~"tile-loop-flag",1);
weather_tile_management.tile_management_loop();}
2010-06-04 21:05:32 +00:00
}
2010-04-12 20:51:46 +00:00
2010-06-04 21:05:32 +00:00
# start the interpolation loop
2010-04-12 20:51:46 +00:00
2010-06-04 21:05:32 +00:00
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);}
2010-04-12 20:51:46 +00:00
2010-08-03 06:18:14 +00:00
# start weather dynamics loops if needed
if (dynamics_flag ==1)
2010-08-03 06:18:14 +00:00
{
if (getprop(lw~"timing-loop-flag") == 0)
{setprop(lw~"timing-loop-flag",1); weather_dynamics.timing_loop();}
if (getprop(lw~"dynamics-loop-flag") == 0)
{
setprop(lw~"dynamics-loop-flag",1);
weather_dynamics.quadtree_loop();
weather_dynamics.weather_dynamics_loop(0,0);
}
if ((getprop(lw~"convective-loop-flag") == 0) and (getprop(lw~"config/dynamical-convection-flag") ==1))
{
setprop(lw~"convective-loop-flag",1);
weather_dynamics.convective_loop();
2010-08-03 06:18:14 +00:00
}
}
2010-09-10 06:52:21 +00:00
# and start the buffer loop and housekeeping loop if needed
if (getprop(lw~"config/buffer-flag") ==1)
{
if (getprop(lw~"buffer-loop-flag") == 0)
{
setprop(lw~"buffer-loop-flag",1); weather_tile_management.buffer_loop(0);
setprop(lw~"housekeeping-loop-flag",1); weather_tile_management.housekeeping_loop(0);
}
}
2011-03-19 13:03:28 +00:00
# and indicate that we're up and running
local_weather_running_flag = 1;
# weather_tile_management.watchdog_loop();
2010-09-10 06:52:21 +00:00
2010-04-12 20:51:46 +00:00
}
#################################################
# Anything that needs to run at startup goes here
#################################################
var startup = func {
print("Loading local weather routines...");
2010-06-15 18:54:44 +00:00
# get local Cartesian geometry
2010-04-12 20:51:46 +00:00
var lat = getprop("position/latitude-deg");
var lon = getprop("position/longitude-deg");
calc_geo(lat);
2010-06-15 18:54:44 +00:00
2010-04-12 20:51:46 +00:00
# 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"));
2010-06-15 18:54:44 +00:00
setprop(lw~"interpolation/turbulence",getprop(ec~"boundary/entry[0]/turbulence/magnitude-norm"));
2010-04-12 20:51:46 +00:00
setprop(lw~"interpolation/rain-norm",0.0);
setprop(lw~"interpolation/snow-norm",0.0);
setprop(lw~"interpolation/thermal-lift",0.0);
2010-06-15 18:54:44 +00:00
2010-04-12 20:51:46 +00:00
# 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"));
2010-06-15 18:54:44 +00:00
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);
2010-06-04 21:05:32 +00:00
2010-08-03 06:18:14 +00:00
2010-06-04 21:05:32 +00:00
# set listener for worker threads
2010-08-03 06:18:14 +00:00
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); });
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); });
2010-06-04 21:05:32 +00:00
setlistener(lw~"tmp/effect-thread-status", func {var s = size(effects_geo); effect_placement_loop(s); });
2010-06-15 18:54:44 +00:00
setlistener(lw~"tmp/presampling-status", func {manage_presampling(); });
2010-08-03 06:18:14 +00:00
setlistener(lw~"config/wind-model", func {set_wind_model_flag();});
2010-09-10 06:52:21 +00:00
setlistener(lw~"config/thermal-properties", func {set_texture_mix();});
setlistener(lw~"config/clouds-in-dynamics-loop", func {weather_dynamics.max_clouds_in_loop = int(getprop(lw~"config/clouds-in-dynamics-loop"));});
2010-08-03 06:18:14 +00:00
2010-09-10 06:52:21 +00:00
setlistener(lw~"config/clouds-visible-range-m", func {weather_tile_management.cloud_view_distance = getprop(lw~"config/clouds-visible-range-m");});
setlistener(lw~"config/distance-to-load-tile-m", func {setprop(lw~"config/distance-to-remove-tile-m",getprop(lw~"config/distance-to-load-tile-m") + 500.0);});
2010-04-12 20:51:46 +00:00
}
#####################################################
# Standard test call (for development and debug only)
#####################################################
var test = func {
2010-06-15 18:54:44 +00:00
var lat = getprop("position/latitude-deg");
var lon = getprop("position/longitude-deg");
2010-04-12 20:51:46 +00:00
var pos = geo.aircraft_position();
2010-09-10 06:52:21 +00:00
props.globals.getNode("/environment/terrain/area/enabled",1).setBoolValue(1);
2010-09-10 06:52:21 +00:00
setprop("/environment/terrain/area/input/analyse-every",200);
setprop("/environment/terrain/area/input/elevation-histogram-count",20);
setprop("/environment/terrain/area/input/elevation-histogram-max-ft",10000);
setprop("/environment/terrain/area/input/elevation-histogram-step-ft",500);
setprop("/environment/terrain/area/input/heading-deg",0.0);
setprop("/environment/terrain/area/input/speed-kt",-.0);
setprop("/environment/terrain/area/input/latitude-deg",lat);
setprop("/environment/terrain/area/input/longitude-deg",lon);
setprop("/environment/terrain/area/input/max-samples",1000);
setprop("/environment/terrain/area/input/max-samples-per-frame",20);
setprop("/environment/terrain/area/input/orientation-deg",0);
setprop("/environment/terrain/area/input/radius-m",40000);
2010-09-10 06:52:21 +00:00
props.globals.getNode("/environment/terrain/area/input/use-aircraft-position",1).setBoolValue(0);
2010-08-03 06:18:14 +00:00
fgcommand("reinit", props.Node.new({subsystem:"environment"}));
}
2010-08-03 06:18:14 +00:00
#################################################################
# object classes
#################################################################
2010-04-12 20:51:46 +00:00
var weatherStation = {
2011-03-19 13:03:28 +00:00
new: func (lat, lon, alt, vis, T, D, p) {
var s = { parents: [weatherStation] };
s.lat = lat;
s.lon = lon;
2011-03-19 13:03:28 +00:00
s.alt = alt;
s.vis = vis;
s.T = T;
s.D = D;
s.p = p;
return s;
},
move: func {
var windfield = weather_dynamics.get_windfield(me.index);
var dt = weather_dynamics.time_lw - me.timestamp;
me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
me.timestamp = weather_dynamics.time_lw;
},
};
2011-03-19 13:03:28 +00:00
var windIpoint = {
new: func (lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8) {
var w = { parents: [windIpoint] };
w.lat = lat;
w.lon = lon;
altvec = [];
var wv = windVec.new(d0, v0);
append(altvec,wv);
wv = windVec.new(d1, v1);
append(altvec, wv);
wv = windVec.new(d2, v2);
append(altvec, wv);
wv = windVec.new(d3, v3);
append(altvec, wv);
wv = windVec.new(d4, v4);
append(altvec, wv);
wv = windVec.new(d5, v5);
append(altvec, wv);
wv = windVec.new(d6, v6);
append(altvec, wv);
wv = windVec.new(d7, v7);
append(altvec, wv);
wv = windVec.new(d8, v8);
append(altvec, wv);
w.alt = altvec;
w.weight = 0.02;
return w;
},
};
var windVec = {
new: func (d, v) {
var wv = { parents: [windVec] };
wv.d = d;
wv.v = v;
return wv;
},
};
var effectVolume = {
new: func (geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat) {
var e = { parents: [effectVolume] };
e.geometry = geometry;
e.lat = lat;
e.lon = lon;
e.r1 = r1;
e.r2 = r2;
e.phi = phi;
e.alt_low = alt_low;
e.alt_high = alt_high;
e.vis = vis;
e.rain = rain;
e.snow = snow;
e.turb = turb;
e.lift = lift;
e.lift_flag = lift_flag;
e.sat = sat;
return e;
},
move: func {
var windfield = weather_dynamics.get_windfield(me.index);
var dt = weather_dynamics.time_lw - me.timestamp;
me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
me.timestamp = weather_dynamics.time_lw;
},
correct_altitude: func {
var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + alt_20_array[me.index-1];
var elevation = compat_layer.get_elevation(me.lat, me.lon);
me.alt_high = local_weather.get_convective_altitude(convective_alt, elevation, me.index) *1.15;
me.height = me.alt_high * 0.87;
},
correct_altitude_and_age: func {
var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1];
var elevation = -1.0; var p_cover = 0.2;
var info = geodinfo(me.lat, me.lon);
if (info != nil)
{
elevation = info[0] * local_weather.m_to_ft;
if (info[1] != nil)
{
var landcover = info[1].names[0];
if (contains(landcover_map,landcover)) {p_cover = landcover_map[landcover];}
else {p_cover = 0.2;}
}
}
me.alt_high = get_convective_altitude(convective_alt, elevation, me.index) * 1.15;
me.height = me.alt_high * 0.87;
var current_lifetime = math.sqrt(p_cover)/math.sqrt(0.35) * weather_dynamics.cloud_convective_lifetime_s;
var fractional_increase = (weather_dynamics.time_lw - me.evolution_timestamp)/current_lifetime;
me.flt = me.flt + fractional_increase;
me.evolution_timestamp = weather_dynamics.time_lw;
},
get_distance: func {
var lat = getprop("position/latitude-deg");
var lon = getprop("position/longitude-deg");
return math.sqrt(calc_d_sq(lat, lon, me.lat, me.lon));
},
};
var thermalLift = {
new: func (lat, lon, radius, height, cn, sh, max_lift, f_lift_radius) {
var l = { parents: [thermalLift] };
l.lat = lat;
l.lon = lon;
l.radius = radius;
l.height = height;
l.cn = cn;
l.sh = sh;
l.max_lift = max_lift;
l.f_lift_radius = f_lift_radius;
return l;
},
move: func {
var windfield = weather_dynamics.get_windfield(me.index);
var dt = weather_dynamics.time_lw - me.timestamp;
me.lat = me.lat + windfield[1] * dt * local_weather.m_to_lat;
me.lon = me.lon + windfield[0] * dt * local_weather.m_to_lon;
me.timestamp = weather_dynamics.time_lw;
},
correct_altitude: func {
var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + alt_20_array[me.index-1];
var elevation = compat_layer.get_elevation(me.lat, me.lon);
me.height = local_weather.get_convective_altitude(convective_alt, elevation, me.index);
},
correct_altitude_and_age: func {
var convective_alt = weather_dynamics.tile_convective_altitude[me.index-1] + local_weather.alt_20_array[me.index-1];
var elevation = -1.0; var p_cover = 0.2;
var info = geodinfo(me.lat, me.lon);
if (info != nil)
{
elevation = info[0] * local_weather.m_to_ft;
if (info[1] != nil)
{
var landcover = info[1].names[0];
if (contains(landcover_map,landcover)) {p_cover = landcover_map[landcover];}
else {p_cover = 0.2;}
}
}
me.height = get_convective_altitude(convective_alt, elevation, me.index);
var current_lifetime = math.sqrt(p_cover)/math.sqrt(0.35) * weather_dynamics.cloud_convective_lifetime_s;
var fractional_increase = (weather_dynamics.time_lw - me.evolution_timestamp)/current_lifetime;
me.flt = me.flt + fractional_increase;
me.evolution_timestamp = weather_dynamics.time_lw;
},
2010-06-15 18:54:44 +00:00
};
2010-06-15 18:54:44 +00:00
2010-04-12 20:51:46 +00:00
var waveLift = {
new: func (lat, lon, x, y, phi, height, max_lift) {
var w = { parents: [waveLift] };
w.lat = lat;
w.lon = lon;
w.x = x;
w.y = y;
w.phi = phi;
w.height = height;
w.max_lift = max_lift;
w.phi = getprop(lw~"tmp/tile-orientation-deg");
return w;
},
2010-04-12 20:51:46 +00:00
};
2010-04-12 20:51:46 +00:00
#################################################################
# 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
2011-03-19 13:03:28 +00:00
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, 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, Bog: 0.05, 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};
2010-04-12 20:51:46 +00:00
# a hash map of average vertical cloud model sizes
2010-08-03 06:18:14 +00:00
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, Cirrocumulus: 200.0};
# the array of aloft wind interpolation altitudes
var wind_altitude_array = [0.0, 5000.0, 10000.0, 18000.0, 24000.0, 30000.0, 34000.0, 39000.0, 45000.0];
2010-06-04 21:05:32 +00:00
# storage arrays for cloud generation
2010-06-15 18:54:44 +00:00
var clouds_path = [];
var clouds_lat = [];
var clouds_lon = [];
var clouds_alt = [];
var clouds_orientation = [];
2010-06-04 21:05:32 +00:00
# additional info needed for dynamical clouds: the base altitude around which cloudlets are distributed
# and the fractional lifetime
var clouds_mean_alt = [];
var clouds_flt = [];
var clouds_evolution_timestamp = [];
2010-08-03 06:18:14 +00:00
# storage arrays for terrain presampling and results by tile
2010-06-15 18:54:44 +00:00
var terrain_n = [];
2010-08-03 06:18:14 +00:00
var alt_50_array = [];
2010-09-10 06:52:21 +00:00
var alt_20_array = [];
2010-08-03 06:18:14 +00:00
# array of currently existing effect volumes
var effectVolumeArray = [];
var n_effectVolumeArray = 0;
# the thermal and the wave hash
var thermal = {};
var wave = {};
2011-03-19 13:03:28 +00:00
# arrays of currently existing weather stations and wind interpolation points
var weatherStationArray = [];
2011-03-19 13:03:28 +00:00
var windIpointArray = [];
2010-08-03 06:18:14 +00:00
# a flag for the wind model (so we don't have to do string comparisons all the time)
# 1: constant 2: constant in tile 3: aloft interpolated 4: airmass interpolated
var wind_model_flag = 1;
2010-09-10 06:52:21 +00:00
# a global determining the relative amount of different textures in detailed convective clouds
var convective_texture_mix = 0.0;
# a global keeping track of the mean cloud altitude when building a Cumulus from individual cloudlets
var cloud_mean_altitude = 0.0;
# globals keeping track of the lifetime when building a Cumulus from individual cloudlets
var cloud_fractional_lifetime = 0.0;
var cloud_evolution_timestamp = 0.0;
2011-03-19 13:03:28 +00:00
# globals propagating gust information inside the interpolation loop
var windspeed_multiplier = 1.0;
var winddir_change = 0.0;
# global flags mirroring property tree menu settings
var generate_thermal_lift_flag = 0;
var thread_flag = 1;
var dynamics_flag = 1;
var presampling_flag = 1;
var detailed_clouds_flag = 1;
var dynamical_convection_flag = 1;
var debug_output_flag = 1;
2011-01-05 12:36:10 +00:00
var metar_flag = 0;
2011-03-19 13:03:28 +00:00
var local_weather_running_flag = 0;
2010-04-12 20:51:46 +00:00
# set all sorts of default properties for the menu
2010-06-04 21:05:32 +00:00
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);
2010-04-12 20:51:46 +00:00
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);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/box-x-m",600.0);
setprop(lw~"tmp/box-y-m",600.0);
2010-06-15 18:54:44 +00:00
setprop(lw~"tmp/box-alt-ft",300.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/box-n",10);
setprop(lw~"tmp/box-core-fraction",0.4);
setprop(lw~"tmp/box-core-offset",0.2);
setprop(lw~"tmp/box-core-height",1.4);
setprop(lw~"tmp/box-core-n",3);
setprop(lw~"tmp/box-bottom-fraction",0.9);
setprop(lw~"tmp/box-bottom-thickness",0.5);
setprop(lw~"tmp/box-bottom-n",12);
2010-06-04 21:05:32 +00:00
setprop(lw~"tmp/tile-type", "High-pressure");
setprop(lw~"tmp/tile-orientation-deg", 260.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/windspeed-kt", 8.0);
2011-03-19 13:03:28 +00:00
setprop(lw~"tmp/gust-frequency-hz", 0.0);
setprop(lw~"tmp/gust-relative-strength",0.0);
setprop(lw~"tmp/gust-angular-variation-deg",0.0);
2010-04-12 20:51:46 +00:00
setprop(lw~"tmp/tile-alt-offset-ft", 0.0);
2010-06-15 18:54:44 +00:00
setprop(lw~"tmp/tile-alt-median-ft",0.0);
setprop(lw~"tmp/tile-alt-min-ft",0.0);
2011-03-19 13:03:28 +00:00
setprop(lw~"tmp/tile-management", "realistic weather");
2010-06-15 18:54:44 +00:00
setprop(lw~"tmp/presampling-flag", 1);
2010-06-04 21:05:32 +00:00
setprop(lw~"tmp/asymmetric-tile-loading-flag", 0);
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");
2010-06-15 18:54:44 +00:00
setprop(lw~"tmp/presampling-status", "idle");
2010-09-10 06:52:21 +00:00
setprop(lw~"tmp/buffer-status", "idle");
setprop(lw~"tmp/buffer-tile-index", 0);
setprop(lw~"tmp/FL0-wind-from-heading-deg",260.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/FL0-windspeed-kt",8.0);
setprop(lw~"tmp/FL50-wind-from-heading-deg",262.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/FL50-windspeed-kt",11.0);
setprop(lw~"tmp/FL100-wind-from-heading-deg",264.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/FL100-windspeed-kt",16.0);
setprop(lw~"tmp/FL180-wind-from-heading-deg",265.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/FL180-windspeed-kt",24.0);
setprop(lw~"tmp/FL240-wind-from-heading-deg",269.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/FL240-windspeed-kt",35.0);
setprop(lw~"tmp/FL300-wind-from-heading-deg",273.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/FL300-windspeed-kt",45.0);
setprop(lw~"tmp/FL340-wind-from-heading-deg",274.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/FL340-windspeed-kt",50.0);
setprop(lw~"tmp/FL390-wind-from-heading-deg",273.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/FL390-windspeed-kt",56.0);
setprop(lw~"tmp/FL450-wind-from-heading-deg",272.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"tmp/FL450-windspeed-kt",65.0);
setprop(lw~"tmp/ipoint-latitude-deg",getprop("position/latitude-deg"));
setprop(lw~"tmp/ipoint-longitude-deg",getprop("position/longitude-deg"));
2010-06-04 21:05:32 +00:00
# set config values
2010-09-10 06:52:21 +00:00
setprop(lw~"config/distance-to-load-tile-m",39000.0);
setprop(lw~"config/distance-to-remove-tile-m",39500.0);
2010-06-15 18:54:44 +00:00
setprop(lw~"config/detailed-clouds-flag",1);
2011-03-19 13:03:28 +00:00
setprop(lw~"config/dynamics-flag",0);
2010-06-15 18:54:44 +00:00
setprop(lw~"config/thermal-properties",1.0);
2010-08-03 06:18:14 +00:00
setprop(lw~"config/wind-model","constant");
2010-09-10 06:52:21 +00:00
setprop(lw~"config/buffer-flag",1);
setprop(lw~"config/asymmetric-reduction",0.7);
setprop(lw~"config/clouds-visible-range-m",30000.0);
setprop(lw~"config/asymmetric-buffering-flag",0);
setprop(lw~"config/asymmetric-buffering-reduction",0.3);
setprop(lw~"config/asymmetric-buffering-angle-deg",90.0);
setprop(lw~"config/clouds-in-dynamics-loop",250);
2011-03-19 13:03:28 +00:00
setprop(lw~"config/debug-output-flag",0);
setprop(lw~"config/generate-thermal-lift-flag", 0);
2011-03-19 13:03:28 +00:00
setprop(lw~"config/dynamical-convection-flag", 0);
setprop(lw~"config/thread-flag", 1);
2010-04-12 20:51:46 +00:00
# set the default loop flags to loops inactive
2010-06-04 21:05:32 +00:00
2010-04-12 20:51:46 +00:00
setprop(lw~"effect-loop-flag",0);
setprop(lw~"interpolation-loop-flag",0);
2010-06-04 21:05:32 +00:00
setprop(lw~"tile-loop-flag",0);
setprop(lw~"lift-loop-flag",0);
setprop(lw~"wave-loop-flag",0);
2010-09-10 06:52:21 +00:00
setprop(lw~"buffer-loop-flag",0);
setprop(lw~"housekeeping-loop-flag",0);
setprop(lw~"convective-loop-flag",0);
2010-04-12 20:51:46 +00:00
2010-06-04 21:05:32 +00:00
# create other management properties
2010-04-12 20:51:46 +00:00
#setprop(lw~"clouds/cloud-number",0);
2010-06-04 21:05:32 +00:00
setprop(lw~"clouds/placement-index",0);
setprop(lw~"clouds/model-placement-index",0);
setprop(lw~"effect-volumes/effect-placement-index",0);
2010-04-12 20:51:46 +00:00
# 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);
setprop(lw~"effect-volumes/number-active-sat",0);
2010-06-15 18:54:44 +00:00
2010-06-04 21:05:32 +00:00
# create properties for tile management
setprop(lw~"tiles/tile-counter",0);
2010-08-03 06:18:14 +00:00
# create properties for wind
setprop(lwi~"ipoint-number",0);
2010-06-04 21:05:32 +00:00
2010-04-12 20:51:46 +00:00
# wait for Nasal to be available and do what is in startup()
_setlistener("/sim/signals/nasal-dir-initialized", func {
startup();
});