1
0
Fork 0

Local Weather 1.02 by Thorsten Renk

This commit is contained in:
Frederic Bouvier 2011-04-19 22:55:25 +02:00
parent 995a219b35
commit d151a25e94
4 changed files with 291 additions and 77 deletions

View file

@ -87,12 +87,24 @@ else
{result = "yes"; features.terrain_presampling = 1;}
print("* hard coded terrain presampling: "~result);
if ((props.globals.getNode("/environment/terrain/area[0]/enabled",1).getBoolValue() == 1) and (features.terrain_presampling ==1))
{result = "yes"; features.terrain_presampling_active = 1;}
else
{result = "no"; features.terrain_presampling_active = 0;}
print("* terrain presampling initialized: "~result);
if (props.globals.getNode("/environment/config/enabled", 0) == nil)
{result = "no"; features.can_disable_environment = 0;}
else
{result = "yes"; features.can_disable_environment = 1;}
print("* can disable global weather: "~result);
#if (features.terrain_presampling_active == 1)
# {
# setlistener("/environment/terrain/area[0]/output/valid", func {local_weather.manage_hardcoded_presampling(); });
# }
print("Compatibility layer: tests done.");
});

View file

@ -323,7 +323,6 @@ return res;
var wind_interpolation = func (lat, lon, alt) {
# var windNodes = props.globals.getNode(lw~"interpolation").getChildren("wind");
var sum_norm = 0;
var sum_wind = [0,0];
@ -331,8 +330,6 @@ var wsize = size(windIpointArray);
for (var i = 0; i < wsize; i=i+1) {
#var wlat = w.getNode("latitude-deg").getValue();
#var wlon = w.getNode("longitude-deg").getValue();
var w = windIpointArray[i];
@ -403,9 +400,11 @@ else
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);
if (debug_output_flag == 1)
{
print("Boundary layer thickness: ",base_layer_thickness);
print("Boundary layer slowdown: ", f_slow);
}
return f_slow;
}
@ -486,7 +485,7 @@ for (var i = 0; i < n_stations; i=i+1) {
}
}
setprop(lwi~"station-number", i);
setprop(lwi~"station-number", i+1);
var ialt = sum_alt/sum_norm;
@ -2690,7 +2689,10 @@ for (var i=0; i<n_bottom; i=i+1)
var terrain_presampling_start = func (blat, blon, nc, size, alpha) {
# terrain presampling start is always used the first time, and initializes
# the hard-coded routine if that is available since the hard-coded routine cannot
# be yet read out on startup
# initialize the result vector
setsize(terrain_n,20);
@ -2716,6 +2718,20 @@ else
terrain_presampling_analysis();
setprop(lw~"tmp/presampling-status", "finished");
}
if (compat_layer.features.terrain_presampling_active == 1)
{
print("Starting hard-coded terrain presampling");
setprop(lw~"tmp/presampling-status", "sampling");
setprop("/environment/terrain/area[0]/input/latitude-deg", blat );
setprop("/environment/terrain/area[0]/input/longitude-deg", blon );
setprop("/environment/terrain/area[0]/input/use-aircraft-position",1);
setprop("/environment/terrain/area[0]/input/radius-m",45000.0);
setprop("/environment/terrain/area[0]/output/valid", 0 );
}
}
###########################################################
@ -2803,63 +2819,72 @@ for (i=0; i<ntries;i=i+1)
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)
if ((compat_layer.features.terrain_presampling_active == 0) or (getprop(lw~"tiles/tile-counter") == 0))
{
sum = sum + terrain_n[i];
if (sum > int(0.5 *n_tot)) {alt_med = i * 500.0; break;}
var sum = 0;
var alt_mean = 0;
var alt_med = 0;
var alt_20 = 0;
var alt_min = 0;
var alt_low_min = 0;
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;}
}
}
sum = 0;
for (var i=0; i<20;i=i+1)
else
{
sum = sum + terrain_n[i];
if (sum > int(0.3 *n_tot)) {alt_20 = i * 500.0; break;}
}
for (var i=0; i<20;i=i+1) {alt_mean = alt_mean + terrain_n[i] * i * 500.0;}
alt_mean = alt_mean/n_tot;
for (var i=0; i<20;i=i+1) {if (terrain_n[i] > 0) {alt_min = i * 500.0; break;}}
var n_max = 0;
sum = 0;
for (var i=0; i<19;i=i+1)
{
sum = sum + terrain_n[i];
if (terrain_n[i] > n_max) {n_max = terrain_n[i];}
if ((n_max > terrain_n[i+1]) and (sum > int(0.3*n_tot)))
{alt_low_min = i * 500; break;}
#print("Hard-coded sampling...");
var n_tot = getprop("/environment/terrain/area[0]/input/max-samples");
var alt_mean = getprop("/environment/terrain/area[0]/output/alt-mean-ft");
var alt_med = getprop("/environment/terrain/area[0]/output/alt-median-ft");
var alt_min = getprop("/environment/terrain/area[0]/output/alt-min-ft");
var alt_20 = getprop("/environment/terrain/area[0]/output/alt-offset-ft");
}
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);}
#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));
setprop(lw~"tmp/tile-alt-layered-ft",0.5 * (alt_min + alt_20));
append(alt_50_array, alt_med);
append(alt_20_array, alt_20);
@ -2945,8 +2970,11 @@ return balt + shift_strength * alt_diff * fraction;
var manage_presampling = func {
var status = getprop(lw~"tmp/presampling-status");
# we only take action when the analysis is done
if (status != "finished") {return;}
@ -2969,6 +2997,32 @@ else # the tile setup call came from weather_tile_management
setprop(lw~"tmp/presampling-status", "idle");
}
###########################################################
# hardcoded terrain presampling listener dispatcher
###########################################################
var manage_hardcoded_presampling = func {
var status = getprop("/environment/terrain/area[0]/enabled");
print("Hard-coded terrain presampling status: ", status);
# no action unless the sampler has finished
if (status ==0) {return;}
# no action if the sampler hasn't been started
if (getprop(lw~"tmp/presampling-status") != "sampling") {return;}
terrain_presampling_analysis();
if (debug_output_flag == 1)
{print("Presampling done!");}
setprop(lw~"tmp/presampling-status", "finished");
}
###########################################################
@ -3800,25 +3854,12 @@ var lon = getprop("position/longitude-deg");
var pos = geo.aircraft_position();
props.globals.getNode("/environment/terrain/area/enabled",1).setBoolValue(1);
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);
setprop("/environment/terrain/area[0]/input/latitude-deg", lat );
setprop("/environment/terrain/area[0]/input/longitude-deg", lon );
props.globals.getNode("/environment/terrain/area/input/use-aircraft-position",1).setBoolValue(0);
setprop("/environment/terrain/area[0]/output/valid", 0 );
fgcommand("reinit", props.Node.new({subsystem:"environment"}));
}
@ -4057,7 +4098,7 @@ var ec = "/environment/config/";
# a hash map of the strength for convection associated with terrain types
var landcover_map = {BuiltUpCover: 0.35, Town: 0.35, Freeway:0.35, BarrenCover:0.3, HerbTundraCover: 0.25, GrassCover: 0.2, CropGrassCover: 0.2, 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};
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, DeciduousNeedleCover: 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};
# a hash map of average vertical cloud model sizes

View file

@ -411,11 +411,13 @@ if (((local_weather.presampling_flag == 1) and (getprop(lw~"tmp/presampling-stat
# now see if we need to presample the terrain
if ((local_weather.presampling_flag == 1) and (getprop(lw~"tmp/presampling-status") == "idle"))
if ((local_weather.presampling_flag == 1) and (getprop(lw~"tmp/presampling-status") == "idle") and (compat_layer.features.terrain_presampling_active == 0))
{
local_weather.terrain_presampling_start(lat, lon, 1000, 40000, getprop(lw~"tmp/tile-orientation-deg"));
return;
}
else if (compat_layer.features.terrain_presampling_active == 1)# we have hard-coded values available and use those
{local_weather.terrain_presampling_analysis();}
if (local_weather.debug_output_flag == 1)
@ -439,6 +441,7 @@ if (getprop(lw~"tmp/tile-management") == "repeat tile")
else if (code == "cold_sector") {weather_tiles.set_cold_sector_tile();}
else if (code == "warm_sector") {weather_tiles.set_warm_sector_tile();}
else if (code == "tropical_weather") {weather_tiles.set_tropical_weather_tile();}
#else if (code == "test") {weather_tiles.set_4_8_stratus_tile;}
else
{
print("Repeat tile not implemented with this tile type!");
@ -916,7 +919,7 @@ if ((index == 2) or (index == 5) or (index == 8)) {x = 40000.0;}
if ((index == 0) or (index == 1) or (index == 2)) {y = 40000.0;}
if ((index == 6) or (index == 7) or (index == 8)) {y = -40000.0;}
var t = props.globals.getNode(lw~"tiles").getChild("tile",index,0);
var t = props.globals.getNode(lw~"tiles").getChild("tile",index,1);
# use the last built tile code as default, in case a tile isn't formed when reached,
# the code is not empty but has a plausible value
@ -1263,6 +1266,73 @@ var i = 0;
print("====================");
var viewpos = geo.aircraft_position();
var n_stations = size(local_weather.weatherStationArray);
var sum_T = 0.0;
var sum_p = 0.0;
var sum_D = 0.0;
var sum_norm = 0.0;
var sum_wind = [0,0];
var wsize = size(local_weather.windIpointArray);
var alt = getprop("position/altitude-ft");
for (var i = 0; i < wsize; i=i+1) {
var w = local_weather.windIpointArray[i];
var wpos = geo.Coord.new();
wpos.set_latlon(w.lat,w.lon,1000.0);
var d = viewpos.distance_to(wpos);
if (d <100.0) {d = 100.0;} # to prevent singularity at zero
sum_norm = sum_norm + (1./d);
var res = local_weather.wind_altitude_interpolation(alt,w);
sum_wind = local_weather.add_vectors(sum_wind[0], sum_wind[1], res[0], (res[1]/d));
print(i, " dir: ", res[0], " speed: ", res[1], " d: ",d);
}
print("dir_int: ", sum_wind[0], " speed_int: ", sum_wind[1]/sum_norm);
if (0==1)
{
for (var i = 0; i < n_stations; i=i+1) {
s = local_weather.weatherStationArray[i];
var stpos = geo.Coord.new();
stpos.set_latlon(s.lat,s.lon,0.0);
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;
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;
print(i, " p: ", s.p, " T: ", s.T, " D: ", s.D, " d: ",d);
}
print("p_int: ", sum_p/sum_norm, " T_int: ", sum_T/sum_norm, " D_int: ", sum_D/sum_norm);
}
if (0==1)
{
foreach(t; tNode)
{
var code = t.getNode("code").getValue();
@ -1283,6 +1353,8 @@ var res = local_weather.wind_interpolation(lat,lon,0.0);
print("Wind: ", res[0], " tile alpha: ", getprop(lw~"tiles/tile[4]/orientation-deg"));
print("Mismatch: ", relangle(res[0], getprop(lw~"tiles/tile[4]/orientation-deg")));
}
print("====================");

View file

@ -95,6 +95,7 @@ calc_geo(blat);
local_weather.set_weather_station(blat, blon, alt_offset, 20000.0, 14.0, 12.0, 29.78);
create_8_8_nimbus_var3(blat, blon, 2000.0 + alt_offset+local_weather.cloud_vertical_size_map["Nimbus"] * 0.5 * m_to_ft, 0.0);
#create_2_8_sstratus_streak(blat, blon,5000.0,0.0);
@ -106,10 +107,11 @@ local_weather.set_weather_station(blat, blon, alt_offset, 20000.0, 14.0, 12.0, 2
#create_detailed_stratocumulus_bank(blat, blon,5000.0+alt_offset,0.0);
create_4_8_altocumulus_perlucidus(blat, blon, 10000.0, 0.0);
#create_4_8_altocumulus_perlucidus(blat, blon, 10000.0, 0.0);
local_weather.create_effect_volume(3, blat, blon, 20000.0, 7000.0, alpha, 0.0, 80000.0, -1, -1, -1, -1, 15.0, -3,-1);
#local_weather.create_effect_volume(3, blat, blon, 20000.0, 7000.0, alpha, 0.0, 80000.0, -1, -1, -1, -1, 15.0, -3,-1);
create_1_8_contrails(blat, blon, 30000.0, 0.0);
# store convective altitude and strength
@ -2121,7 +2123,7 @@ var set_METAR_weather_station = func {
# if we use aloft interpolated winds with METAR, also set a new wind interpolation point
if (local_weather.wind_model_flag == 5)
if ((local_weather.wind_model_flag == 5) and (getprop(lw~"tiles/tile-counter") !=1))
{
# if zero winds are reported, we do not rotate the tile to face north but use the last value
@ -2187,19 +2189,84 @@ local_weather.create_streak("Cirrostratus",lat,lon,alt,500.0,30,1250.0,0.0,400.0
var create_8_8_nimbus = func (lat, lon, alt, alpha) {
local_weather.create_streak("Nimbus",lat, lon, alt,500.0,32,1250.0,0.0,200.0,32,1250.0,0.0,200.0,alpha,1.0);
}
var create_8_8_nimbus_var1 = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
local_weather.create_streak("Nimbus",lat, lon, alt,500.0,35,1111.0,0.0,200.0,35,1111.0,0.0,200.0,alpha,1.0);
for (var i = 0; i < 3; i=i+1)
{
var x = -15000.0 + 30000.0 * rand();
var y = -15000.0 + 30000.0 * rand();
local_weather.create_streak("Stratocumulus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt+600.0,200.0,11,1100.0,0.1,800.0,8,1100.0,0.1,800.0,alpha,1.4);
}
}
var create_8_8_nimbus_var2 = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
local_weather.create_streak("Nimbus",lat, lon, alt,500.0,35,1111.0,0.0,200.0,35,1111.0,0.0,200.0,alpha,1.0);
for (var i=0; i<8; i=i+1)
{
var x = 2.0 * (rand()-0.5) * 18000;
var y = 2.0 * (rand()-0.5) * 18000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus (structured)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt+1200.0,300.0,5,900.0,0.2,500.0,7,900.0,0.2,500.0,alpha+beta,1.0);
}
}
var create_8_8_nimbus_var3 = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
local_weather.create_streak("Nimbus",lat, lon, alt,500.0,35,1111.0,0.0,200.0,35,1111.0,0.0,200.0,alpha,1.0);
for (var i=0; i<6; i=i+1)
{
var x = 2.0 * (rand()-0.5) * 18000;
var y = 2.0 * (rand()-0.5) * 18000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt+1600.0,300.0,6,1200.0,0.2,700.0,6,1200.0,0.2,700.0,alpha+beta,1.0);
}
# reduced visibility in layer
#local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, alt-1500.0, alt+900.0, 2000.0, -1 , -1, -1, -1,0 ,-1);
# cloud shade
#local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, 0.0, alt, -1, -1 , -1, -1, -1,0 ,0.8);
}
var create_8_8_nimbus_rain = func (lat, lon, alt, alpha, rain) {
local_weather.create_streak("Nimbus",lat, lon, alt,500.0,32,1250.0,0.0,200.0,32,1250.0,0.0,200.0,alpha,1.0);
if (local_weather.detailed_clouds_flag == 0)
{local_weather.create_streak("Nimbus",lat, lon, alt,500.0,32,1250.0,0.0,200.0,32,1250.0,0.0,200.0,alpha,1.0);}
else
{
var rn = rand();
if (rn > 0.66) {create_8_8_nimbus_var1(lat, lon, alt, alpha);}
else if (rn > 0.33) {create_8_8_nimbus_var2(lat, lon, alt, alpha);}
else {create_8_8_nimbus_var3(lat, lon, alt, alpha);}
}
if (rain > 0.1)
{
local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, 0.0, alt, 500.0 + (1.0 - 0.5 * rain) * 5500.0, 0.5 * rain , -1, -1, -1,0 ,0.95);
local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, 0.0, alt+900.0, 500.0 + (1.0 - 0.5 * rain) * 5500.0, 0.5 * rain , -1, -1, -1,0 ,0.95);
local_weather.create_effect_volume(3, lat , lon, 16000.0, 16000.0, alpha, 0.0, alt - 300.0, 500.0 + (1.0-rain) * 5500.0, rain, -1, -1, -1,0 ,0.8);
}
else
{
local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, alt-1500.0, alt+900.0, 2000.0, -1 , -1, -1, -1,0 ,-1);
local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, 0.0, alt, -1, rain , -1, -1, -1,0 ,0.8);
}
@ -2245,12 +2312,13 @@ for (var i = 0; i < 3; i = i + 1)
if (rain > 0.1)
{
local_weather.create_effect_volume(2, lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), 10000.0, 6000.0, beta, 0.0, alt, 500.0 + (1.0-0.5*rain) * 5500.0, 0.5 * rain, -1, -1, -1,0,0.95 );
local_weather.create_effect_volume(2, lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), 10000.0, 6000.0, beta, 0.0, alt+900, 500.0 + (1.0-0.5*rain) * 5500.0, 0.5 * rain, -1, -1, -1,0,0.95 );
local_weather.create_effect_volume(2, lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), 9000.0, 5000.0, beta, 0.0, alt-300.0, 500.0 + (1.0-rain) * 5500.0, rain, -1, -1, -1,0,0.8);
}
else
{
local_weather.create_effect_volume(2, lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), 10000.0, 6000.0, beta, 0.0, alt, -1, rain, -1, -1, -1,0, 0.8 );
local_weather.create_effect_volume(2, lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), 10000.0, 6000.0, beta, alt-1500.0, alt+900.0, 2000.0, -1, -1, -1, -1,0, 0.8 );
}
}
@ -2708,6 +2776,27 @@ for (var i = 0; i < 2; i = i + 1)
}
var create_1_8_contrails = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
var n_contrails = int(rand() * 3.0) + 1;
for (var i=0; i<n_contrails; i=i+1)
{
var x = 2.0 * (rand()-0.5) * 5000;
var y = 2.0 * (rand()-0.5) * 5000;
var alt_variation = 2.0 * (rand()-0.5) * 4000.0;
var beta = rand() * 180;
var contrail_length = 20 + int(rand() * 30);
local_weather.create_streak("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt+alt_variation,100.0,contrail_length,500.0,0.2,50.0,2,100.0,0.0,300.0,alpha+beta,1.0);
}
}
var create_thunderstorm_scenario = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;