1
0
Fork 0

Local weather 0.92 by Thorsten Renk

This commit is contained in:
Frederic Bouvier 2011-01-05 13:36:10 +01:00
parent 332fa9c0c8
commit 3ff6a53bf1
3 changed files with 288 additions and 74 deletions

View file

@ -14,6 +14,7 @@
# 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
# get_slowdown_fraction to compute the effect of boundary layer wind slowdown
# 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
@ -340,6 +341,52 @@ return sum_wind;
}
###################################
# 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;
}
###################################
# interpolation management loop
###################################
@ -365,6 +412,7 @@ var n_stations = size(weatherStationArray);
for (var i = 0; i < n_stations; i=i+1) {
s = weatherStationArray[i];
var stpos = geo.Coord.new();
stpos.set_latlon(s.lat,s.lon,0.0);
@ -387,7 +435,8 @@ for (var i = 0; i < n_stations; i=i+1) {
# automatically delete stations out of range
# take care not to unload if weird values appear for a moment
if ((d > 80000.0) and (d<100000.0))
# never unload if only one station left
if ((d > 120000.0) and (d<140000.0) and (n_stations > 1))
{
if (debug_output_flag == 1)
{print("Distance to weather station ", d, " m, unloading ...", i);}
@ -3169,11 +3218,12 @@ if (dynamical_convection_flag == 1)
}
# if we can do so, we switch global weather off at this point
# 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);
props.globals.getNode("/environment/params/metar-updates-environment").setBoolValue(0);
}
@ -3187,7 +3237,31 @@ if ((presampling_flag == 1) and (getprop(lw~"tmp/presampling-status") == "idle")
}
# 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");
# switch off normal 3d clouds
var layers = props.globals.getNode("/environment/clouds").getChildren("layer");
foreach (l; layers)
{
l.getNode("coverage-type").setValue(5);
}
}
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;
}
# see if we need to create an aloft wind interpolation structure
@ -3198,49 +3272,69 @@ if ((wind_model_flag == 3) or ((wind_model_flag ==5) and (getprop(lwi~"ipoint-nu
# prepare the first tile wind field
if (wind_model_flag == 5) # it needs to be interpolated
if (metar_flag == 1) # the winds from current METAR are used
{
var res = wind_interpolation(lat,lon,0.0);
if ((wind_model_flag == 1) or (wind_model_flag == 2))
{
# 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);
append(weather_dynamics.tile_wind_direction,res[0]);
append(weather_dynamics.tile_wind_speed,res[1]);
var boundary_correction = 1.0/get_slowdown_fraction();
append(weather_dynamics.tile_wind_direction, getprop("environment/metar/base-wind-dir-deg"));
append(weather_dynamics.tile_wind_speed, boundary_correction * getprop("environment/metar/base-wind-speed-kt"));
setprop(lw~"tmp/tile-orientation-deg",getprop("environment/metar/base-wind-dir-deg"));
}
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;
}
}
else if (wind_model_flag == 3) # it comes from a different menu
else
{
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"));
}
# 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
if (wind_model_flag == 5) # it needs to be interpolated
{
var res = wind_interpolation(lat,lon,0.0);
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"));
}
# 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
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]);
}
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]);
}
}
# create all the neighbouring tile coordinate sets
weather_tile_management.create_neighbours(lat,lon,getprop(lw~"tmp/tile-orientation-deg"));
# see if we use METAR for weather setup
if ((getprop(lw~"METAR/available-flag") == 1) and (getprop(lw~"tmp/tile-management") == "METAR"))
{type = "METAR";}
setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
@ -3743,6 +3837,7 @@ var presampling_flag = 1;
var detailed_clouds_flag = 1;
var dynamical_convection_flag = 1;
var debug_output_flag = 1;
var metar_flag = 0;
# set all sorts of default properties for the menu

View file

@ -189,21 +189,60 @@ if (((local_weather.presampling_flag == 1) and (getprop(lw~"tmp/presampling-stat
if ((local_weather.wind_model_flag == 2) or (local_weather.wind_model_flag ==4))
{
alpha = alpha + 2.0 * (rand()-0.5) * 10.0;
if (local_weather.metar_flag == 0)
{
alpha = alpha + 2.0 * (rand()-0.5) * 10.0;
# account for the systematic spin of weather systems around a low pressure
# core dependent on hemisphere
if (lat >0.0) {alpha = alpha -3.0;}
else {alpha = alpha +3.0;}
}
else
{
var step = 20.0;
var alpha_test = getprop("/environment/metar/base-wind-dir-deg");
print("alpha: ", alpha, " alpha_test: ", alpha_test, " relangle: ", relangle(alpha, alpha_test));
if (relangle(alpha, alpha_test) > step)
{
print("Coordinate rotation by more than ",step," deg... compensating");
if (relangle(alpha + step, alpha_test) < relangle(alpha-step, alpha_test))
{
alpha = alpha + step;
}
else
{
alpha = alpha - step;
}
}
else
{
alpha = alpha_test;
}
}
# account for the systematic spin of weather systems around a low pressure
# core dependent on hemisphere
if (lat >0.0) {alpha = alpha -3.0;}
else {alpha = alpha +3.0;}
setprop(lw~"tmp/tile-orientation-deg",alpha);
# compute the new windspeed
var windspeed = getprop(lw~"tmp/windspeed-kt");
windspeed = windspeed + 2.0 * (rand()-0.5) * 2.0;
if (windspeed < 0) {windspeed = rand();}
if (local_weather.metar_flag == 0)
{
var windspeed = getprop(lw~"tmp/windspeed-kt");
windspeed = windspeed + 2.0 * (rand()-0.5) * 2.0;
if (windspeed < 0) {windspeed = rand();}
}
else
{
var boundary_correction = 1.0/local_weather.get_slowdown_fraction();
windspeed = boundary_correction * getprop("/environment/metar/base-wind-speed-kt");
}
setprop(lw~"tmp/windspeed-kt",windspeed);
# store the tile orientation and wind strength in an array for fast processing
@ -392,6 +431,10 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather")
}
} # end if mode == realistic weather
else if (getprop(lw~"tmp/tile-management") == "METAR")
{
weather_tiles.set_METAR_tile();
}
}
@ -1227,6 +1270,17 @@ return (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon;
}
var relangle = func (alpha, beta) {
var angdiff = abs (alpha - beta);
if ((360.0 - angdiff) < angdiff)
{angdiff = 360.0 - angdiff};
return angdiff;
}
var delete_from_vector = func(vec, index) {
var n = index+1;

View file

@ -1735,9 +1735,11 @@ var lon = 0.0;
var alpha = getprop(lw~"METAR/wind-direction-deg");
var alpha = getprop("/environment/metar/base-wind-dir-deg");
var phi = alpha * math.pi/180.0;
var alt_offset = getprop(lw~"METAR/altitude-ft");
var metar_alt_offset = 700.0 + getprop("/environment/metar/station-elevation-ft");
print("metar_alt_offset", metar_alt_offset);
# get the local time of the day in seconds
@ -1752,32 +1754,44 @@ calc_geo(blat);
# get the METAR position info
var station_lat = getprop(lw~"METAR/latitude-deg");
var station_lon = getprop(lw~"METAR/longitude-deg");
var station_lat = getprop("/environment/metar/station-latitude-deg");
var station_lon = getprop("/environment/metar/station-longitude-deg");
# get the weather parameters
var vis = getprop(lw~"METAR/visibility-m");
var T = getprop(lw~"METAR/temperature-degc");
var D = getprop(lw~"METAR/dewpoint-degc");
var p = getprop(lw~"METAR/pressure-inhg");
var rain_norm = getprop(lw~"METAR/rain-norm");
var snow_norm = getprop(lw~"METAR/snow-norm");
var vis = getprop("/environment/metar/max-visibility-m");
var T = getprop("/environment/metar/temperature-sea-level-degc");
var D = getprop("/environment/metar/dewpoint-sea-level-degc");
var p = getprop("/environment/metar/pressure-sea-level-inhg");
var rain_norm = getprop("/environment/metar/rain-norm");
var snow_norm = getprop("/environment/metar/snow-norm");
if (getprop(lw~"METAR/station-id") != getprop("/environment/metar/station-id")) # the weather station has changed, set a new station
{
# set the cstation
local_weather.set_weather_station(station_lat, station_lon, vis, T, D, p);
# and mark that we have used this station
setprop(lw~"METAR/station-id",getprop("/environment/metar/station-id"));
}
# and set the corresponding station
local_weather.set_weather_station(station_lat, station_lon, vis, T, D, p);
# now get the cloud layer info
var layers = props.globals.getNode(lw~"METAR", 1).getChildren("layer");
var layers = props.globals.getNode("/environment/metar/clouds", 1).getChildren("layer");
var n_layers = size(layers); # the system initializes with 4 layers, but who knows...
var n = 0; # start with lowest layer
# now determine the nature of the lowest layer
var cumulus_flag = 1; # default assumption - the lowest layer is cumulus
var cover_low = layers[0].getNode("cover-oct").getValue();
var alt_low = layers[0].getNode("alt-agl-ft").getValue();
var cover_low = 8 - 2 * layers[0].getNode("coverage-type").getValue(); # conversion to oktas
var alt_low = layers[0].getNode("elevation-ft").getValue();
print("alt_low: ", alt_low);
# first check a few obvious criteria
@ -1799,7 +1813,7 @@ if ((cover_low == 3) or (cover_low == 4)) # scattered
# now see if there is a layer shading convective development
var coverage_above = layers[1].getNode("cover-oct").getValue();
var coverage_above = 8 - 2 * layers[1].getNode("coverage-type").getValue();
if (coverage_above > 6) {cumulus_flag = 0;} # no Cumulus with strong layer above
# always do Cumulus when there's a thunderstorm
@ -1812,66 +1826,90 @@ if (cumulus_flag == 1)
{
if ((cover_low < 4) and (t > 39600) and (t < 68400)) {var strength = 0.4;}
else {var strength = 1.0;}
local_weather.create_cumosys(blat,blon, alt_low+alt_offset,get_n(strength), 20000.0);
local_weather.create_cumosys(blat,blon, alt_low+metar_alt_offset,get_n(strength), 20000.0);
n = n + 1; # do not start parsing with lowest layer
}
else
{var strength = 0.0;}
for (var i = n; i <n_layers; i=i+1)
{
var altitude = layers[i].getNode("alt-agl-ft").getValue();
var cover = layers[i].getNode("cover-oct").getValue();
var altitude = layers[i].getNode("elevation-ft").getValue();
print("altitude: ",altitude);
var cover = 8 - 2 * layers[i].getNode("coverage-type").getValue();
if (cover == 0) {break;} # a zero cover layer indicates we are done
if (cover == -2) {break;} # a clear cover layer indicates we are done
if (altitude < 9000.0) # draw Nimbostratus or Stratus models
{
if (cover == 8)
{create_8_8_nimbus(blat, blon, altitude+alt_offset, alpha);}
if ((altitude < 2000) or (rain_norm > 0.3))
{create_8_8_nimbus(blat, blon, altitude+metar_alt_offset, alpha);}
else
{create_8_8_stratus(blat, blon, altitude+metar_alt_offset, alpha);}
else if ((cover < 8) and (cover > 4))
{create_6_8_stratus(blat, blon, altitude+alt_offset, alpha);}
{
if (cumulus_flag == 1)
{
create_4_8_sstratus_patches(blat, blon, altitude+metar_alt_offset, alpha);
}
else
{
create_6_8_stratus(blat, blon, altitude+metar_alt_offset, alpha);
}
}
else if ((cover == 3) or (cover == 4))
{
var rn = rand();
if (rn > 0.75)
{create_4_8_stratus(blat, blon, altitude+alt_offset, alpha);}
{create_4_8_stratus(blat, blon, altitude+metar_alt_offset, alpha);}
else if (rn > 0.5)
{create_4_8_stratus_patches(blat, blon, altitude+alt_offset, alpha);}
{create_4_8_stratus_patches(blat, blon, altitude+metar_alt_offset, alpha);}
else if (rn > 0.25)
{create_4_8_sstratus_patches(blat, blon, altitude+alt_offset, alpha);}
{create_4_8_sstratus_patches(blat, blon, altitude+metar_alt_offset, alpha);}
else if (rn > 0.0)
{create_4_8_sstratus_undulatus(blat, blon, altitude+alt_offset, alpha);}
{create_4_8_sstratus_undulatus(blat, blon, altitude+metar_alt_offset, alpha);}
}
else
{
var rn = rand();
if (rn > 0.5)
{create_2_8_stratus(blat, blon, altitude+alt_offset, alpha);}
{create_2_8_stratus(blat, blon, altitude+metar_alt_offset, alpha);}
else if (rn > 0.0)
{create_2_8_sstratus(blat, blon, altitude+alt_offset, alpha);}
{create_2_8_sstratus(blat, blon, altitude+metar_alt_offset, alpha);}
}
} # end if altitude
else if ((altitude > 9000.0) and (altitude < 20000.0)) # select thin cloud layers
{
if (cover == 8)
{create_8_8_cirrostratus(blat, blon, altitude+alt_offset, alpha);}
{create_8_8_cirrostratus(blat, blon, altitude+metar_alt_offset, alpha);}
else if (cover > 2)
{
rn = rand();
if (rn > 0.5)
{create_4_8_tstratus_patches(blat, blon, altitude+alt_offset, alpha);}
{create_4_8_tstratus_patches(blat, blon, altitude+metar_alt_offset, alpha);}
else if (rn > 0.0)
{create_4_8_tstratus_undulatus(blat, blon, altitude+alt_offset, alpha);}
{create_4_8_tstratus_undulatus(blat, blon, altitude+metar_alt_offset, alpha);}
}
else
{create_2_8_tstratus(blat, blon, altitude+alt_offset, alpha);}
{create_2_8_tstratus(blat, blon, altitude+metar_alt_offset, alpha);}
} # end if altitude
else
{
if (cover == 8)
{create_8_8_cirrostratus(blat, blon, altitude+metar_alt_offset, alpha);}
else if (cover > 4)
{create_6_8_cirrostratus(blat, blon, altitude+metar_alt_offset, alpha);}
else if (cover > 2)
{create_4_8_cirrostratus_patches(blat, blon, altitude+metar_alt_offset, alpha);}
else
{create_2_8_cirrostratus(blat, blon, altitude+metar_alt_offset, alpha);}
}
} # end for
# now that the weather info is used, set the flag so that offline mode starts unless a new METAR
# is available for the next tile
setprop(lw~"METAR/available-flag",0);
# store convective altitude and strength
@ -1908,6 +1946,12 @@ local_weather.create_streak("Stratus",lat, lon, alt,500.0,20,0.0,0.2,20000.0,20,
}
var create_6_8_cirrostratus = func (lat, lon, alt, alpha) {
local_weather.create_streak("Cirrostratus",lat,lon,alt,500.0,24,1500.0,0.0,900.0,24,1500.0,0.0,900.0,alpha,1.0);
}
var create_4_8_stratus = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
@ -2193,6 +2237,27 @@ for (var i=0; i<25; i=i+1)
}
var create_1_8_cirrocumulus = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
for (var i = 0; i < 2; i = i + 1)
{
var x = 2.0 * (rand()-0.5) * 10000;
var y = -6000 + i * 12000 + 2.0 * (rand()-0.5) * 1000;
var beta = rand() * 90;
var alt_variation = rand() * 2000;
var path = local_weather.select_cloud_model("Cirrocumulus", "large");
compat_layer.create_cloud(path, lat + get_lat(x,y,phi), lon+get_lon(x,y,phi), alt + alt_variation,alpha+ beta);
}
}
var create_stratocumulus_bank = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;