1
0
Fork 0

Merge branch 'master' of git://gitorious.org/fg/fgdata

This commit is contained in:
Detlef Faber 2011-04-27 20:46:37 +02:00
commit d6fafb5447
9 changed files with 546 additions and 132 deletions

View file

@ -1,9 +1,107 @@
Configuring 3D Clouds
=====================
3D clouds are configured using the file data/cloudlayers.xml.
3D clouds can be created in two ways:
- By placing individual clouds using a command (e.g. from Nasal)
- Using the global weather function, which reads cloud definition from
an XML file.
This file has 3 distinct sections: layers, cloud boxes and clouds,
Placing Clouds Individually
===========================
Clouds are created using the "add-cloud" command, passing a property
node defining the location and characterstics of the cloud.
Location is defined by the following properties:
<layer> - The cloud layer number to add the cloud to. (default 0)
<index> - A unique identifier for the cloud in the layer. If a cloud
already exists with this index, the new cloud will not be
created, and 0 is returned.
<lon-deg> - Longitude to place the cloud, in degrees (default 0)
<lat-deg> - Latitude t place the cloud, in degrees (default 0)
<alt-ft> - Altitude to place the cloud, relative to the layer (!) in ft
(default 0)
<x-offset-m> - Offset in m from the lon-deg. +ve is south (default 0)
<y-offset-m> - Offset in m from the lat-deg. +ve is east (default 0)
The cloud itself is built up of a number of "sprites" - simple 2D textures
that are always rotated to be facing the viewer. These sprites are handled
by a OpenGL Shader - a small program that is run on your graphics card.
The cloud is defined by the following properties:
<min-cloud-width-m> - minimum width of the cloud in meters (default 500)
<max-cloud-width-m> - maximum width of the cloud (default min-cloud-width-m)
<min-cloud-height-m> - minimum height of the cloud (default min-cloud-width-m)
<max-cloud-height-m> - maximum height of the cloud (default max-cloud-width-m)
<texture> - texture file of sprites to use (default cl_cumulus.png)
<num-textures-x> - number of cloud textures defined horizontally in the
texture file (default 4)
<num-textures-y> - number of cloud textures defined vertically in the
texture file (default 4)
<num-sprites> - Number of sprite to generate for the cloud (default 20)
<bottom-shade> - Light multiplier for sprites at the bottom of the cloud
(default 1.0)
<min-sprite-width-m> - minimum width of the sprites used to create the cloud
(default 200)
<max-sprite-width-m> - maximum width of the sprites used to create the cloud
(default min-sprite-width-m)
<min-sprite-height-m> - minimum height of the spites used to create the cloud
(default min-sprite-width-m)
<max-sprite-height-m> - maximum height of the sprites used to create the cloud
(default max-sprite-height-m)
The texture to use for the sprites is defined in the <texture> tag.
To allow some variation, you can create a texture file containing multiple
sprites in a grid, and define the <num-textures-x/y> tags. The code
decides which texture to use for a given sprite : randomly in the x-direction
and based on the altitude of the sprite within the cloud in the y-direction.
Therefore, you should put sprite textures you want to use for the bottom of
your cloud at the bottom of the texture file, and those you want to use for
the top of the cloud at the top of the texture file.
For example, the following Nasal snippet will create a cloud immediately above the
aircraft at an altitude of 1000 ft above /environment/clouds/layer[0]/elevation-ft :
var p = props.Node.new({ "layer" : 0,
"index": 1,
"lat-deg": getprop("/position/latitude-deg"),
"lon-deg": getprop("/position/longitude-deg"),
"alt-ft" : 1000 });
fgcommand("add-cloud", p);
Moving Individual Clouds
========================
Clouds may be moved by using the "move-cloud" command. This takes the following
property arguments.
<layer> - The cloud layer number containing the cloud to move. (default 0)
<index> - The unique identifier of the cloud to move.
<lon-deg> - Longitude to place the cloud, in degrees (default 0)
<lat-deg> - Latitude t place the cloud, in degrees (default 0)
<alt-ft> - Altitude to place the cloud, relative to the layer (!) in ft
(default 0)
TODO: Add offset arguments for consistency with add-cloud.
Deleting Individual Clouds
===========================
Clouds may be deleted by using the "del-cloud" command. This takes the following
property arguments.
<layer> - The cloud layer number containing the cloud to delet. (default 0)
<index> - The unique identifier of the cloud to delete.
Global 3D Clouds
================
The global weather system uses sets of clouds defined in cloudlayers.xml
in your FG_ROOT.
The file has 3 distinct sections: layers, cloud boxes and clouds,
described below.
Notes for those editing clouds:
@ -23,41 +121,9 @@ Notes for those editing clouds:
Clouds
======
The <clouds> section contains definitions of clouds themselves, built
up of a number of "sprites" - simple 2D textures that are always
rotated to be facing the viewer. These sprites are handled by a OpenGL
Shader - a small program that is run on your graphics card.
Sets of these clouds are be built up into bigger masses, described below.
Each cloud group has a completely user-defined name, and contains the
following tags.
<min-cloud-width-m> - minimum width of the cloud
<max-cloud-width-m> - maximum width of the cloud
<min-cloud-height-m> - minimum height of the cloud
<max-cloud-height-m> - maximum height of the cloud
<texture> - texture file of sprites to use
<num-textures-x> - number of cloud textures defined horizontally in the
texture file
<num-textures-y> - number of cloud textures defined vertically in the
texture file
<num-sprites> - Number of sprite to generate for the cloud
<bottom-shade> - Light multiplier for sprites at the bottom of the cloud.
<min-sprite-width-m> - minimum width of the sprites used to create the cloud
<max-sprite-width-m> - maximum width of the sprites used to create the cloud
<min-sprite-height-m> - minimum height of the spites used to create the cloud
<max-sprite-height-m> - maximum height of the sprites used to create the cloud
The texture to use for the sprites is defined in the XML <texture> tag.
To allow some variation, you can create a texture file containing multiple
sprites in a grid, and define the <num-textures-x/y> tags. The code
decides which texture to use for a given sprite : randomly in the x-direction
and based on the altitude of the sprite within the cloud in the y-direction.
Therefore, you should put sprite textures you want to use for the bottom of
your cloud at the bottom of the texture file, and those you want to use for
the top of the cloud at the top of the texture file.
The cloud definitions are as described above for placing individual
clouds, but no position information is used (this is defined in the
cloud box and layers below).
Cloud Boxes
===========
@ -81,7 +147,7 @@ Each <box> section contains the following tags:
<hdist> - The horizontal distribution of the clouds within the area.
Equates to a sum of random distributions. Defaults to 1.
1 = even distribution, 2 = distributed towards the center.
3 = very strongly distributed towards the center.
3 = very strongly distributed towards the center.
<vdist> - The vertical distribution of the clouds. As for hdist.
@ -117,7 +183,7 @@ The layer type is derived from the METAR/Weather settings by FG itself.
Each layer type is a named XML tag, i.e.: ns, sc, st, ac, cb, cu.
If a layer type is not defined, then a 2D layer is used instead.
The layer type contains one or more <cloud> definitions. This
The layer type contains one or more <cloud> definitions. This
defines a type of cloud box, and a weighting for that type (<count>).
For example, the following XML fragment will produce 3 "cb" cloud boxes
@ -134,4 +200,4 @@ for every 1 "cu":
Clouds are randomly distributed across the sky in the x/y plane, but the
height of them is set by the weather conditions, with a random height range
applied, defined by <grid-z-rand>
applied, defined by <grid-z-rand>

View file

@ -1047,6 +1047,93 @@ var HUD = {
},
};
# crossfeed_valve
# =============================================================================
# class that creates a fuel tank cross-feed valve. Designed for YASim aircraft;
# JSBSim aircraft can simply use systems code within the FDM (see 747-400 for
# an example).
#
# WARNING: this class requires the tank properties to be ready, so call new()
# after the FDM is initialized.
#
# SYNOPSIS:
# crossfeed_valve.new(<max_flow_rate>, <property>, <tank>, <tank>, ... );
# crossfeed_valve.open(<update>);
# crossfeed_valve.close(<update>);
#
# <max_flow_rate> ... maximum transfer rate between the tanks in lbs/sec
# <property> ... property path to use as switch - pass nil to use no such switch
# <tank> ... number of a tank to connect - can have unlimited number of tanks connected
# <update> ... update switch property when opening/closing valve via Nasal - 0 or 1; by default, 1
#
#
# EXAMPLES:
# aircraft.crossfeed_valve.new(0.5, "/controls/fuel/x-feed", 0, 1, 2);
#-------
# var xfeed = aircraft.crossfeed_valve.new(1, nil, 0, 1);
# xfeed.open();
#
var crossfeed_valve = {
new: func(flow_rate, path) {
var m = { parents: [crossfeed_valve] };
m.valve_open = 0;
m.interval = 0.5;
m.loopid = -1;
m.flow_rate = flow_rate;
if (path != nil) {
m.switch_node = props.globals.initNode(path, 0, "BOOL");
setlistener(path, func(node) {
if (node.getBoolValue()) m.open(0);
else m.close(0);
}, 1, 0);
}
m.tanks = [];
for (var i = 0; i < size(arg); i += 1) {
var tank = props.globals.getNode("consumables/fuel/tank[" ~ arg[i] ~ "]");
if (tank.getChild("level-lbs") != nil) append(m.tanks, tank);
}
return m;
},
open: func(update_prop = 1) {
if (me.valve_open == 1) return;
if (update_prop and contains(me, "switch_node")) me.switch_node.setBoolValue(1);
me.valve_open = 1;
me.loopid += 1;
settimer(func me._loop_(me.loopid), me.interval);
},
close: func(update_prop = 1) {
if (update_prop and contains(me, "switch_node")) me.switch_node.setBoolValue(0);
me.valve_open = 0;
},
_loop_: func(id) {
if (id != me.loopid) return;
var average_level = 0;
var count = size(me.tanks);
for (var i = 0; i < count; i += 1) {
var level_node = me.tanks[i].getChild("level-lbs");
average_level += level_node.getValue();
}
average_level /= size(me.tanks);
var highest_diff = 0;
for (var i = 0; i < count; i += 1) {
var level = me.tanks[i].getChild("level-lbs").getValue();
var diff = math.abs(average_level - level);
if (diff > highest_diff) highest_diff = diff;
}
for (var i = 0; i < count; i += 1) {
var level_node = me.tanks[i].getChild("level-lbs");
var capacity = me.tanks[i].getChild("capacity-gal_us").getValue() * me.tanks[i].getChild("density-ppg").getValue();
var diff = math.abs(average_level - level_node.getValue());
var min_level = math.max(0, level_node.getValue() - me.flow_rate * diff / highest_diff);
var max_level = math.min(capacity, level_node.getValue() + me.flow_rate * diff / highest_diff);
var level = level_node.getValue() > average_level ? math.max(min_level, average_level) : math.min(max_level, average_level);
level_node.setValue(level);
}
if (me.valve_open) settimer(func me._loop_(id), me.interval);
}
};
# module initialization

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

@ -2,6 +2,31 @@ var abs = func(n) { n < 0 ? -n : n }
var sgn = func(x) { x < 0 ? -1 : x > 0 }
var max = func(x) {
var n = x;
for (var i = 0; i < size(arg); i += 1) {
if (arg[i] > n) n = arg[i];
}
return n;
}
var min = func(x) {
var n = x;
for (var i = 0; i < size(arg); i += 1) {
if (arg[i] < n) n = arg[i];
}
return n;
}
var avg = func {
var x = 0;
for (var i = 0; i < size(arg); i += 1) {
x += arg[i];
}
x /= size(arg);
return x;
}
var pow = func(x, y) { exp(y * ln(x)) }
var mod = func(n, m) {

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;

View file

@ -35,15 +35,34 @@
<group>
<layout>table</layout>
<checkbox>
<name>auto-tower</name>
<row>0</row>
<col>0</col>
<colspan>2</colspan>
<label>Always use nearest tower</label>
<property>/sim/tower/auto-position</property>
<live>true</live>
<binding>
<command>dialog-apply</command>
<object-name>auto-tower</object-name>
</binding>
</checkbox>
<text>
<row>0</row><col>0</col>
<row>1</row><col>0</col>
<halign>right</halign>
<label>Airport ID:</label>
</text>
<input>
<name>airport-id</name>
<row>0</row><col>1</col>
<row>1</row><col>1</col>
<property>/sim/tower/airport-id</property>
<enable>
<not>
<property>/sim/tower/auto-position</property>
</not>
</enable>
</input>
<empty>
<row>0</row><col>3</col>
@ -54,7 +73,7 @@
are not filled in on the COM radio retuning...
I'm working on that part now.
<button>
<row>1</row><col>0</col>
<row>2</row><col>0</col>
<legend>COM1</legend>
<binding>
<command>property-assign</command>
@ -67,22 +86,22 @@
</binding>
</button>
<text>
<row>1</row><col>2</col>
<row>2</row><col>2</col>
<property>/instrumentation/comm[0]/frequencies/selected-mhz</property>
<label>MHz</label>
<format>%-0.2f</format>
</text>
<text>
<row>1</row><col>1</col>
<row>2</row><col>1</col>
<property>/instrumentation/comm[0]/airport-id</property>
</text>
<empty>
<row>1</row><col>3</col>
<row>2</row><col>3</col>
<stretch>true</stretch>
</empty>
<button>
<row>2</row><col>0</col>
<row>3</row><col>0</col>
<legend>COM2</legend>
<binding>
<command>property-assign</command>
@ -95,24 +114,24 @@
</binding>
</button>
<text>
<row>2</row><col>2</col>
<row>3</row><col>2</col>
<property>/instrumentation/comm[1]/frequencies/selected-mhz</property>
<label>MHz</label>
<format>%-0.2f</format>
</text>
<text>
<row>2</row><col>1</col>
<row>3</row><col>1</col>
<property>/instrumentation/comm[1]/airport-id</property>
</text>
<empty>
<row>2</row><col>3</col>
<row>3</row><col>3</col>
<stretch>true</stretch>
</empty>
-->
<button>
<legend>Preset</legend>
<row>3</row><col>0</col>
<row>4</row><col>0</col>
<!--
Is there a way in the GUI (other than via a dynamic
NASAL dialog) to assign text to the widget rather
@ -120,6 +139,9 @@
The way it is now CANCEL doesn't undo PRESET :-(
-->
<enable>
<not><property>sim/tower/auto-position</property></not>
</enable>
<binding>
<command>property-assign</command>
<property>/sim/tower/airport-id</property>
@ -131,11 +153,11 @@
</binding>
</button>
<text>
<row>3</row><col>1</col>
<row>4</row><col>1</col>
<property>/sim/presets/airport-id</property>
</text>
<empty>
<row>3</row><col>3</col>
<row>4</row><col>3</col>
<stretch>true</stretch>
</empty>
</group>

View file

@ -410,10 +410,10 @@ Started September 2000 by David Megginson, david@megginson.com
<roll-deg type="double">0</roll-deg>
<pitch-deg type="double">0</pitch-deg>
<heading-deg type="double">0</heading-deg>
<auto-position type="bool" userarchive="y">true</auto-position>
</tower>
<view-name-popup type="bool"
userarchive="y">true</view-name-popup>
<view-name-popup type="bool" userarchive="y">true</view-name-popup>
<view>
<name>Cockpit View</name>