The aim of a local weather system is to simulate weather phenomena tied to specific locations. Examples for this are a thunderstorm, a rainfront or thermal development. In the case of the thunderstorm, severe rain and turbulence occur in a location a few kilometers in scale, i.e. one can easily view it 'from outside' or fly in and out of this region. Similarly, the development of thermal convection clouds is strongly tied to features of the terrain - thermal development does not occur easily over open water or snow, but it is strong over rock or similar surfaces which heat in the sun. Finally, a rainfront is a phenomenon like a thunderstorm that divides the sky into two regions - one with essentially good visibility and clear sky, the other with severe clouds and rain, and both are visible at the same time.
-This is in contrast to the current (v.2.0.0) weather system of Flightgear where weather changes affect the weather everywhere in the simulated world and are (with few exceptions) not tied to specific locations. In such a system, it is impossible to observe e.g. the approach of a rainfront while flying in sunshine.
+This is in contrast to the current (v.2.0.0) global weather system of Flightgear where weather changes affect the weather everywhere in the simulated world and are (with few exceptions) not tied to specific locations. In such a system, it is impossible to observe e.g. the approach of a rainfront while flying in sunshine.
-The local weather package ultimately aims to provide the functionality to simulate such local phenomena. In version 0.9, the package supplies various cloud placement algorithms, as well as local control over most major weather parameters (wind, visibility, pressure, temperature, rain, snow, thermal lift, turbulence...) through interpolation routines and effect volumes. The dynamics of the different systems is tied together - clouds and weather effects drift in the specified wind field. The package also contains a fairly detailed algorithm to generate convective clouds and thermals with a realistic distribution. In addition, there is a simulation of realistic interaction of the convective cloud system with the terrain as a function of time. Clouds drifting in the wind flow over obstacles, i.e. they change their altitude dynamically. Convection is implemented with a life cycle model of clouds - they are generated, evolve for a given lifetime dependent on the underlying terrain and decay at the end of their life cycle. Thermals associated with the clouds follow the same pattern. In particular, in the presence of wind favourable spots for convection generate 'alleys' of dense cloud cover downwind, or thermals and clouds generated over land decay rapidly once they reach open water.
+The local weather package aims to provide the functionality to simulate such local phenomena. In version 1.0, the package supplies various cloud placement algorithms, as well as local control over most major weather parameters (wind, visibility, pressure, temperature, rain, snow, thermal lift, turbulence...) through interpolation routines and effect volumes. The dynamics of the different systems is tied together - for instance clouds and weather effects drift in the specified wind field. The package also contains a fairly detailed algorithm to generate convective clouds and thermals with a realistic distribution over the various terrain types. There is a simulation of the interaction of the convective cloud system with the terrain as a function of time. Clouds drifting in the wind flow over obstacles, i.e. they change their altitude dynamically. Convection is implemented with a life cycle model of Cumulus clouds - they are generated, evolve for a given lifetime dependent on the underlying terrain and decay at the end of their life cycle. Thermals associated with the clouds follow the same pattern. In particular, in the presence of wind favourable spots for convection generate 'alleys' of dense cloud cover downwind, or thermals and clouds generated over land decay rapidly once they reach open water.
-For long-range flights, the system automatically provides transitions between different weather patterns like fronts and low and high pressure areas. However, basically all features currently present can and will eventually be improved.
+For long-range flights, the system provides an offline weather system with plausible transitions between different large-scale weather patterns like fronts and low and high pressure areas, as well as the optional use of live METAR data.
2. Installation
-The package needs to be unpacked in the Flightgear root directory. It writes content into the Nasal/, gui/, gui/dialogs/, Shaders, Effects/, Docs/, and Models/Weather/ subdirectories. The installation does not overwrite any of the default Flightgear files, but to be accessible from the menu, one must copy gui/menubar.xml.alt to the default menubar.xml or copy the last two lines of the environemnt menu calling local_weather and local_weather_tiles into the default file.
+The package needs to be unpacked in the Flightgear root directory. It writes content into the Nasal/, gui/, gui/dialogs/, Shaders, Effects/, Docs/, and Models/Weather/ subdirectories. The installation does not overwrite any of the default Flightgear files, but to be accessible from the menu for Flightgear 2.0.0, one must copy gui/menubar.xml.alt to the default menubar.xml or copy the three lines of the environemnt menu calling local_weather, local_weather_tiles and local_weather_config into the default file. More recent versions of Flightgear already provide the necessary menu items.
-This adds the items Local Weather, Local Weather Tiles and Local Weather Settings to the Environment menu when Flightgear is up. Most of the basic functionality is contained in local_weather.nas which is loaded at startup and identifies itself with a message.
+This adds the items Local Weather, Local Weather Tiles and Local Weather Config to the Environment menu when Flightgear is up. Most of the basic functionality is contained in local_weather.nas which is loaded at startup and identifies itself with a message.
-Unless asked to do so from the menu, local weather does not run any process in the background. Upon loading, the package does not set any properties already existing, but only generates properties necessary for the menu entries in its own subdirectory /local-weather/ in the tree. The package also does a features check on startup if particular functions are available in hard-coded form. If the features are not present, the package will still function properly using slower Nasal fallbacks.
+Unless asked to do so from the menu, local weather does not run any process in the background. Upon loading, the package does not set any properties already existing, but only generates properties necessary for the menu entries in its own subdirectory /local-weather/ in the tree. The package also does a features check on startup if particular functions are available in hard-coded form. If the features are not present, the package will largely still function properly using slower Nasal fallback code.
3. Functionality
-The general rule is that the gui is not hardened against problematic user input, for example it will not reject meaningless input like negative windspeeds or unphysical windshear. It is recommended to watch the console, because some level of warnings and errors are passed to the console if the log options is on. Crucial warnings are also printed on-screen.
+The general rule is that the gui is not hardened against problematic user input, for example it will not reject meaningless input like negative windspeeds or unphysical windshear. It is recommended to watch the console, because some level of warnings and errors are passed to the console if the log option is on. Crucial warnings are also printed on-screen.
-Placement calls may sometimes take a significant time to execute especially for large numbers of clouds tied in a complicated way to the terrain. Placing 500 barrier clouds against a small barrier may take a minute to compute. During this time, a reduced framerate is to be expected
+Cloud placement calls may sometimes take a significant time to execute especially for large numbers of clouds tied in a complicated way to the terrain. Placing 500 barrier clouds against a small barrier may take a minute to compute. During this time, a reduced framerate is to be expected
The first menu Local Weather contains the low level cloud placement functions. Its purpose is mainly for developing cloud patterns without having to resort to re-type the underlying Nasal code every time. Currently five options are supported: Place a single cloud, Place a cloud streak, Start the convective system, Create barrier clouds , Place a cloud layer and Make a cloudbox.
@@ -43,7 +43,7 @@ The first menu Local Weather contains the low level cloud placement funct
Single cloud placement
-Single cloud placement is straightforward, it places a (randomly chosen) cloud model of specified type and subtype to a set location. The last parameter, direction of placement, is only relevant for static cloud models (Cirrus) as it sets their orientation, it is overwritten for any rotated cloud models (Altocumulus, Cumulus, Cumulonimbus).
+Single cloud placement is straightforward, it places a (randomly chosen) cloud model of specified type and subtype to a set location. The last parameter, direction of placement, is only relevant for static cloud models (Cirrus, Cirrocumulus) as it sets their orientation, it is overwritten for any rotated cloud models (Altocumulus, Cumulus, Cumulonimbus, Stratus, Nimbostratus).
Streak placement
@@ -55,29 +55,29 @@ The pattern can then be randomized in x, y and altitude. Basically, specifying n
The convective system
-The convective system places Cumulus clouds centered on the current position based on the underlying terrain. Currently it models daily variation of convective strength and the latitude variation based on a simple sinusoidal model (i.e. it produces different results when called in the morning than at noon), but it does not take into account seasonal variation (i.e. it assumes the date to be the equinox). This will be significantly improved in the future. The actual placement is chosen based on the type of the underlying terrain, with Cumulus development more likely over city areas than on water. Details of the algorithm are described in the appendix. The parameters for this need fine-tuning and are currently rather rough, but they lead for example to pronounced differences between land and sea in coastal regions. The following picture shows the result of a call of the system in the afternoon over TNCM.
+The convective system places Cumulus clouds centered on the current position based on the underlying terrain. Currently it models daily variation of convective strength and the latitude variation based on a simple sinusoidal model (i.e. it produces different results when called in the morning than at noon), but it does not take into account seasonal variation (i.e. it assumes the date to be the equinox). The actual placement is chosen based on the type of the underlying terrain, with Cumulus development more likely over city areas than on water. Details of the algorithm are described in the appendix. The parameters for this need some fine-tuning and are currently rather rough, but they lead for example to pronounced differences between land and sea in coastal regions. The following picture shows the result of a call of the system in the afternoon over TNCM.
-Unless 'Terrain presampling' is active, clouds are placed in a constant altitude alt in a tile with given size where the size measures the distance to the tile border, i.e. a size parameter of 15 km corresponds to a 30x30 km region. When 'Terrain presampling' is selected, the distribution of clouds in altitude is determined by a more complicated algorithm described in the appendix. Clouds are placed with constant density for given terrain type, so be careful with large area placements! strength is an overall multiplicative factor to fine-tune.
+Unless 'Terrain presampling' is active, clouds are placed in a constant altitude alt in a tile with given size where the size measures the distance to the tile border, i.e. a size parameter of 15 km corresponds to a 30x30 km region. When 'Terrain presampling' is selected, the distribution of clouds in altitude is determined by a more complicated algorithm described in the appendix. Clouds are placed with constant density for given terrain type, so be careful with large area placements! strength is an overall multiplicative factor to fine-tune the amount of cloud generation.
The barrier cloud system
-The barrier cloud system places a cloud at a random point within the region centered around the current position given by size with some probability if there is a terrain barrier downwind with the elevation alt within a distance dist or less. Cloud placement probability is larger for small distances to the barrier. The system tries to place number clouds and assumes that the wind comes from direction wind. If clouds cannot be placed (because there is no barrier within the specified altitude) the algorithm exits with a warning. The picture illustrates the result for the mountains above Geneva.
+The barrier cloud system places a cloud at a random point within the region centered around the current position given by size with some probability if there is a terrain barrier downwind with the elevation alt within a distance dist or less. Cloud placement probability is larger for small distances to the barrier. The system tries to place number clouds and assumes that the wind comes from direction wind. If clouds cannot be placed (because there is no barrier within the specified altitude) the algorithm exits with a warning. The picture illustrates the result for the mountains above Las Vegas.
-Currently, the algorithm does not check for a barrier upstream - this will change in future versions. The ufo is a good way to explore the results of the algorithm by simply flying to a suitable location and calling it there for a relatively small region.
+Currently, the algorithm does not check for a barrier upstream - this may change in future versions. The ufo is a good way to explore the results of the algorithm by simply flying to a suitable location and calling it there for a relatively small region. Due to its large performance use, the barrier cloud system is currently not part of the large-scale weather generating system.
The layer cloud system
The layer cloud placement is not drastically different from the streak placement in terms of what one can do with it - just its application philosophy is different. It randomly places cloudlets into an ellipsoid region given by the radii rad x and rad y which is rotated by dir, beginning at altitude alt up to thickness thick with a density controlled by density. The parameter edge specifies a boundary region in which smaller clouds are placed less densely.
-If rainflag is set to 1, the system will also place external precipitation models (i.e. visible rain layers), roughly at the transition between edge and core of the cloud placement region with a density given by rain dens.. The system will however not place an effect volume which would lead to the actual simulation of rain below the layer - this must be done separately by the user.
+If rainflag is set to 1, the system will also place external precipitation models (i.e. visible rain layers), roughly at the transition between edge and core of the cloud placement region with a density given by rain dens. The system will however not place an effect volume which would lead to the actual simulation of rain below the layer - this must be done separately by the user.
The picture illustrates the result of a layer generation call for Nimbostratus clouds with precipitation models.
@@ -92,36 +92,38 @@ The cloudbox placement is an experimental routine allowing to define a cloud cor
Tile placement
-The second menu is used to place complete weather tiles based on low-level calls. It is intended for the user to automatically create the various weather patterns during flight.
+The second menu is used to place complete weather patterns based on low-level calls. It is intended for the user to automatically create the various weather development during flight. Unless stated otherwise, all parameters in this menu are parsed at startup time of local weather only (i.e. when the user selects the OK button, but not while the system runs.
-Weather is created in a series of 40x40 km squares, called tiles. Tiles are classified by airmass, such that the sequence of tiles can describe for example the transition from a high pressure area to a low pressure area. The dropdown menu is used to select the type of weather tile to build initially.
+Weather is created in a series of 40x40 km squares, called tiles. Tiles are classified by airmass, such that the sequence of tiles can describe for example the transition from a high pressure area to a low pressure area. The dropdown menu is used to select the type of weather tile to build initially and to determine the rules according to which subsequent tiles are generated.
-Below are entries for three parameters. The first two are the simplified version of wind direction and speed for the user who is not interested in specifying many different wind interpolation points.
-The third parameter, the altitude offset, is to manually adjust the altitude level of clouds in the absence of terrain presampling. Cloud layer placement calls are then specified for absolute altitudes and calibrated at sea level. As a result, layers are placed too low in mountainous terrain, hence the need for an offset.
+Below are entries for three parameters. The first two are the simplified version of wind direction and speed for the user who is not interested in specifying many different wind interpolation points or an altitude structure.
+The third parameter, the altitude offset, is to manually adjust the altitude level of clouds in the absence of terrain presampling. Cloud layer placement calls are then specified for absolute altitudes and calibrated at sea level. As a result, layers are placed too low in mountainous terrain, hence the need for an offset. If aloft interpolated or aloft waypoints are chosen as wind models or if tile selection mode is set to METAR, the first two fields are not parsed, if the option terrain presampling is selected the offset is not used.
+
+The three sliders below are used to control gusty winds. They are parsed at runtime and changes do not require a restart of local weather to be effective. The first slider controls how frequent gusts are, the second slider determines the strength relative to the base wind and the third slider their variation in direction. Gusts are, regardless of the wind model, only created in the atmospheric boundary layer close to the terrain surface. The rules for the gust model are described in the appendix in more detail. If the tile selection mode is set to METAR, user-specified values may be overwritten.
The dropdown menu for the wind contains various models for how the windfield is specified which require a different amount of user-specified input. The options are described further down when the windfield modelling is described in more detail.
-The dropdown menu for the tile selection mode controls the long-range behaviour of weather. It specifies according to what rules tiles are automatically generated once the aircraft reaches the border of the original tile. The option 'single tile' creates a single weather tile as specified without automatic generation of further tiles. The option 'repeat tile' creates new tiles of the same type as the originally selected tile. This does not mean that weather will be unchanged during flight, as both parameters like pressure, temperature and visibility as well as the positioning of cloud banks are randomized to some degree. In addition, each tile typically contains 2-5 different cloud scenarios, so five repeated generations of 'low-pressure-border' tiles may never result in the same arrangement of cloud layers. Necertheless, the option will keep weather conditions roughly the same. This is different with the (somewhat optimistically named) 'realistic weather'. This option allows transitions between different airmasses, thus one may select 'low-pressure-core' initially, but as the flight goes on, eventually a region of high pressure and clear skies may be reached. Moreover, it does not cover transitions to arctic or tropical weather conditions - those will be covered in a future release. 'repeat tile' does not work for any tile which is part of a front.
+The dropdown menu for the tile selection mode controls the long-range behaviour of weather. It specifies according to what rules tiles are automatically generated once the aircraft reaches the border of the original tile. The option single tile creates a single weather tile as specified without automatic generation of further tiles. The option repeat tile creates new tiles of the same type as the originally selected tile. This does not mean that weather will be unchanged during flight, as both parameters like pressure, temperature and visibility as well as the positioning of cloud banks are randomized to some degree. In addition, each tile typically contains typically 6-8 different cloud scenarios, so five repeated generations of low-pressure-border tiles may never result in the same arrangement of cloud layers. Nevertheless, the option will keep weather conditions roughly the same. This is different with the (somewhat optimistically named) realistic weather. This option allows transitions between different airmasses, thus one may select low-pressure-core initially, but as the flight goes on, eventually a region of high pressure and clear skies may be reached. Currently, it does not cover transitions to arctic or tropical weather conditions - those will be covered in a future release. Note also that repeat tile does not work for any tile which is part of a weather front.
-The final option, 'METAR', generates weather according to parsed METAR information. This information must be made available in the property tree. Currently this is not done automatically and the METAR system does not work with real-weather-fetch, this needs some work on the Flightgear core. Future versions will be able to use parsed METAR to generate weather tiles.
+The final option, METAR, generates weather according to parsed METAR information. This means that user-specified information for tile type and winds is overwritten by live data. The METAR mode is described in more detail below.
-Below the menu are six tickboxes. 'Terrain presampling' finds the distribution of altitude in the terrain before placing a cloud layer. As a result, the layers or clouds are automatically placed at the correct altitude above ground in level terrain. In mountain regions, cloud placement is fairly tricky, and the algorithm analyzes quantities like the median altitude to determine what to do. The appendix contains a detailed description of the algorithm. If the box is ticked, the altitude offset specified above is not parsed.
+Below the menu are six tickboxes. Terrain presampling finds the distribution of altitude in the terrain before placing a cloud layer. As a result, the layers or clouds are automatically placed at the correct altitude above ground in level terrain. In mountain regions, cloud placement is fairly tricky, and the algorithm analyzes quantities like the median altitude to determine what to do. The appendix contains a detailed description of the algorithm. If the box is ticked, the altitude offset specified above is not parsed.
-'generate thermals' is an option intended primarily for soaring. It determines if thermals will be placed whenever a convective clouds is generated. Since managing a large number of thermals costs some amount of resources, it is recommended to generate thermals only if they are needed, i.e. definitely for soaring, possibly for added realism in small aircraft.
+generate thermals is an option intended primarily for soaring. It determines if thermals will be placed whenever a convective clouds is generated. Since managing a large number of thermals costs some amount of resources, it is recommended to generate thermals only if they are needed, i.e. definitely for soaring, possibly for added realism in small aircraft.
-'debug output' determines if the system writes status messages to the console. Unselecting the option suppresses normal status messages (warnings and errors will still be written). However, in many cases the log of status messages is needed to trace bugs, so if you switch it off and experience a problem, it is likely that the problem cannot be traced.
+debug output determines if the system writes status messages to the console. Unselecting the option suppresses normal status messages (warnings and errors will still be written). However, in many cases the log of status messages is needed to trace bugs, so if you switch it off and experience a problem, it is likely that the problem cannot be traced.
-'detailed clouds' will change the technique for generating Cumulus clouds from a multilayer model to multiple cloudlets filling a box. This improves the visual appearance of the clouds significantly, albeit at the expense of some loss of framerate. Rendering multiple tiles of dense Cumulus development with detailed clouds will quite possibly slow down even a powerful system.
+detailed clouds will change the technique for generating Cumulus clouds from a multilayer model to multiple cloudlets filling a box. This improves the visual appearance of the clouds significantly, albeit at the expense of some loss of framerate. Rendering multiple tiles of dense Cumulus development with detailed clouds will quite possibly slow down even a powerful system.
-The option 'dynamical weather' ties all clouds and weather effects to the windfield. If that option is not chosen, the wind is still generated according to the chosen model, but only felt by the aircraft. This makes e.g. soaring unrealistic, as the aircraft continuously drifts out of a static thermal below a static cap cloud. When 'dynamical weather' is selected, aircraft, cloud and thermal are all displaced by the wind and follow elevation changes to some degree.
+The option dynamical weather ties all clouds and weather effects to the windfield. If that option is not chosen, the wind is still generated according to the chosen model, but only felt by the aircraft. This makes e.g. soaring unrealistic, as the aircraft continuously drifts out of a static thermal below a static cap cloud. When dynamical weather is selected, aircraft, cloud and thermal are all displaced by the wind and follow elevation changes to some degree.
-The final option 'dynamical convection' requires both 'terrain presamling' and 'dynamical weather' to be on (if not, a warning is given and the system aborts). If this option is chosen, all convective clouds and thermals have a life cycle - clouds are continually spawned and decay after a while. This preserves realistic cloud configurations over islands even with wind drift on and improves the realism of the soaring experience as the thermals change over time, but again uses somewhat more performance - switch it on if you need it, for fast planes the visual gain is almost non-existent.
+The final option dynamical convection requires both terrain presamling and dynamical weather to be selected (if not, a warning is given and the system aborts). If this option is chosen, all convective clouds and thermals have a life cycle - clouds are continually spawned and decay after a while. This preserves realistic cloud configurations over islands even with wind drift on and improves the realism of the soaring experience as the properties of thermals change over time, but again uses somewhat more performance - switch it on if you need it, for fast planes the visual gain is almost non-existent.
-The slider 'Thermal properties' is mainly relevant for soaring scenarios. It governs the rato of maximum lift to radius of a thermal. A setting close to 'low convection' creates large thermals with relatively small lift and virtually no turbulence, a setting close to 'rough day' creates very narrow, turbulent thermals with large lift. However, it also affects the Cumulus textures to be used. 'low convection' creates well-formed, smooth Cumuli whereas 'rough day' biases the texture selection towards more rugged and diffuse clouds.
+The slider Thermal properties is mainly relevant for soaring scenarios. It governs the rato of maximum lift to radius of a thermal. A setting close to low convection creates large thermals with relatively small lift and virtually no turbulence, a setting close to rough day creates very narrow, turbulent thermals with large lift. However, it also affects the Cumulus textures to be used. low convection creates well-formed, smooth Cumuli whereas rough day biases the texture selection towards more rugged and diffuse clouds.
The difference is apparent from the following pictures: Smooth and well-formed clouds characteristic of a calm day:
@@ -135,17 +137,16 @@ Rough clouds characteristic of windshear and more turbulent conditions:
-As for the buttons, 'Ok' starts the local weather system with the selected options (note that all options in this menu are startup-time options, they are read once and changing them without restarting the system will not affect the behaviour of the system). 'Clear/End' clears all clouds and ends all local weather functionality - the button brings the system back into the state before it was started. No loops or other subroutines are executed after the button is pressed. 'Close' closes the dialog without starting the system.
+As for the buttons, OK starts the local weather system with the selected options (note that almost all options in this menu are startup-time options, they are read once and changing them without restarting the system will not affect the behaviour of the system). Clear/End clears all clouds and ends all local weather functionality - the button brings the system back into the state before it was started. No loops or other subroutines are executed after the button is pressed. Close closes the dialog without starting the system.
-The button 'Show winds' brings up the detailed wind menu which is needed for the wind models 'aloft interpolated' and 'aloft waypoints':
+The button Show winds brings up the detailed wind menu which is needed for the wind models aloft interpolated and aloft waypoints when not in METAR mode:
-For 'aloft interpolated', the menu is used by inserting wind direction and speed for all given altitudes. After 'Okay', the specified values are used. For 'aloft waypoints', the same info must be supplied for a series of waypoints. First, the latitude and longitude has to be inserted, afterwards the aloft winds for that point below. The button 'Set waypoint' commits the windfield as specified in the menu for this position into memory. For orientation, the number of points inserted is counted on the lower right. 'Clear Waypoints' removes all information entered so far. Note that 'Okay' does not commit the data for a waypoint.
+For aloft interpolated, the menu is used by inserting wind direction and speed for all given altitudes. After OK, the specified values are used. For aloft waypoints, the same info must be supplied for a series of waypoints. First, the latitude and longitude has to be inserted, afterwards the aloft winds for that point below. The button Set waypoint commits the windfield as specified in the menu for this position into memory. For orientation, the number of points inserted is counted on the lower right. Clear Waypoints removes all information entered so far. Note that OK does not commit the data for a waypoint. Entering a windfield in this way by hand is rather cumbersome, but may be useful occasionally - the main purpose of the wind model however is to work with live weather data.
-In principle, the waypoint information inserted so far can be seen using the property browser. It is stored under /local-weather/interpolation/wind[n]/.
@@ -184,7 +185,7 @@ All performance setting menu-options work at runtime, but are processed over tim
4. Cloud models
-The package contains a number of different cloud models, both static ones for Cirrus and Cirrocumulus clouds as well as rotated ones for Altocumulus, Cirrostratus, Cumulus, Cumulonimbus, Stratus and Nimbostratus cloudlet models. Neither the cloud textures, nor the models nor the transformations are perfected, and any aspect can be improved.
+The package contains a number of different cloud models, both static ones for Cirrus and Cirrocumulus clouds as well as rotated ones for Altocumulus, Cirrostratus, Cumulus, Cumulonimbus, Stratus and Nimbostratus cloudlet models. Neither the cloud textures, nor the models nor the transformations are perfected, and any aspect can be improved, albeit at the cost of performance consumption.
Static clouds project textures onto curved sheets into the sky. The advantage of the technique is that cloud layers consisting of thousands of cloudlets with different sizes can be modelled. However, the sheets do not look equally well from all perspectives and somewhat unrealistic from close up.
@@ -215,17 +216,17 @@ Currently all clouds which need to be rotated are treated in the shaders using a
5. Local weather parameters
-The local weather package provides three different ways to control the position dependence of weather parameters: 1) by interpolation between stations 2) by constant inside an effect volume and 3) by function inside an effect volume. This in principle allows to represent any 3-dim distribution of a parameter in a tile volume - but the user-input required to actually do that kind of micromanagement is substantial. However, the system is designed to run with good results and minimum user input.
+The local weather package provides three different ways to control the position dependence of weather parameters: 1) by interpolation between stations 2) by constant inside an effect volume and 3) by function inside an effect volume. This in principle allows to represent any 3-dim distribution of a parameter in a tile volume - but the user-input required to actually do that kind of micromanagement is substantial. However, the system is designed to run with good results and minimal user input.
The idea is that the interpolation system takes care of slow, large-distance scale changes of weather whereas the effect volume system models rapid, small-scale changes. Thus, the gradual drop in visibility when flying into a more humid air mass is dealt with by the interpolation system whereas the sudden drop in visibility when entering a cloud is modeled as an effect volume. It doesn't make sense to have both systems available for all weather parameters, for example pressure can't change suddenly and discontinuously. Consequently the effect volume system does not support pressure.
Determining weather parameters by interpolation
-The interpolation system determines the weather parameters outside of special regions, i.e. possibly in the larger part of the tile volume. The weather is specified for an arbitrary number of stations. Each station is characterized by its position (latitude and longitude) and a set of surface weather observations (visibility, pressure, temperature, dew point, rain, snow). In Flight, the interpolation system computes the distance to each station every 3 seconds, computes an average of the parameters weighted by the inverse distance to the station and sets this average as the current weather. This procedure makes the weather exactly as specified whenever the plane is above a station and changes smoothly as the plane is between stations. It may not yield the desired results when the plane is outside a grid of stations.
+The interpolation system determines the weather parameters outside of special regions, i.e. possibly in the larger part of the tile volume. The weather is specified for an arbitrary number of stations. Each station is characterized by its position (latitude and longitude) and a set of surface weather observations (visibility, pressure, temperature, dew point, rain, snow). In flight, the interpolation system computes the distance to each station regularly, computes an average of the parameters weighted by the inverse distance to the station and sets this average as the current weather. This procedure makes the weather exactly as specified whenever the plane is above a station and changes smoothly as the plane is between stations. It may not yield the desired results when the plane is outside a grid of stations.
While the station concept is designed to support easy connection with weather updates from real METAR stations, stations do not need to be real stations, and each weather tile creation call by default writes its own station at the center of the tile, so weather tiles can be different and the weather will change smoothly between them.
-Technically, the structure of the interpolation system means that while it is running, neither setting weather parameters in the standard menu nor changing visibility using the z-key will have an effect - any setting made there will be overwritten periodically. Only changing the relevant properties (see below) will have the desired effect.
+Technically, the structure of the interpolation system means that while it is running, neither setting weather parameters in the GUI menu nor changing visibility using the z-key will have an effect - any setting made there will be overwritten by the interpolation loop periodically, and local weather needs to be stopped before such changes have an effect.
Weather parameters in effect volumes
@@ -243,9 +244,11 @@ Effect volumes are always specified between a minimum and a maximum altitude, an
create_effect_volume(geometry, lat, lon, r1, r2, phi, alt_low, alt_high, vis, rain, snow, turb, lift, lift_flag, sat);
- where geometry is a flag (1: circular, 2: elliptical and 3: rectangular), lat and lon are the latitude and longitude, r1 and r2 are the two size parameters for the elliptic or rectangular shape (for the circular shape, only the first is used), phi is the rotation angle of the shape (not used for circular shape), alt_low and alt_high are the altitude boundaries, vis, rain, snow, turb and lift are weather parameters which are either set to the value they should assume, or to -1 if they are not to be used, or to -2 if a function instead of a parameter is to be used and -3 if a function for wave lift is used. Since thermal lift can be set to negative values in a sink, a separate flag is provided in this case. sat finally determines the light saturation - it can be used to dim the light beneath cloud layers (which is not done automatically as objects don't cast shades in Flightgear, and given that most cloud models are rotated, their shade would look rather odd on any case).
+ where geometry is a flag (1: circular, 2: elliptical and 3: rectangular), lat and lon are the latitude and longitude, r1 and r2 are the two size parameters for the elliptic or rectangular shape (for the circular shape, only the first is used), phi is the rotation angle of the shape (not used for circular shape), alt_low and alt_high are the altitude boundaries, vis, rain, snow, turb and lift are weather parameters which are either set to the value they should assume, or to -1 if they are not to be used, or to -2 if a function instead of a parameter is to be used and -3 if a function for wave lift is used. Since thermal lift can be set to negative values in a sink, a separate flag is provided in this case. sat finally determines the light saturation, a parameter between 0 (dark) and 1 (normal light) - it can be used to dim the light beneath cloud layers (which is not done automatically as objects don't cast shades in Flightgear, and given that most cloud models are rotated, their shade would look rather odd on any case).
-In version 0.9, thermal lift and wave lift are implemented by function (wave lift is not yet automatically placed, but can be easily from Nasal). There is no easy way to implement any weather parameter by function in an effect volume, as this requires some amount of Nasal coding.
+In version 1.0, thermal lift and wave lift are implemented by function (wave lift is not yet automatically placed, but can be easily from Nasal). There is no easy way to implement any weather parameter by function in an effect volume, as this requires some amount of Nasal coding.
+
+Both thermal lift and saturation require a more recent version of Flightgear than 2.0.0 in order to take effect.
6. Wind models and dynamical weather
@@ -257,7 +260,7 @@ In the horizontal windfield, aloft and bounday layers need to be distinguished.
When the option 'dynamical weather' is active, clouds and effect volumes move with the wind. Due to performance reasons, only clouds in the field of view are processed in each frame. As an efficient way to do this, a quadtree structure is used. However, this has the side effect that all clouds inside a tile need to be moved with the same windspeed (otherwise they would over time drift out of the position where the quadtree expects to find them). Since thermals and their cap clouds should not drift apart, also weather effects are moved with the same windspeed inside each tile. In the following, this is referred to as 'tile wind speed'. The tile wind speed always corresponds to the lowest aloft layer windspeed. The reason why this is considered acceptable is that at the same altitude for different positions inside a tile, the correct windspeed is at most a few kt different from the tile windspeed, and this is impossible to see visually. At high altitudes, the true wind is very different from the speed at which clouds are moved, but without reference and from fast-moving planes, the error is again very hard to see. However, with e.g. a hot air balloon, the fact that at high altitudes clouds are not moved with the high-altitude windspeed would be quite apparent.
-Dependent on how detailed the wind field should be specified, what the pilot aims to do and how much user control is desired, there are several options to model the wind.
+Dependent on how detailed the wind field should be specified, what the pilot aims to do and how much user control is desired, there are several options to model the wind outside the boundary layer:
constant sets the aloft wind to the same speed and direction as specified in the weather tile menu everywhere - at every spatial position and at every altitude. This is for the casual pilot who just wants some simple wind setting, or when it is mandatory that clouds, plane and weather effects all move with the same speed, such as for lighter-than-air aviation. Note that the wind set in the menu is not the wind seen on the runway, as the option sets the aloft wind, from which the boundary layer wind is computed using terrain information.
@@ -269,10 +272,38 @@ Dependent on how detailed the wind field should be specified, what the pilot aim
In all cases, the boundary layer is computed separately. Since the boundary layer effect depends on terrain, there is no direct way to set the wind as experienced on the runway (but of course changing the lowest aloft layer will lead to the desired result).
+
7. The METAR mode
+The METAR mode allows to use live weather data together with the local weather package, which means that all user-defined weather parameters in the menu will not be parsed if this mode is selected. Some weather parameters (pressure, temperature, dewpoint) are straightforwardly passed on to the interpolation subsystem. For other parameters, the problem is more tricky.
+METAR information usually specifies cloud cover and altitude only, but does not mention the type of cloud to be placed. The purpose of the METAR heuristics subroutine is to decide what cloud patterns to place based on the available information. This routine for instance checks if there is an overcast layer above the lowest layer (which makes Cumulus development unlikely) or if the cloud cover is too strong for convective clouds given the time of the day. Once the heuristics has some idea of what the weather pattern indicated in the METAR string looks like, it starts to select cloud types to place into the individual layers. The heuristics is usually good, but certainly not perfect, it may occasionally go wrong and create implausible cloud configurations.
-
7. Property tree structure
+The behaviour of winds depends on the selected wind model. In all cases, the wind in a METAR string is assumed to be the wind on the runway, i.e. the lowest aloft wind will be increased relative to the METAR info because the wind on the runway is subject to the boundary layer effect.
+
+
+
constant uses the winds of the first METAR everywhere at all times
+
constant in tile uses the winds of the first METAR in all tiles until the next METAR becomes available, then uses the new value for all tiles till yet another report becomes available. To maintain the internal coordinate system, intermediate tiles may be generated which do not immediately show the new wind
+
aloft interpolated is not supported by the METAR mode - this makes no sense, as METAR does not report aloft winds
+
aloft waypoints uses a plausible guess for the aloft winds based on the surface winds. In general, higher aloft winds become stronger and turn somewhat dependent on the Coriolis effect. Based on the interpolation points generated in this way, the windfield at each tile center determines the tile wind direction for the visual cloud movement.
+
+
+The METAR mode currently does not support all types of reported weather. The weather is not changed when the same station makes a different report, only if a new station comes in range. Note that the METAR mode cannot be used for Flightgear 2.0.0!
+
+
8. Program and property tree structure
+
+The program runs in a number of loops which operate at different timescales - some tasks require attention in every frame, others may wait for a number of seconds. The main loops are:
+
+
+
Effect volume loop: Determines if the plane is inside an effect volume and executes its action
+
Interpolation loop: Interpolates the weather parameters in space, takes care of the altitude model of the visibility and of the wind boundary layer effects (see Appendix for details)
+
Tile management loop: Controls the generation of new tiles and the removal of old ones.
+
Buffer loop and housekeeping loop: Move clouds out of the buffer into the scenery and back.
+
Quadtree loop: Determines which clouds are inside the field of view and moves them with the tile windspeed.
+
Dynamics loop: Moves effect volumes, tile centers and interpolation points with the windfield, ages convective clouds in their lifecycle.
+
Convective loop: Periodically re-creates convective clouds to compensate for the decaying clouds.
+
+
+All other subroutines are called by one of these loops.
The internal state of the local weather system is found in the property tree under local-weather/. In this directory, various loop flags are found. They indicate the state of the main monitoring loops - they are set to 1 if a loop is running, setting them to zero terminates the loop.
@@ -284,9 +315,9 @@ The local-weather/effect-volumes/ subfolder contains the management of th
local-weather/tiles stores the information of the 9 managed weather tiles (the one the airplane is currently in, and the 8 surrounding it). By default each directory contains the tile center coordinates and a flag if it has been generated. Tiles are not generated unless a minimum distance to the tile center has been reached. Once this happens, the tile type is written as a code, and the cloud, interpolation and effect volume information corresponding to the tile is generated.
-local-weather/METAR is used to store the METAR information for the METAR based tile setup. This must include latitude, longitude and altitude of a weather station, temperature, dewpoint, pressure, wind direction and speed, rain, snow and thunderstorm information as well as cloud layer altitude and coverage in oktas. Any properties set here will be executed when 'METAR' is selected as tile selection mode as long as local-weather/METAR/available-flag is set to 1 (this flag is set to zero whenever a tile has been created from METAR info to indicate that the information has been used, so if the user wants to reuse the information, then the flag must be reset).
-
8. Weather tile setup
+
+
9. Weather tile setup
Examples for weather tile setup can be found in Nasal/weather-tiles.nas. Each tile is generated by a sequence of Nasal function calls to first set weather stations, then to draw the cloud layers, and effect volumes. It is a bit awkward to have to write in Nasal to customize the system, but I can't think of a reasonable GUI for the task, except marking every placement on a map which exceeds my coding skills a bit. The problem is that there is no real standard solution - every weather tile needs different calls, sometimes a large one generating many clouds, sometimes several smaller ones.
@@ -300,7 +331,7 @@ If the cloud layer has an orientation, then all placement coordinates should be
To make your own tile visible, an entry in the menu gui/dialogs/local_weather_tiles.xml needs to be created and the name needs to be added with a tile setup call to the function set_tile in Nasal/local_weather.nas.
-
9. Performance tuning
+
10. Performance tuning
With default settings, the local weather package generates a 40x40 km weather tile when the aircraft is closer than 39 km to the tile center and unloads it when the aircraft is more than 39.5 km away. This means that the system can generate at most 4 tiles at once and clouds are visible for at least 19 km and up to 45 km (the latter number determined by fading in the shaders). However, rendering and managing multiple overcast cloud layers in a region of 80x80 km is a significant drain on performance. For older systems, a few things can be done:
@@ -321,29 +352,33 @@ With default settings, the local weather package generates a 40x40 km weather ti
-
10. Known issues
+
11. Known issues
-
The local weather system does not mix well with the standard weather system. 3d cloud layers can be placed in the absence of effect volumes, but any effect volume causing precipitation will let the layer behave in a strange way. Likewise, 2d cloud layers can be placed, but may or may not lead to strange rendering artefacts. Local weather, as long as interpolation and effect volumes are running, will in general overwrite all other settings - bother real weather METAR and user-specified settings from the menu. The results of mixing standard and local weather settings are unpredictable, and may not lead to the desired results.
+
[2.0.0] The local weather system does not mix well with the standard weather system. 3d cloud layers can be placed in the absence of effect volumes, but any effect volume causing precipitation will let the layer behave in a strange way. Likewise, 2d cloud layers can be placed, but may or may not lead to strange rendering artefacts. Local weather, as long as interpolation and effect volumes are running, will in general overwrite all other settings - bother real weather METAR and user-specified settings from the menu. The results of mixing standard and local weather settings are unpredictable, and may not lead to the desired results.
Some cloud textures have artefacts, rain textures have too sharp boundaries and in general some things could look better. Please don't complain, but rather get me good photographs of the sky, cloud texture files or create better AC3D cloud models. I will eventually improve texture quality, but it's not high up in the to-do list, and the cloud model files are openly accessible for anyone with an editor.
Rain and snow may not start properly. For me, rain is only generated when I switch 'Shader effects' on and off in the menu on startup, otherwise neither moving the menu slider nor entering an effect volume generate rain. This seems to be a bug of some Flightgear versions, not of the local weather system.
-
Especially with multiple overcast layers and weather fronts, loading and unloading weather tiles may take a long time / cause severe drops in framerate. The problem is much worse in GIT than in 2.0.0. Please refer to performance tuning to solve such problems. In general, overcast layers and tropical weather tiles do require a system on the high end of the performance scale to render properly.
+
Especially with multiple overcast layers and weather fronts, loading and unloading weather tiles may take a long time / cause severe drops in framerate. It seems that a dual core processor is very valuable with this particular issue - try switching multiprocessor support on if needed, otherwise please refer to performance tuning to solve such problems. In general, overcast layers and tropical weather tiles do require a system on the high end of the performance scale to render properly.
The local weather package is able to occasionally trigger errors like 'Warning:: Picked up error in TriangleIntersect'. These seem to be a problem in the core Flightgear code - the package does nothing but placing normal (rather simple) AC3D models into the scenery.
For dynamical weather, clouds sometimes appear to 'jump' to a position. The reason is that the control loop of cloud drift accepts for performance reasons only a limited number of clouds. If there are more in the field of view, the most distant clouds are not processed. As nearby clouds drift out of the visual field, more distant clouds move into the loop, at which point their position is suddenly updated, resulting in a jump. Short of committing potentially vast computational resources (if there is a large number of clouds in the visual field), there is no easy fix to the problem.
-
To smooth out changes in wind settings, rather than producing a sudden gust, the wind is changed over about 1 second. In sharp wind gradients, this produces a series of ripples like turbulence. This is actually a realistic behaviour in this situation and hence left as it is. Some systems seem to take an unreasonable effort to reinit the environment (as must be done to set wind) - here the function call setWindSmoothly() in local_weather.nas should perhaps be replace by setWind to ease the load.
+
[2.0.0] To smooth out changes in wind settings, rather than producing a sudden gust, the wind is changed over about 1 second. In sharp wind gradients, this produces a series of ripples like turbulence. This is actually a realistic behaviour in this situation and hence left as it is. Some systems seem to take an unreasonable effort to reinit the environment (as must be done to set wind) - here the function call setWindSmoothly() in local_weather.nas should perhaps be replace by setWind to ease the load.
Large tile creation distances can cause problems in low visibility weather, because Flightgear loads terrain only if it is within visual range. Thus, trying to sample the terrain for a tile 55 km away in 8 km visibility doesn't work because the terrain elevation and altitude is not known. This may cause improper placement of clouds - chiefly convective clouds, but also layered clouds may not appear on the proper altitude. Currently, there is a limit which restricts tile loading range to 3 times the visibility, but presumably a better solution can be found.
-
Using the 'aloft interpolated' wind option, it is possible to turn the wind direction sharply over a small distance (for example, one may turn the wind by 90 degrees from one tile to the next). Such sharp wind changes are (in most situations) unphysical, and they may cause problems for local weather because they rotate the coordinate system to a degree that the neighbouring tile may not be identified correctly. In essence, the system may not generate new tiles because the nearest tile is still the last generated one. There will be a future fix to address the problem, for the moment just avoid rotating the wind strongly.
+
Using the 'aloft interpolated' wind option, it is possible to turn the wind direction sharply over a small distance (for example, one may turn the wind by 90 degrees from one tile to the next). Such sharp wind changes are (in most situations) unphysical, and they may cause problems for local weather because they rotate the coordinate system to a degree that the neighbouring tile may not be identified correctly. In essence, the system may not generate new tiles because the nearest tile is still the last generated one. The system largely tries to detect the problem and compensate, but occasionally this may be an issue.
-
The thermals in the soaring scenarios need GIT to work.
+
[2.0.0] The thermals in the soaring scenarios need GIT to work.
@@ -492,14 +527,20 @@ The boundary layer effectiveness, i.e. the amount of windspeed reduction at the
Realistically, the boundary layer should also depend on terrain coverage. Due to the need for performance to sample terrain coverage in the vicinity of the aircraft frequently, this is currently not implemented.
+Gusty winds are assumed to be a bounday layer phenomenon and faded out to zero at a multiple of the boundary layer thickness which is given by base wind speed [kt]/10, i.e. for 25 kt winds the gusts are absent for 2.5 times the bounday layer thickness.
+
The altitude model of visibility
+
+Relatively low visibility (as reported by METAR) is usually confined to low altitudes, due to the lower density of the upper atmosphere and the reduced level of dust and water the visibility at airliner cruise altitude is frequently in excess of 100 km. This means that an altitude model of the visibility is needed.
+
+Local weather assumes that haze and dust are confined to the lowest air layer. As a proxy for the transition to a higher layer, the altitude of the lowest cloud layer (or for 9000 ft for clear skies) is used. The visibility always increases with altitude, but below the first layer transition it increases slowly at a rate of 0.2 m / ft in altitude gain. During the layer transition where the transition occurs during 1500 ft, the visibility increases rather rapidly at a rate of 5 m / ft, which means that above the lowest air layer the visibility is almost 10 km more than on the ground. The visibility then continues to grow at a rate of 1 m / ft towards the Stratosphere. In all likelihood, this dramatically underestimates the true visibility at high altitudes, but has been chosen to limit the impact on performance.
Credits
The model of a thermal has been developed by Patrice Poly. The shader code used to transform clouds is heavily based on prior work by Stuart Buchanan. Hard-coding of some features by Torsten Dreyer is greatly appreciated.
-Thorsten Renk, October 2010
+Thorsten Renk, March 2011
diff --git a/Docs/barrier.jpg b/Docs/barrier.jpg
index 7b1a5f15f..cbe95ef62 100644
Binary files a/Docs/barrier.jpg and b/Docs/barrier.jpg differ
diff --git a/Docs/cloud_altitude_01.jpg b/Docs/cloud_altitude_01.jpg
index 2653567e9..6bbeeb333 100644
Binary files a/Docs/cloud_altitude_01.jpg and b/Docs/cloud_altitude_01.jpg differ
diff --git a/Docs/cloud_altitude_02.jpg b/Docs/cloud_altitude_02.jpg
index 41c293d46..e774ae49c 100644
Binary files a/Docs/cloud_altitude_02.jpg and b/Docs/cloud_altitude_02.jpg differ
diff --git a/Docs/cloud_altitude_03.jpg b/Docs/cloud_altitude_03.jpg
index 3b1d6afe0..ba54730fd 100644
Binary files a/Docs/cloud_altitude_03.jpg and b/Docs/cloud_altitude_03.jpg differ
diff --git a/Docs/clouds-cumulus_afternoon.jpg b/Docs/clouds-cumulus_afternoon.jpg
index 1bb103fb7..8d77dbed5 100644
Binary files a/Docs/clouds-cumulus_afternoon.jpg and b/Docs/clouds-cumulus_afternoon.jpg differ
diff --git a/Docs/high_pressure_border.jpg b/Docs/high_pressure_border.jpg
index e9b77f635..bb2b215c0 100644
Binary files a/Docs/high_pressure_border.jpg and b/Docs/high_pressure_border.jpg differ
diff --git a/Docs/menu2.jpg b/Docs/menu2.jpg
index 97714ac0c..a7bf2cd9d 100644
Binary files a/Docs/menu2.jpg and b/Docs/menu2.jpg differ
diff --git a/Effects/clouds-box.eff b/Effects/clouds-box.eff
new file mode 100644
index 000000000..88699a331
--- /dev/null
+++ b/Effects/clouds-box.eff
@@ -0,0 +1,72 @@
+
+
+ Effects/clouds-thin
+
+
+
+
+
+
+
+ /sim/rendering/shader-effects
+
+ 1.0
+
+
+
+
+
+
+ true
+
+ 0.5 0.5 0.5 1.0
+ 0.5 0.5 0.5 1.0
+ off
+
+
+ greater
+ 0.01
+
+ smooth
+
+ src-alpha
+ one-minus-src-alpha
+
+
+ false
+
+
+ 10
+ DepthSortedBin
+
+
+ 0
+
+
+
+
+
+
+
+
+ Shaders/clouds-box.vert
+ Shaders/clouds-box.frag
+
+
+
+ baseTexture
+ sampler-2d
+ 0
+
+ true
+
+
+
diff --git a/Effects/rain-layer.eff b/Effects/rain-layer.eff
new file mode 100644
index 000000000..c524e4ebc
--- /dev/null
+++ b/Effects/rain-layer.eff
@@ -0,0 +1,64 @@
+
+
+ Effects/rain-layer
+
+
+
+
+
+
+
+ /sim/rendering/shader-effects
+
+ 1.0
+
+
+
+
+
+
+ true
+
+ 0.5 0.5 0.5 1.0
+ 0.5 0.5 0.5 1.0
+ off
+
+
+ greater
+ 0.01
+
+ smooth
+
+ src-alpha
+ one-minus-src-alpha
+
+
+ false
+
+
+ 10
+ DepthSortedBin
+
+
+ 0
+
+
+
+
+
+
+
+
+ Shaders/rain-layer.vert
+ Shaders/rain-layer.frag
+
+
+ baseTexture
+ sampler-2d
+ 0
+
+ true
+
+
+
diff --git a/Nasal/compat_layer.nas b/Nasal/compat_layer.nas
index dd90543f9..0e851c8bc 100644
--- a/Nasal/compat_layer.nas
+++ b/Nasal/compat_layer.nas
@@ -1,11 +1,12 @@
########################################################
# compatibility layer for local weather package
-# Thorsten Renk, July 2010
+# Thorsten Renk, March 2011
########################################################
# function purpose
#
+# setDefaultCloudsOff to remove the standard Flightgear 3d clouds
# setVisibility to set the visibility to a given value
# setLift to set lift to given value
# setRain to set rain to a given value
@@ -97,6 +98,29 @@ print("Compatibility layer: tests done.");
+var setDefaultCloudsOff = func {
+
+if (features.can_disable_environment == 1)
+ {
+ var layers = props.globals.getNode("/environment/clouds").getChildren("layer");
+
+ foreach (l; layers)
+ {
+ l.getNode("coverage-type").setValue(5);
+ }
+ }
+else
+ {
+ var layers = props.globals.getNode("/environment/clouds").getChildren("layer");
+
+ foreach (l; layers)
+ {
+ l.getNode("coverage").setValue("clear");
+ }
+ }
+
+}
+
####################################
# set visibility to given value
@@ -144,11 +168,18 @@ if (features.can_disable_environment == 1)
var setRain = func (rain) {
-# setting the lowest cloud layer to 30.000 ft is a workaround
-# as rain is only created below that layer in default
+if (features.can_disable_environment == 1)
+ {
+ setprop("/environment/rain-norm", rain);
+ }
+else
+ {
+ # setting the lowest cloud layer to 30.000 ft is a workaround
+ # as rain is only created below that layer in default
-setprop("environment/clouds/layer[0]/elevation-ft", 30000.0);
-setprop("environment/metar/rain-norm",rain);
+ setprop("/environment/clouds/layer[0]/elevation-ft", 30000.0);
+ setprop("/environment/metar/rain-norm",rain);
+ }
}
@@ -158,11 +189,18 @@ setprop("environment/metar/rain-norm",rain);
var setSnow = func (snow) {
-# setting the lowest cloud layer to 30.000 ft is a workaround
-# as snow is only created below that layer in default
+if (features.can_disable_environment == 1)
+ {
+ setprop("/environment/snow-norm", snow);
+ }
+else
+ {
+ # setting the lowest cloud layer to 30.000 ft is a workaround
+ # as snow is only created below that layer in default
-setprop("environment/clouds/layer[0]/elevation-ft", 30000.0);
-setprop("environment/metar/snow-norm",snow);
+ setprop("environment/clouds/layer[0]/elevation-ft", 30000.0);
+ setprop("environment/metar/snow-norm",snow);
+ }
}
diff --git a/Nasal/local_weather.nas b/Nasal/local_weather.nas
index 6b6ca052b..34517a7d1 100644
--- a/Nasal/local_weather.nas
+++ b/Nasal/local_weather.nas
@@ -1,7 +1,7 @@
########################################################
# routines to set up, transform and manage local weather
-# Thorsten Renk, October 2010
+# Thorsten Renk, March 2011
# thermal model by Patrice Poly, April 2010
########################################################
@@ -41,6 +41,7 @@
# cumulus_exclusion_layer to create a layer with 'holes' left for thunderstorm placement
# create_rise_clouds to create a barrier cloud system
# create_streak to create a cloud streak
+# create_undulatus to create an undulating cloud pattern
# create_layer to create a cloud layer with optional precipitation
# 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)
@@ -56,6 +57,7 @@
# create_effect_volume to create an effect volume
# set_weather_station to specify a weather station for interpolation
# set_wind_ipoint to set an aloft wind interpolation point
+# set_wind_ipoint_metar to set a wind interpolation point from available ground METAR info where aloft is modelled
# showDialog to pop up a dialog window
# readFlags to read configuration flags from the property tree into Nasal variables at startup
# streak_wrapper wrapper to execute streak from menu
@@ -72,6 +74,7 @@
# object purpose
# weatherStation to store info about weather conditions
+# 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
@@ -285,20 +288,26 @@ 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];}
-else {alt_wind = altitude;}
+else {var alt_wind = altitude;}
for (var i = 0; i<9; i=i+1)
{if (alt_wind < wind_altitude_array[i]) {break;}}
-var altNodeMin = w.getChild("altitude",i-1);
-var altNodeMax = w.getChild("altitude",i);
+#var altNodeMin = w.getChild("altitude",i-1);
+#var altNodeMax = w.getChild("altitude",i);
-var vmin = altNodeMin.getNode("windspeed-kt").getValue();
-var vmax = altNodeMax.getNode("windspeed-kt").getValue();
+#var vmin = altNodeMin.getNode("windspeed-kt").getValue();
+#var vmax = altNodeMax.getNode("windspeed-kt").getValue();
-var dir_min = altNodeMin.getNode("wind-from-heading-deg").getValue();
-var dir_max = altNodeMax.getNode("wind-from-heading-deg").getValue();
+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;
var f = (alt_wind - wind_altitude_array[i-1])/(wind_altitude_array[i] - wind_altitude_array[i-1]);
@@ -307,20 +316,28 @@ var res = add_vectors(dir_min, (1-f) * vmin, dir_max, f * vmax);
return res;
}
+
+###################################
+# windfield spatial interpolation
+###################################
+
var wind_interpolation = func (lat, lon, alt) {
-var windNodes = props.globals.getNode(lw~"interpolation").getChildren("wind");
+# var windNodes = props.globals.getNode(lw~"interpolation").getChildren("wind");
var sum_norm = 0;
var sum_wind = [0,0];
+var wsize = size(windIpointArray);
-foreach (var w; windNodes) {
+for (var i = 0; i < wsize; i=i+1) {
- var wlat = w.getNode("latitude-deg").getValue();
- var wlon = w.getNode("longitude-deg").getValue();
+ #var wlat = w.getNode("latitude-deg").getValue();
+ #var wlon = w.getNode("longitude-deg").getValue();
+
+ var w = windIpointArray[i];
var wpos = geo.Coord.new();
- wpos.set_latlon(wlat,wlon,1000.0);
+ wpos.set_latlon(w.lat,w.lon,1000.0);
var ppos = geo.Coord.new();
ppos.set_latlon(lat,lon,1000.0);
@@ -328,10 +345,16 @@ foreach (var w; windNodes) {
var d = ppos.distance_to(wpos);
if (d <100.0) {d = 100.0;} # to prevent singularity at zero
- sum_norm = sum_norm + 1./d;
+ sum_norm = sum_norm + (1./d) * w.weight;
var res = wind_altitude_interpolation(alt,w);
- sum_wind = add_vectors(sum_wind[0], sum_wind[1], res[0], res[1]/d);
+
+ 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;}
}
@@ -397,12 +420,30 @@ var iNode = props.globals.getNode(lw~"interpolation", 1);
var cNode = props.globals.getNode(lw~"current", 1);
var viewpos = geo.aircraft_position();
+var sum_alt = 0.0;
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;
+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;}
+
# get an inverse distance weighted average from all defined weather stations
@@ -422,7 +463,7 @@ for (var i = 0; i < n_stations; i=i+1) {
sum_norm = sum_norm + 1./d * s.weight;
-
+ 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;
@@ -431,12 +472,12 @@ for (var i = 0; i < n_stations; i=i+1) {
# gradually fade in the interpolation weight of newly added stations to
# avoid sudden jumps
- if (s.weight < 1.0) {s.weight = s.weight + 0.1;}
+ if (s.weight < 1.0) {s.weight = s.weight + 0.02;}
# automatically delete stations out of range
# take care not to unload if weird values appear for a moment
# never unload if only one station left
- if ((d > 120000.0) and (d<140000.0) and (n_stations > 1))
+ 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);}
@@ -448,24 +489,62 @@ for (var i = 0; i < n_stations; i=i+1) {
setprop(lwi~"station-number", i);
-
+var ialt = sum_alt/sum_norm;
var vis = sum_vis/sum_norm;
var p = sum_p/sum_norm;
var D = sum_D/sum_norm;
var T = sum_T/sum_norm;
-# a simple altitude model for visibility - increase it with increasing altitude
+# altitude model for visibility - increase above the lowest inversion layer to simulate ground haze
var altitude = getprop("position/altitude-ft");
-vis = vis + 0.5 * altitude;
+var current_tile_index = getprop(lw~"tiles/tile[4]/tile-index");
-if (vis > 0.0) {iNode.getNode("visibility-m",1).setValue(vis);} # a redundancy check
-iNode.getNode("temperature-degc",1).setValue(T);
-iNode.getNode("dewpoint-degc",1).setValue(D);
-if (p>0.0) {iNode.getNode("pressure-sea-level-inhg",1).setValue(p);} # a redundancy check
-iNode.getNode("turbulence",1).setValue(0.0);
+#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;
+
+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);
# now check if an effect volume writes the property and set only if not
@@ -517,15 +596,15 @@ else if (wind_model_flag ==2) # constant in tile
}
else if (wind_model_flag ==3) # aloft interpolated, constant in tiles
{
- var w = props.globals.getNode(lw~"interpolation").getChild("wind",0);
+ var w = windIpointArray[0];
var res = wind_altitude_interpolation(altitude,w);
var winddir = res[0];
var windspeed = res[1];
}
else if (wind_model_flag == 5) # aloft waypoint interpolated
{
- var res = wind_interpolation(viewpos.lat(), viewpos.lon(), viewpos.alt());
-
+ var res = wind_interpolation(viewpos.lat(), viewpos.lon(), altitude);
+
var winddir = res[0];
var windspeed = res[1];
}
@@ -540,6 +619,8 @@ if (presampling_flag == 0)
{
var boundary_alt = 600.0;
var windspeed_ground = windspeed/3.0;
+
+ var f_min = 2.0/3.0;
if (altitude_agl < boundary_alt)
{var windspeed_current = windspeed_ground + 2.0 * windspeed_ground * (altitude_agl/boundary_alt);}
@@ -577,6 +658,37 @@ else
}
+# 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;
+ }
+
+
+
compat_layer.setWindSmoothly(winddir, windspeed_current);
@@ -588,7 +700,9 @@ cNode.getNode("wind-from-heading-deg").setValue(winddir);
cNode.getNode("wind-speed-kt").setValue(windspeed_current);
-if (getprop(lw~"interpolation-loop-flag") ==1) {settimer(interpolation_loop, 1.0);}
+
+
+if (getprop(lw~"interpolation-loop-flag") ==1) {settimer(interpolation_loop, interpolation_loop_time);}
}
@@ -1572,10 +1686,11 @@ setprop(lw~"convective-loop-flag",0);
weather_dynamics.convective_loop_kill_flag = 1; # long-running loop needs a different scheme to end
-# also remove rain and snow effects
+# also remove rain snow and saturation effects
compat_layer.setRain(0.0);
compat_layer.setSnow(0.0);
+compat_layer.setLight(1.0);
# set placement indices to zero
@@ -1592,6 +1707,10 @@ settimer ( func { setsize(weather_dynamics.cloudQuadtrees,0);},0.1); # to avoid
setsize(effectVolumeArray,0);
n_effectVolumeArray = 0;
+# if we have used METAR, we may no longer want to do so
+
+metar_flag = 0;
+
settimer ( func {
setsize(weather_tile_management.modelArrays,0);
@@ -1604,15 +1723,20 @@ settimer ( func {
setsize(weather_dynamics.tile_convective_altitude,0);
setsize(weather_dynamics.tile_convective_strength,0);
setsize(weatherStationArray,0);
+ setsize(windIpointArray,0);
setprop(lw~"clouds/buffer-count",0);
setprop(lw~"clouds/cloud-scenery-count",0);
weather_tile_management.n_cloudSceneryArray = 0;
- props.globals.getNode("local-weather/interpolation", 1).removeChildren("wind");
+ #props.globals.getNode("local-weather/interpolation", 1).removeChildren("wind");
setprop(lwi~"ipoint-number",0);
},1.1);
setprop(lw~"tmp/presampling-status", "idle");
+# indicate that we are no longer running
+
+local_weather_running_flag = 0;
+
}
@@ -2246,6 +2370,76 @@ for (var i=0; i(nx-jlow-1))) and ((i(ny-ilow-1)))) # select a small or no cloud
+ {
+ if (rn > 2.0) {flag = 1;} else {path = select_cloud_model(type,"small");}
+ }
+ if ((j(nx-jlow-1)) or (i(ny-ilow-1)))
+ {
+ if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"small");}
+ }
+ else { # select a large cloud
+ if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"large");}
+ }
+
+
+ if (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);}
+
+
+ }
+ }
+
+ }
+
+}
+
###########################################################
# place a cloud layer
@@ -2876,11 +3070,11 @@ append(effectVolumeArray,ev);
# set a weather station for interpolation
###########################################################
-var set_weather_station = func (lat, lon, vis, T, D, p) {
+var set_weather_station = func (lat, lon, alt, vis, T, D, p) {
-var s = weatherStation.new (lat, lon, vis, T, D, p);
+var s = weatherStation.new (lat, lon, alt, vis, T, D, p);
s.index = getprop(lw~"tiles/tile-counter");
-s.weight = 0.1;
+s.weight = 0.02;
# set a timestamp if needed
@@ -2899,42 +3093,56 @@ append(weatherStationArray,s);
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) {
-var n = props.globals.getNode(lwi, 1);
- for (var i = 0; 1; i += 1)
- if (n.getChild("wind", i, 0) == nil)
- break;
+var w = windIpoint.new(lat, lon, d0, v0, d1, v1, d2, v2, d3, v3, d4, v4, d5, v5, d6, v6, d7, v7, d8, v8);
-s = n.getChild("wind", i, 1);
+append(windIpointArray, w);
-s.getNode("latitude-deg",1).setValue(lat);
-s.getNode("longitude-deg",1).setValue(lon);
-s.getChild("altitude",0,1).getNode("wind-from-heading-deg",1).setValue(d0);
-s.getChild("altitude",0,1).getNode("windspeed-kt",1).setValue(v0);
+}
-s.getChild("altitude",1,1).getNode("wind-from-heading-deg",1).setValue(d1);
-s.getChild("altitude",1,1).getNode("windspeed-kt",1).setValue(v1);
-s.getChild("altitude",2,1).getNode("wind-from-heading-deg",1).setValue(d2);
-s.getChild("altitude",2,1).getNode("windspeed-kt",1).setValue(v2);
+###########################################################
+# set a wind interpolation point from ground METAR data
+###########################################################
-s.getChild("altitude",3,1).getNode("wind-from-heading-deg",1).setValue(d3);
-s.getChild("altitude",3,1).getNode("windspeed-kt",1).setValue(v3);
+var set_wind_ipoint_metar = func (lat, lon, d0, v0) {
-s.getChild("altitude",4,1).getNode("wind-from-heading-deg",1).setValue(d4);
-s.getChild("altitude",4,1).getNode("windspeed-kt",1).setValue(v4);
+# insert a plausible pattern of aloft winds based on ground info
-s.getChild("altitude",5,1).getNode("wind-from-heading-deg",1).setValue(d5);
-s.getChild("altitude",5,1).getNode("windspeed-kt",1).setValue(v5);
-s.getChild("altitude",6,1).getNode("wind-from-heading-deg",1).setValue(d6);
-s.getChild("altitude",6,1).getNode("windspeed-kt",1).setValue(v6);
+# 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);
+
+var v3 = v0 * (1.3 + rand() * 0.4) + 5.0;
+var d3 = d0 + dsign * (3.0 * rand() + dsign * 4.0);
+
+var v4 = v0 * (1.7 + rand() * 0.5) + 10.0;
+var d4 = d0 + dsign * (4.0 * rand() + dsign * 8.0);
+
+var v5 = v0 * (1.7 + rand() * 0.5) + 20.0;
+var d5 = d0 + dsign * (4.0 * rand() + dsign * 10.0);
+
+var v6 = v0 * (1.7 + rand() * 0.5) + 40.0;
+var d6 = d0 + dsign * (4.0 * rand() + dsign * 12.0);
+
+var v7 = v0 * (2.0 + rand() * 0.7) + 50.0;
+var d7 = d0 + dsign * (4.0 * rand() + dsign * 13.0);
+
+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);
-s.getChild("altitude",7,1).getNode("wind-from-heading-deg",1).setValue(d7);
-s.getChild("altitude",7,1).getNode("windspeed-kt",1).setValue(v7);
-s.getChild("altitude",8,1).getNode("wind-from-heading-deg",1).setValue(d8);
-s.getChild("altitude",8,1).getNode("windspeed-kt",1).setValue(v8);
}
@@ -3185,6 +3393,14 @@ if (wind_model_flag == 5)
var set_tile = func {
+# 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;
+ }
+
var type = getprop("/local-weather/tmp/tile-type");
@@ -3227,6 +3443,9 @@ if (compat_layer.features.can_disable_environment ==1)
}
+# switch off normal 3d clouds
+
+compat_layer.setDefaultCloudsOff();
# now see if we need to presample the terrain
@@ -3246,14 +3465,7 @@ if ((getprop("/environment/metar/valid") == 1) and (getprop(lw~"tmp/tile-managem
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"))
@@ -3267,24 +3479,44 @@ else if ((getprop("/environment/metar/valid") == 0) and (getprop(lw~"tmp/tile-ma
# 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)))
- {set_aloft_wrapper();}
+ {
+ if (metar_flag != 1)
+ {set_aloft_wrapper();}
+ }
# prepare the first tile wind field
if (metar_flag == 1) # the winds from current METAR are used
{
+
+ # 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");
+
+
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, 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");
- var boundary_correction = 1.0/get_slowdown_fraction();
+ set_wind_ipoint_metar(station_lat, station_lon, metar_base_wind_deg, metar_base_wind_speed);
- 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"));
+ var res = wind_interpolation(lat,lon,0.0);
+
+ 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]);
}
else
{
@@ -3466,6 +3698,10 @@ if (getprop(lw~"config/buffer-flag") ==1)
}
}
+# and indicate that we're up and running
+
+local_weather_running_flag = 1;
+
# weather_tile_management.watchdog_loop();
}
@@ -3594,10 +3830,11 @@ fgcommand("reinit", props.Node.new({subsystem:"environment"}));
#################################################################
var weatherStation = {
- new: func (lat, lon, vis, T, D, p) {
+ new: func (lat, lon, alt, vis, T, D, p) {
var s = { parents: [weatherStation] };
s.lat = lat;
s.lon = lon;
+ s.alt = alt;
s.vis = vis;
s.T = T;
s.D = D;
@@ -3613,6 +3850,63 @@ var weatherStation = {
},
};
+
+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] };
@@ -3763,7 +4057,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, Sand: 0.25, Grass: 0.2, Ocean: 0.01, Marsh: 0.05, Lake: 0.01, ShrubCover: 0.15, Landmass: 0.2, CropWoodCover: 0.15, MixedForestCover: 0.1, DryCropPastureCover: 0.25, MixedCropPastureCover: 0.2, IrrCropPastureCover: 0.15, DeciduousBroadCover: 0.1, pa_taxiway : 0.35, pa_tiedown: 0.35, pc_taxiway: 0.35, pc_tiedown: 0.35, Glacier: 0.01, DryLake: 0.3, IntermittentStream: 0.2};
+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};
# a hash map of average vertical cloud model sizes
@@ -3806,9 +4100,11 @@ var thermal = {};
var wave = {};
-# array of currently existing weather stations
+# arrays of currently existing weather stations and wind interpolation points
var weatherStationArray = [];
+var windIpointArray = [];
+
# 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
@@ -3828,6 +4124,11 @@ var cloud_mean_altitude = 0.0;
var cloud_fractional_lifetime = 0.0;
var cloud_evolution_timestamp = 0.0;
+# 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;
@@ -3838,7 +4139,7 @@ var detailed_clouds_flag = 1;
var dynamical_convection_flag = 1;
var debug_output_flag = 1;
var metar_flag = 0;
-
+var local_weather_running_flag = 0;
# set all sorts of default properties for the menu
@@ -3893,10 +4194,13 @@ setprop(lw~"tmp/box-bottom-n",12);
setprop(lw~"tmp/tile-type", "High-pressure");
setprop(lw~"tmp/tile-orientation-deg", 260.0);
setprop(lw~"tmp/windspeed-kt", 8.0);
+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);
setprop(lw~"tmp/tile-alt-offset-ft", 0.0);
setprop(lw~"tmp/tile-alt-median-ft",0.0);
setprop(lw~"tmp/tile-alt-min-ft",0.0);
-setprop(lw~"tmp/tile-management", "single tile");
+setprop(lw~"tmp/tile-management", "realistic weather");
setprop(lw~"tmp/presampling-flag", 1);
setprop(lw~"tmp/asymmetric-tile-loading-flag", 0);
setprop(lw~"tmp/last-reading-pos-del",0);
@@ -3933,7 +4237,7 @@ setprop(lw~"tmp/ipoint-longitude-deg",getprop("position/longitude-deg"));
setprop(lw~"config/distance-to-load-tile-m",39000.0);
setprop(lw~"config/distance-to-remove-tile-m",39500.0);
setprop(lw~"config/detailed-clouds-flag",1);
-setprop(lw~"config/dynamics-flag",1);
+setprop(lw~"config/dynamics-flag",0);
setprop(lw~"config/thermal-properties",1.0);
setprop(lw~"config/wind-model","constant");
setprop(lw~"config/buffer-flag",1);
@@ -3943,9 +4247,9 @@ 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);
-setprop(lw~"config/debug-output-flag",1);
+setprop(lw~"config/debug-output-flag",0);
setprop(lw~"config/generate-thermal-lift-flag", 0);
-setprop(lw~"config/dynamical-convection-flag", 1);
+setprop(lw~"config/dynamical-convection-flag", 0);
setprop(lw~"config/thread-flag", 1);
# set the default loop flags to loops inactive
diff --git a/Nasal/weather_tile_management.nas b/Nasal/weather_tile_management.nas
index f1634d004..a17e0af2c 100644
--- a/Nasal/weather_tile_management.nas
+++ b/Nasal/weather_tile_management.nas
@@ -1,6 +1,6 @@
########################################################
# routines to set up, transform and manage weather tiles
-# Thorsten Renk, October 2010
+# Thorsten Renk, March 2011
########################################################
# function purpose
@@ -18,6 +18,8 @@
# calc_geo to get local Cartesian geometry for latitude conversion
# get_lat to get latitude from Cartesian coordinates
# get_lon to get longitude from Cartesian coordinates
+# relangle to compute the relative angle between two directions, normalized to [0:180]
+# norm_relangle to compute the relative angle between two directions, normalized to [0:360]
# delete_from_vector to delete an element 'n' from a vector
# object purpose
@@ -53,6 +55,15 @@ if (distance_to_load > 3.0 * current_visibility)
if (distance_to_load < 29000.0)
{distance_to_load = 29000.0;}
+# check here if we have a new weather station if METAR is running
+
+if ((local_weather.metar_flag == 1) and (getprop(lw~"METAR/station-id") != getprop("/environment/metar/station-id")))
+ {
+ weather_tiles.set_METAR_weather_station();
+ }
+
+
+
foreach (var t; tNode) {
var tpos = geo.Coord.new();
@@ -200,32 +211,81 @@ if (((local_weather.presampling_flag == 1) and (getprop(lw~"tmp/presampling-stat
}
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)
+ if (local_weather.debug_output_flag == 1)
+ {print("alpha: ", alpha, " alpha_test: ", alpha_test, " relangle: ", relangle(alpha, alpha_test));}
+
+
+ var coordinate_rotation_angle = norm_relangle(alpha, alpha_test);
+
+
+ if (coordinate_rotation_angle < 45.0)
{
- 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;
- }
+ var system_rotation_angle = 0;
+ var displacement_angle = coordinate_rotation_angle;
+ }
+ else if (coordinate_rotation_angle < 135.0)
+ {
+ var system_rotation_angle = 90.0;
+ var displacement_angle = coordinate_rotation_angle - 90.0;
+ }
+ else if (coordinate_rotation_angle < 225.0)
+ {
+ var system_rotation_angle = 180.0;
+ var displacement_angle = coordinate_rotation_angle - 180.0;
+ }
+ else if (coordinate_rotation_angle < 315.0)
+ {
+ var system_rotation_angle = 270.0;
+ var displacement_angle = coordinate_rotation_angle - 270.0;
}
else
{
- alpha = alpha_test;
+ var system_rotation_angle = 0;
+ var displacement_angle = coordinate_rotation_angle - 360.0;
}
+
+
+ if (displacement_angle < -step)
+ {
+ print("Coordinate rotation by more than ",step," deg... compensating");
+ displacement_angle = -step;
+ }
+ else if (displacement_angle > step)
+ {
+ print("Coordinate rotation by more than ",step," deg... compensating");
+ displacement_angle = step;
+ }
+
+ alpha = alpha + system_rotation_angle + displacement_angle;
+
+
+
+ #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;
+ # }
}
-
+
setprop(lw~"tmp/tile-orientation-deg",alpha);
@@ -255,7 +315,85 @@ if (((local_weather.presampling_flag == 1) and (getprop(lw~"tmp/presampling-stat
{
var res = local_weather.wind_interpolation(lat,lon,0.0);
- alpha = res[0];
+ var step = 20.0;
+ var alpha_test = res[0];
+
+
+ if (local_weather.debug_output_flag == 1)
+ {print("alpha: ", alpha, " alpha_test: ", alpha_test, " relangle: ", relangle(alpha, alpha_test));}
+
+
+ var coordinate_rotation_angle = norm_relangle(alpha, alpha_test);
+
+ #print("Norm_relangle : ", norm_relangle(alpha, alpha_test));
+
+
+ if (coordinate_rotation_angle < 45.0)
+ {
+ var system_rotation_angle = 0;
+ var displacement_angle = coordinate_rotation_angle;
+ }
+ else if (coordinate_rotation_angle < 135.0)
+ {
+ var system_rotation_angle = 90.0;
+ var displacement_angle = coordinate_rotation_angle - 90.0;
+ }
+ else if (coordinate_rotation_angle < 225.0)
+ {
+ var system_rotation_angle = 180.0;
+ var displacement_angle = coordinate_rotation_angle - 180.0;
+ }
+ else if (coordinate_rotation_angle < 315.0)
+ {
+ var system_rotation_angle = 270.0;
+ var displacement_angle = coordinate_rotation_angle - 270.0;
+ }
+ else
+ {
+ var system_rotation_angle = 0;
+ var displacement_angle = coordinate_rotation_angle - 360.0;
+ }
+
+ #print("Displacement angle: ", displacement_angle);
+
+ if (displacement_angle < -step)
+ {
+ print("Coordinate rotation by more than ",step," deg... compensating");
+ displacement_angle = -step;
+ }
+ else if (displacement_angle > step)
+ {
+ print("Coordinate rotation by more than ",step," deg... compensating");
+ displacement_angle = step;
+ }
+
+ #print("Normalized displacement angle: ", displacement_angle);
+
+ alpha = alpha + system_rotation_angle + displacement_angle;
+
+ #print("alpha_out: ", alpha);
+
+ #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;
+ # }
+
+ #alpha = alpha_test;
+
+
+
setprop(lw~"tmp/tile-orientation-deg",alpha);
var windspeed = res[1];
setprop(lw~"tmp/windspeed-kt",windspeed);
@@ -521,8 +659,40 @@ var t = props.globals.getNode(lw~"tiles").getChild("tile",index,0);
var lat = t.getNode("latitude-deg").getValue();
var lon = t.getNode("longitude-deg").getValue();
-var alpha = getprop(lw~"tmp/tile-orientation-deg");
+# var alpha = getprop(lw~"tmp/tile-orientation-deg");
+var alpha_old = getprop(lw~"tiles/tile[4]/orientation-deg");
+var alpha = t.getNode("orientation-deg").getValue();
+
+var coordinate_rotation_angle = norm_relangle(alpha_old, alpha);
+
+if (coordinate_rotation_angle < 45.0)
+ {
+ var system_rotation_angle = 0;
+ var displacement_angle = coordinate_rotation_angle;
+ }
+else if (coordinate_rotation_angle < 135.0)
+ {
+ var system_rotation_angle = 90.0;
+ var displacement_angle = coordinate_rotation_angle - 90.0;
+ }
+else if (coordinate_rotation_angle < 225.0)
+ {
+ var system_rotation_angle = 180.0;
+ var displacement_angle = coordinate_rotation_angle - 180.0;
+ }
+else if (coordinate_rotation_angle < 315.0)
+ {
+ var system_rotation_angle = 270.0;
+ var displacement_angle = coordinate_rotation_angle - 270.0;
+ }
+else
+ {
+ var system_rotation_angle = 0;
+ var displacement_angle = coordinate_rotation_angle - 360.0;
+ }
+
+alpha = alpha_old + displacement_angle;
if (index == 0)
{
@@ -622,8 +792,90 @@ else if (index == 8)
}
+
+
+if (system_rotation_angle > 0.0)
+ {
+ if (local_weather.debug_output_flag == 1)
+ {print("Rotating coordinate system by ", system_rotation_angle, " degrees");}
+
+ # create a buffer entry for rotation, this is deleted in the rotation routine
+
+ create_neighbour(lat, lon, 9, alpha);
+ rotate_tile_scheme(system_rotation_angle);
+ }
}
+
+###################################
+# rotate tile scheme
+###################################
+
+var rotate_tile_scheme = func (angle) {
+
+if (angle < 45.0)
+ {
+ return;
+ }
+else if (angle < 135)
+ {
+
+
+ copy_entry(2,9);
+ copy_entry(8,2);
+ copy_entry(6,8);
+ copy_entry(0,6);
+ copy_entry(9,0);
+ copy_entry(5,9);
+ copy_entry(7,5);
+ copy_entry(3,7);
+ copy_entry(1,3);
+ copy_entry(9,1);
+
+ props.globals.getNode(lw~"tiles").removeChild("tile",9);
+ }
+else if (angle < 225)
+ {
+ copy_entry(8,9);
+ copy_entry(0,8);
+ copy_entry(9,0);
+
+ copy_entry(7,9);
+ copy_entry(1,7);
+ copy_entry(9,1);
+
+ copy_entry(6,9);
+ copy_entry(2,6);
+ copy_entry(9,2);
+
+ copy_entry(5,9);
+ copy_entry(3,5);
+ copy_entry(9,3);
+
+ props.globals.getNode(lw~"tiles").removeChild("tile",9);
+ }
+else if (angle < 315)
+ {
+ copy_entry(0,9);
+ copy_entry(6,0);
+ copy_entry(8,6);
+ copy_entry(2,8);
+ copy_entry(9,2);
+ copy_entry(3,9);
+ copy_entry(7,3);
+ copy_entry(5,7);
+ copy_entry(1,5);
+ copy_entry(9,1);
+
+ props.globals.getNode(lw~"tiles").removeChild("tile",9);
+ }
+else
+ {
+ return;
+ }
+}
+
+
#####################################
# copy tile info in neighbour matrix
#####################################
@@ -1024,6 +1276,14 @@ foreach(t; tNode)
}
print("alpha: ",getprop(lw~"tmp/tile-orientation-deg"));
+var lat = getprop("/position/latitude-deg");
+var lon = getprop("/position/longitude-deg");
+
+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("====================");
settimer(watchdog_loop, 10.0);
@@ -1281,6 +1541,23 @@ return angdiff;
}
+var norm_relangle = func (alpha, beta) {
+
+var angdiff = beta - alpha;
+
+while (angdiff < 0.0)
+ {angdiff = angdiff + 360.0;}
+
+while (angdiff > 360.0)
+ {angdiff = angdiff - 360.0;}
+
+if (angdiff == 360.0)
+ {angdiff = 0.0;}
+
+return angdiff;
+}
+
+
var delete_from_vector = func(vec, index) {
var n = index+1;
diff --git a/Nasal/weather_tiles.nas b/Nasal/weather_tiles.nas
index 8ccfb2015..f1414739a 100644
--- a/Nasal/weather_tiles.nas
+++ b/Nasal/weather_tiles.nas
@@ -1,7 +1,7 @@
########################################################
# routines to set up weather tiles
-# Thorsten Renk, October 2010
+# Thorsten Renk, March 2011
########################################################
# function purpose
@@ -93,7 +93,7 @@ calc_geo(blat);
# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure)
-local_weather.set_weather_station(blat, blon, 20000.0, 14.0, 12.0, 29.78);
+local_weather.set_weather_station(blat, blon, alt_offset, 20000.0, 14.0, 12.0, 29.78);
#create_2_8_sstratus_streak(blat, blon,5000.0,0.0);
@@ -110,6 +110,13 @@ 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);
+
+# store convective altitude and strength
+
+append(weather_dynamics.tile_convective_altitude,3000.0);
+append(weather_dynamics.tile_convective_strength,0.0);
+
+
tile_finished();
}
@@ -149,7 +156,7 @@ var D = T - spread;
var p = 1025.0 + rand() * 6.0; p = adjust_p(p);
# and set them at the tile center
-local_weather.set_weather_station(blat, blon, vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
var alt = spread * 1000;
@@ -158,7 +165,8 @@ var strength = 0.0;
var rn = rand();
-if (rn > 0.5)
+
+if (rn > 0.66)
{
# cloud scenario 1: weak cumulus development and blue thermals
@@ -176,12 +184,24 @@ if (rn > 0.5)
}
}
-else if (rn > 0.0)
+else if (rn > 0.33)
{
# cloud scenario 2: some Cirrocumulus patches
+ strength = rand() * 0.03;
+ local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0);
+
create_2_8_cirrocumulus(blat, blon, alt + alt_offset + 5000.0, alpha);
}
+else if (rn > 0.0)
+ {
+ # cloud scenario 3: Cirrostratus undulatus over weak cumulus
+
+ strength = rand() * 0.03;
+ local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0);
+
+ create_4_8_cirrostratus_undulatus(blat, blon, alt + alt_offset + 32000.0, alpha);
+ }
# store convective altitude and strength
@@ -229,7 +249,7 @@ var D = T - spread;
var p = 1019.0 + rand() * 6.0; p = adjust_p(p);
# and set them at the tile center
-local_weather.set_weather_station(blat, blon, vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
var alt = spread * 1000;
@@ -241,7 +261,7 @@ var rn = rand();
-if (rn > 0.66)
+if (rn > 0.75)
{
# cloud scenario 1: possible Cirrus over Cumulus
strength = 0.2 + rand() * 0.4;
@@ -263,7 +283,7 @@ if (rn > 0.66)
}
}
-else if (rn > 0.33)
+else if (rn > 0.5)
{
# cloud scenario 2: Cirrostratus over weak Cumulus
@@ -273,7 +293,7 @@ else if (rn > 0.33)
create_2_8_cirrostratus(blat, blon, alt+alt_offset+25000.0, alpha);
}
-else if (rn > 0.0)
+else if (rn > 0.25)
{
# cloud scenario 3: Cirrocumulus sheet over Cumulus
@@ -288,6 +308,15 @@ else if (rn > 0.0)
compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset +24000,alpha);
}
+else if (rn > 0.0)
+ {
+ # cloud scenario 4: Cirrostratus undulatus over weak Cumulus
+
+ strength = 0.15 + rand() * 0.15;
+ local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0);
+
+ create_4_8_cirrostratus_undulatus(blat, blon, alt + alt_offset + 25000.0, alpha);
+ }
# store convective altitude and strength
@@ -334,7 +363,7 @@ var D = T - spread;
var p = 1013.0 + rand() * 6.0; p = adjust_p(p);
# and set them at the tile center
-local_weather.set_weather_station(blat, blon, vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
# now a random selection of different possible cloud configuration scenarios
@@ -490,14 +519,14 @@ calc_geo(blat);
# get probabilistic values for the weather parameters
-var vis = 15000.0 + rand() * 10000.0;
+var vis = 12000.0 + rand() * 9000.0;
var T = 10.0 + rand() * 10.0;
var spread = 2.0 + 2.0 * rand();
var D = T - spread;
var p = 1007.0 + rand() * 6.0; p = adjust_p(p);
# and set them at the tile center
-local_weather.set_weather_station(blat, blon, vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0;
@@ -507,7 +536,7 @@ var strength = 0.0;
var rn = rand();
-if (rn > 0.8)
+if (rn > 0.875)
{
# cloud scenario 1: a low 4/8 stratus patches, thin patches above
@@ -515,7 +544,7 @@ if (rn > 0.8)
create_4_8_stratus_patches(blat, blon, alt+alt_offset,alpha);
create_4_8_tstratus_patches(blat, blon, alt+alt_offset+6000,alpha);
}
-else if (rn > 0.6)
+else if (rn > 0.75)
{
# cloud scenario 2: a low 4/8 undulatus, thin patches above
@@ -523,7 +552,7 @@ else if (rn > 0.6)
create_4_8_sstratus_undulatus(blat, blon, alt+alt_offset,alpha);
create_2_8_tstratus(blat, blon, alt+alt_offset+7000,alpha);
}
-else if (rn > 0.4)
+else if (rn > 0.625)
{
# cloud scenario 3: low Stratocumulus
@@ -532,7 +561,7 @@ else if (rn > 0.4)
create_2_8_sstratus(blat, blon, alt+alt_offset+6000,alpha);
create_2_8_tstratus(blat, blon, alt+alt_offset+9000,alpha);
}
-else if (rn > 0.2)
+else if (rn > 0.5)
{
# cloud scenario 4: dense low Stratocumulus
@@ -541,7 +570,7 @@ else if (rn > 0.2)
create_detailed_stratocumulus_bank(blat, blon, alt+alt_offset,alpha);
create_2_8_sstratus(blat, blon, alt+alt_offset+8000,alpha);
}
-else if (rn > 0.0)
+else if (rn > 375)
{
# cloud scenario 5: Cirrocumulus over 4/8 Stratus
@@ -550,6 +579,32 @@ else if (rn > 0.0)
create_4_8_cirrocumulus_bank(blat, blon, alt+alt_offset + 12000.0, alpha);
}
+else if (rn > 0.250)
+ {
+ # cloud scenario 6: Cirrostratus over 4/8 Stratus undulatus
+
+ alt = alt + local_weather.cloud_vertical_size_map["Stratus"] * 0.5 * m_to_ft;
+
+ create_6_8_stratus_undulatus(blat, blon, alt+alt_offset,alpha);
+
+ create_4_8_cirrostratus_undulatus(blat, blon, alt+alt_offset+24000,alpha);
+ }
+else if (rn > 0.125)
+ {
+ # cloud scenario 7: thin stratus
+
+ create_4_8_alttstratus_streaks(blat, blon, alt+alt_offset,alpha);
+
+ create_2_8_sstratus(blat, blon, alt+alt_offset+6000,alpha);
+ }
+else if (rn > 0.0)
+ {
+ # cloud scenario 8: thin stratus
+
+ create_4_8_alttstratus_patches(blat, blon, alt+alt_offset,alpha);
+
+ create_4_8_cirrostratus_undulatus(blat, blon, alt+alt_offset+25000,alpha);
+ }
# store convective altitude and strength
@@ -592,14 +647,16 @@ calc_geo(blat);
# get probabilistic values for the weather parameters
-var vis = 10000.0 + rand() * 10000.0;
+var vis = 8000.0 + rand() * 8000.0;
var T = 5.0 + rand() * 10.0;
var spread = 1.0 + 2.0 * rand();
var D = T - spread;
var p = 1001.0 + rand() * 6.0; p = adjust_p(p);
+
+
# and set them at the tile center
-local_weather.set_weather_station(blat, blon, vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0;
@@ -608,6 +665,7 @@ var strength = 0.0;
var rn = rand();
+
if (rn > 0.75)
{
@@ -708,14 +766,14 @@ calc_geo(blat);
# get probabilistic values for the weather parameters
-var vis = 8000.0 + rand() * 7000.0;
+var vis = 5000.0 + rand() * 5000.0;
var T = 3.0 + rand() * 7.0;
var spread = 1.0 + 1.0 * rand();
var D = T - spread;
var p = 995.0 + rand() * 6.0; p = adjust_p(p);
# and set them at the tile center
-local_weather.set_weather_station(blat, blon, vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
# set a closed Nimbostratus layer
@@ -783,7 +841,7 @@ var D = T - spread;
var p = 1005.0 + rand() * 10.0; p = adjust_p(p);
# and set them at the tile center
-local_weather.set_weather_station(blat, blon, vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0;
@@ -867,7 +925,7 @@ var D = T - spread;
var p = 1005.0 + rand() * 10.0; p = adjust_p(p);
# and set them at the tile center
-local_weather.set_weather_station(blat, blon, vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0;
@@ -982,7 +1040,7 @@ var p = 970 + rand() * 10.0; p = adjust_p(p);
# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure)
-local_weather.set_weather_station(blat, blon, vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0;
@@ -1104,18 +1162,18 @@ var p = 1005 + rand() * 10.0; p = adjust_p(p);
# after the front
x = 15000.0; y = 15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T-3.0, D-3.0, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T-3.0, D-3.0, p * hp_to_inhg);
x = -15000.0; y = 15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T-3.0, D-3.0, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T-3.0, D-3.0, p * hp_to_inhg);
# before the front
x = 15000.0; y = -15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis*0.7, T+3.0, D+3.0, (p-2.0) * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis*0.7, T+3.0, D+3.0, (p-2.0) * hp_to_inhg);
x = -15000.0; y = -15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis*0.7, T+3.0, D+3.0, (p-2.0) * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis*0.7, T+3.0, D+3.0, (p-2.0) * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0;
@@ -1235,18 +1293,18 @@ var p = 1005 + rand() * 10.0; p = adjust_p(p);
# after the front
x = 15000.0; y = 15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T+2.0, D+1.0, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T+2.0, D+1.0, p * hp_to_inhg);
x = -15000.0; y = 15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T+2.0, D+1.0, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T+2.0, D+1.0, p * hp_to_inhg);
# before the front
x = 15000.0; y = -15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T, D, p * hp_to_inhg);
x = -15000.0; y = -15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0;
@@ -1332,18 +1390,18 @@ var p = 1005 + rand() * 10.0; p = adjust_p(p);
# after the front
x = 15000.0; y = 15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T+2.0, D+1.0, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T+2.0, D+1.0, p * hp_to_inhg);
x = -15000.0; y = 15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T+2.0, D+1.0, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T+2.0, D+1.0, p * hp_to_inhg);
# before the front
x = 15000.0; y = -15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T, D, p * hp_to_inhg);
x = -15000.0; y = -15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0;
@@ -1445,18 +1503,18 @@ var p = 1005 + rand() * 10.0; p = adjust_p(p);
# after the front
x = 15000.0; y = 15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T+2.0, D+1.0, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T+2.0, D+1.0, p * hp_to_inhg);
x = -15000.0; y = 15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T+2.0, D+1.0, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T+2.0, D+1.0, p * hp_to_inhg);
# before the front
x = 15000.0; y = -15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T, D, p * hp_to_inhg);
x = -15000.0; y = -15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0 + local_weather.cloud_vertical_size_map["Nimbus"] * 0.5 * m_to_ft;
@@ -1548,18 +1606,18 @@ var p = 1005 + rand() * 10.0; p = adjust_p(p);
# after the front
x = 15000.0; y = 15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T+2.0, D+1.0, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T+2.0, D+1.0, p * hp_to_inhg);
x = -15000.0; y = 15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T+2.0, D+1.0, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T+2.0, D+1.0, p * hp_to_inhg);
# before the front
x = 15000.0; y = -15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T, D, p * hp_to_inhg);
x = -15000.0; y = -15000.0;
-local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T, D, p * hp_to_inhg);
+local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt_offset, vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0 + local_weather.cloud_vertical_size_map["Nimbus"] * 0.5 * m_to_ft;
@@ -1635,7 +1693,7 @@ var blon = getprop(lw~"tiles/tmp/longitude-deg");
calc_geo(blat);
# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure)
-local_weather.set_weather_station(blat, blon, 35000.0, 20.0, 16.0, 1018 * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, 35000.0, 20.0, 16.0, 1018 * hp_to_inhg);
@@ -1683,7 +1741,7 @@ var blon = getprop(lw~"tiles/tmp/longitude-deg");
calc_geo(blat);
# first weather info for tile center (lat, lon, visibility, temperature, dew point, pressure)
-local_weather.set_weather_station(blat, blon, 45000.0, 20.0, 15.0, 1018 * hp_to_inhg);
+local_weather.set_weather_station(blat, blon, alt_offset, 45000.0, 20.0, 15.0, 1018 * hp_to_inhg);
local_weather.generate_thermal_lift_flag = 3;
@@ -1724,7 +1782,7 @@ tile_finished();
var set_METAR_tile = func {
-setprop(lw~"tiles/code","METAR"); # to be replaced
+setprop(lw~"tiles/code","METAR");
tile_start();
@@ -1739,7 +1797,7 @@ var alpha = getprop("/environment/metar/base-wind-dir-deg");
var phi = alpha * math.pi/180.0;
var metar_alt_offset = 700.0 + getprop("/environment/metar/station-elevation-ft");
-print("metar_alt_offset", metar_alt_offset);
+# print("metar_alt_offset", metar_alt_offset);
# get the local time of the day in seconds
@@ -1752,32 +1810,9 @@ var blat = getprop(lw~"tiles/tmp/latitude-deg");
var blon = getprop(lw~"tiles/tmp/longitude-deg");
calc_geo(blat);
-# get the METAR position info
-
-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("/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"));
- }
-
+var p = inhg_to_hp * getprop("/environment/metar/pressure-sea-level-inhg");
# now get the cloud layer info
@@ -1788,10 +1823,14 @@ 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 thunderstorm_flag = 0;
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);
+# print("alt_low: ", alt_low);
+
+if ((alt_low < 0.0) or (cover_low ==0)) # we have to guess a value for the convective altitude for the visibility model
+ {alt_low = 8000.0;}
# first check a few obvious criteria
@@ -1814,17 +1853,23 @@ if ((cover_low == 3) or (cover_low == 4)) # scattered
# now see if there is a layer shading convective development
var coverage_above = 8 - 2 * layers[1].getNode("coverage-type").getValue();
+var coverage_above2 = 8 - 2 * layers[2].getNode("coverage-type").getValue();
+
+if (coverage_above2 > coverage_above)
+ {coverage_above = coverage_above2;}
+
if (coverage_above > 6) {cumulus_flag = 0;} # no Cumulus with strong layer above
-# always do Cumulus when there's a thunderstorm
-if (getprop(lw~"METAR/thunderstorm-flag") ==1) {cumulus_flag = 1;}
+# never do Cumulus when there's a thunderstorm
+if (getprop(lw~"METAR/thunderstorm-flag") ==1) {cumulus_flag = 0; thunderstorm_flag = 1;}
# if cumulus_flag is still 1 at this point, the lowest layer is Cumulus
# see if we need to adjust its strength
-if (cumulus_flag == 1)
+if ((cumulus_flag == 1) and (cover_low > 0))
{
if ((cover_low < 4) and (t > 39600) and (t < 68400)) {var strength = 0.4;}
+ if ((cover_low < 2) and (t > 39600) and (t < 68400)) {var strength = 0.2;}
else {var strength = 1.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
@@ -1832,21 +1877,35 @@ if (cumulus_flag == 1)
else
{var strength = 0.0;}
+
+# if thunderstorm_flag is 1, we do the lowest layer as thunderstorm scenario, somewhat ignoring the coverage info
+
+if (thunderstorm_flag == 1)
+ {
+ create_thunderstorm_scenario(blat, blon, alt_low+metar_alt_offset, alpha);
+ n = n + 1; # do not start parsing with lowest layer
+ }
+
+
for (var i = n; i 0) { rain_norm = 0.0; snow_norm = 0.0;} # rain and snow fall only from the lowest layer
+
if (altitude < 9000.0) # draw Nimbostratus or Stratus models
{
if (cover == 8)
+ {
if ((altitude < 2000) or (rain_norm > 0.3))
- {create_8_8_nimbus(blat, blon, altitude+metar_alt_offset, alpha);}
+ {create_8_8_nimbus_rain(blat, blon, altitude+metar_alt_offset, alpha, rain_norm);}
else
- {create_8_8_stratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ {create_8_8_stratus_rain(blat, blon, altitude+metar_alt_offset, alpha, rain_norm);}
+ }
else if ((cover < 8) and (cover > 4))
{
if (cumulus_flag == 1)
@@ -1855,44 +1914,115 @@ for (var i = n; i 0.1) and (altitude < 5000.0))
+ {
+ create_6_8_nimbus_rain(blat, blon, altitude+metar_alt_offset, alpha, rain_norm);
+ }
+ else if (rain_norm > 0.0)
+ {
+ create_6_8_stratus_rain(blat, blon, altitude+metar_alt_offset, alpha, rain_norm);
+ }
+ else
+ {
+ if ((p > 1010.0) and (i == 0)) # the lowest layer may be Stratocumulus
+ {
+ create_6_8_stratocumulus(blat, blon, altitude+metar_alt_offset, alpha);
+ }
+ else
+ {
+ if (rand() > 0.5)
+ {create_6_8_stratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ else
+ {create_6_8_stratus_undulatus(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+metar_alt_offset, alpha);}
- else if (rn > 0.5)
- {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+metar_alt_offset, alpha);}
- else if (rn > 0.0)
- {create_4_8_sstratus_undulatus(blat, blon, altitude+metar_alt_offset, alpha);}
+ if ((p > 1010.0) and (i == 0)) # the lowest layer may be Stratocumulus
+ {
+ create_4_8_stratocumulus(blat, blon, altitude+metar_alt_offset, alpha);
+ }
+ else
+ {
+ var rn = rand();
+ if (rn > 0.75)
+ {create_4_8_stratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ else if (rn > 0.5)
+ {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+metar_alt_offset, alpha);}
+ else if (rn > 0.0)
+ {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+metar_alt_offset, alpha);}
- else if (rn > 0.0)
- {create_2_8_sstratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ if (cumulus_flag == 0)
+ {
+ var rn = rand();
+ if (rn > 0.5)
+ {create_2_8_stratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ else if (rn > 0.0)
+ {create_2_8_sstratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ }
+ else
+ {
+ create_2_8_altocumulus_streaks(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+metar_alt_offset, alpha);}
+ {
+ if (altitude < 14000.0)
+ {create_8_8_tstratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ else
+ {create_8_8_cirrostratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ }
+ else if (cover > 4)
+ {
+ if (altitude < 14000.0)
+ {create_6_8_tstratus_undulatus(blat, blon, altitude+metar_alt_offset, alpha);}
+ else
+ {create_6_8_cirrostratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ }
else if (cover > 2)
{
- rn = rand();
- if (rn > 0.5)
+ var rn = rand();
+ if (rn > 0.75)
{create_4_8_tstratus_patches(blat, blon, altitude+metar_alt_offset, alpha);}
+ else if (rn > 0.5)
+ {create_4_8_alttstratus_streaks(blat, blon, altitude+metar_alt_offset, alpha);}
+ else if (rn > 0.25)
+ {create_4_8_alttstratus_patches(blat, blon, altitude+metar_alt_offset, alpha);}
else if (rn > 0.0)
{create_4_8_tstratus_undulatus(blat, blon, altitude+metar_alt_offset, alpha);}
}
else
- {create_2_8_tstratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ {
+ if (altitude < 14000.0)
+ {
+ var rn = rand();
+ if (rn > 0.66)
+ {create_2_8_tstratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ else if (rn > 0.33)
+ {create_2_8_sstratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ else
+ {create_2_8_alttstratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ }
+ else
+ {
+ var rn = rand();
+ if (rn > 0.5)
+ {create_2_8_cirrocumulus(blat, blon, altitude+metar_alt_offset, alpha);}
+ else
+ {create_2_8_alttstratus(blat, blon, altitude+metar_alt_offset, alpha);}
+
+ }
+ }
} # end if altitude
else
{
@@ -1901,9 +2031,22 @@ for (var i = n; i 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);}
+ {
+ var rn = rand();
+ if (rn > 0.5)
+ {create_4_8_cirrostratus_patches(blat, blon, altitude+metar_alt_offset, alpha);}
+ else
+ {create_4_8_cirrostratus_undulatus(blat, blon, altitude+metar_alt_offset, alpha);}
+ }
else
- {create_2_8_cirrostratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ {
+ var rn = rand();
+ if (rn > 0.5)
+ {create_2_8_cirrostratus(blat, blon, altitude+metar_alt_offset, alpha);}
+ else
+ {create_1_8_cirrocumulus(blat, blon, altitude+metar_alt_offset, alpha);}
+
+ }
}
@@ -1919,6 +2062,107 @@ append(weather_dynamics.tile_convective_strength,strength);
tile_finished();
}
+
+
+####################################
+# METAR station setup
+####################################
+
+var set_METAR_weather_station = func {
+
+
+# get the METAR position info
+
+ var station_lat = getprop("/environment/metar/station-latitude-deg");
+ var station_lon = getprop("/environment/metar/station-longitude-deg");
+ var metar_alt_offset = 700.0 + getprop("/environment/metar/station-elevation-ft");
+
+
+
+ # get the weather parameters
+
+ 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");
+
+ var windspeed = getprop("/environment/metar/base-wind-speed-kt");
+ var wind_range_from = getprop("/environment/metar/base-wind-range-from");
+ var wind_range_to = getprop("/environment/metar/base-wind-range-to");
+
+ var gust_strength = getprop("/environment/metar/gust-wind-speed-kt");
+ var alpha = getprop("/environment/metar/base-wind-dir-deg");
+
+
+ # some METAR report just above max. visibility, if so we guess visibility based on pressure
+
+ var is_visibility_max = 0;
+
+ if (vis == 9999) {is_visibility_max = 1;}
+
+ if ((vis > 16093) and (vis < 16094)) # that's 10 nm
+ {is_visibility_max = 1;}
+
+ if (is_visibility_max == 1)
+ {
+ if (p * inhg_to_hp < 1000.0) {vis = 10000.0 + 5000 * rand();}
+ else if (p * inhg_to_hp < 1010.0) {vis = 15000.0 + 7000 * rand();}
+ else if (p * inhg_to_hp < 1020.0) {vis = 22000.0 + 14000.0 * rand();}
+ else {vis = 30000.0 + 15000.0 * rand();}
+ }
+
+
+
+ # set the station
+ local_weather.set_weather_station(station_lat, station_lon, metar_alt_offset, vis, T, D, p);
+
+
+ # if we use aloft interpolated winds with METAR, also set a new wind interpolation point
+
+ if (local_weather.wind_model_flag == 5)
+ {
+ # if zero winds are reported, we do not rotate the tile to face north but use the last value
+
+ if ((alpha == 0.0) and (windspeed == 0.0))
+ {
+ alpha = getprop(lw~"tmp/tile-orientation-deg");
+ var phi = alpha * math.pi/180.0;
+ }
+
+
+ var boundary_correction = 1.0/local_weather.get_slowdown_fraction();
+ local_weather.set_wind_ipoint_metar(station_lat, station_lon, alpha, boundary_correction * windspeed);
+ }
+
+ # also compute and set gust wind info
+
+ var gust_angvar = 0.5 * weather_tile_management.relangle(wind_range_from, wind_range_to);
+
+ if (gust_strength > 0.0)
+ {
+ var gust_relative_strength = (gust_strength - windspeed)/windspeed;
+ setprop(lw~"tmp/gust-frequency-hz", 1.0);
+ }
+ else
+ {
+ var gust_relative_strength = 0.0;
+ setprop(lw~"tmp/gust-frequency-hz", 0.0);
+ }
+
+
+ setprop(lw~"tmp/gust-relative-strength", gust_relative_strength);
+ setprop(lw~"tmp/gust-angular-variation-deg", gust_angvar);
+
+
+ # and mark that we have used this station
+ setprop(lw~"METAR/station-id",getprop("/environment/metar/station-id"));
+
+
+}
+
+
####################################
# mid-level cloud setup calls
####################################
@@ -1929,6 +2173,12 @@ local_weather.create_streak("Stratus",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32
}
+var create_8_8_tstratus = func (lat, lon, alt, alpha) {
+
+local_weather.create_streak("Stratus (thin)",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0);
+
+}
+
var create_8_8_cirrostratus = func (lat, lon, alt, alpha) {
local_weather.create_streak("Cirrostratus",lat,lon,alt,500.0,30,1250.0,0.0,400.0,30,1250.0,0.0,400.0,alpha,1.0);
@@ -1939,6 +2189,38 @@ 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_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 (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, 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, 0.0, alt, -1, rain , -1, -1, -1,0 ,0.8);
+ }
+
+}
+
+
+var create_8_8_stratus_rain = func (lat, lon, alt, alpha, rain) {
+
+local_weather.create_streak("Stratus",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0);
+
+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 ,-1);
+ 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.9);
+ }
+else
+ {
+ local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, 0.0, alt, -1, rain , -1, -1, -1,0 ,0.9);
+ }
+}
+
var create_6_8_stratus = func (lat, lon, alt, alpha) {
@@ -1946,12 +2228,102 @@ local_weather.create_streak("Stratus",lat, lon, alt,500.0,20,0.0,0.2,20000.0,20,
}
+
+
+var create_6_8_nimbus_rain = func (lat, lon, alt, alpha, rain) {
+
+var phi = alpha * math.pi/180.0;
+
+
+for (var i = 0; i < 3; i = i + 1)
+ {
+ var x = 2.0 * (rand()-0.5) * 2000.0 + i * 12000.0 - 12000.0;
+ var y = 2.0 * (rand()-0.5) * 12000.0;
+ var beta = rand() * 360.0;
+
+ local_weather.create_layer("Nimbus", lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 1, 1.0);
+
+ 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), 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 );
+ }
+ }
+
+
+}
+
+
+var create_6_8_stratus_rain = func (lat, lon, alt, alpha, rain) {
+
+var phi = alpha * math.pi/180.0;
+
+
+for (var i = 0; i < 3; i = i + 1)
+ {
+ var x = 2.0 * (rand()-0.5) * 2000.0 + i * 12000.0 - 12000.0;
+ var y = 2.0 * (rand()-0.5) * 12000.0;
+ var beta = rand() * 360.0;
+
+ local_weather.create_layer("Stratus", lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt, 500.0, 12000.0, 7000.0, beta, 1.0, 0.2, 0, 0.0);
+
+ 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), 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 );
+ }
+ }
+
+
+}
+
+
+var create_6_8_stratus_undulatus = func (lat, lon, alt, alpha) {
+
+local_weather.create_undulatus("Stratus",lat, lon, alt,300.0,10,4000.0,0.1,400.0,50,800.0,0.1,100.0, 1000.0, alpha,1.0);
+}
+
+var create_6_8_tstratus_undulatus = func (lat, lon, alt, alpha) {
+
+local_weather.create_undulatus("Stratus (thin)",lat, lon, alt,300.0,10,4000.0,0.1,400.0,50,800.0,0.1,100.0, 1000.0, alpha,1.0);
+}
+
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_6_8_stratocumulus = func (lat, lon, alt, alpha) {
+
+if (local_weather.detailed_clouds_flag == 1)
+ {
+ for (i=0; i< 2; i=i+1)
+ {
+ var phi = alpha * math.pi/180.0;
+ var x = 2.0 * (rand()-0.5) * 4000;
+ var y = 2.0 * (rand()-0.5) * 4000;
+ var beta = rand() * 360.0;
+ create_detailed_stratocumulus_bank(lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt, alpha+beta);
+ }
+ }
+else
+ {
+ create_stratocumulus_bank(lat, lon, alt, alpha);
+ create_stratocumulus_bank(lat, lon, alt, alpha);
+ }
+
+}
+
+
var create_4_8_stratus = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
@@ -2039,6 +2411,11 @@ for (var i=0; i<6; i=i+1)
}
+var create_4_8_cirrostratus_undulatus = func (lat, lon, alt, alpha) {
+
+local_weather.create_undulatus("Cirrostratus",lat, lon, alt,300.0,5,8000.0,0.1,400.0,40,1000.0,0.1,100.0, 1500.0, alpha,1.0);
+}
+
var create_4_8_stratus_undulatus = func (lat, lon, alt, alpha) {
@@ -2140,6 +2517,51 @@ for (var i=0; i<20; i=i+1)
}
+var create_4_8_alttstratus_streaks = func (lat, lon, alt, alpha) {
+
+var phi = alpha * math.pi/180.0;
+
+for (var i=0; i<10; i=i+1)
+ {
+ var x = 2.0 * (rand()-0.5) * 15000;
+ var y = 2.0 * (rand()-0.5) * 15000;
+ var beta = (rand() -0.5) * 20.0;
+ var m = 20 + int(rand() * 20);
+ var n = 3 + int(rand() * 3);
+
+ local_weather.create_streak("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,600.0,m,550.0,0.0,700.0,n,550.0,0.0,450.0,alpha+beta+90,1.0);
+
+ }
+
+}
+
+var create_4_8_alttstratus_patches = func (lat, lon, alt, alpha) {
+
+var phi = alpha * math.pi/180.0;
+
+for (var i=0; i<14; 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("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,600.0,10,550.0,0.0,250.0,8,550.0,0.0,250.0,alpha+beta,1.0);
+
+ }
+
+}
+
+var create_4_8_stratocumulus = func (lat, lon, alt, alpha) {
+
+if (local_weather.detailed_clouds_flag == 1)
+ {
+ create_detailed_stratocumulus_bank(lat, lon, alt, alpha);
+ }
+else
+ {
+ create_stratocumulus_bank(lat, lon, alt, alpha);
+ }
+
+}
var create_2_8_stratus = func (lat, lon, alt, alpha) {
@@ -2237,6 +2659,36 @@ for (var i=0; i<25; i=i+1)
}
+var create_2_8_alttstratus = func (lat, lon, alt, alpha) {
+
+var phi = alpha * math.pi/180.0;
+
+for (var i=0; i<4; 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("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,600.0,10,550.0,0.0,250.0,8,550.0,0.0,250.0,alpha+beta,1.0);
+
+ }
+
+}
+
+var create_2_8_altocumulus_streaks = 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 = 2.0 * (rand()-0.5) * 10000;
+ var tri = 1.0 + rand();
+
+ local_weather.create_streak("Altocumulus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,1500.0,22,750.0,0.2,1000.0,8,750.0,0.2,1000.0,alpha ,tri);
+ }
+}
+
+
var create_1_8_cirrocumulus = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
@@ -2256,7 +2708,40 @@ for (var i = 0; i < 2; i = i + 1)
}
+var create_thunderstorm_scenario = func (lat, lon, alt, alpha) {
+var phi = alpha * math.pi/180.0;
+x = 2.0 * (rand()-0.5) * 12000;
+y = 2.0 * (rand()-0.5) * 12000;
+
+if (rand() > 0.6)
+ {create_medium_thunderstorm(lat +get_lat(x,y,phi), lon + get_lon(x,y,phi), alt, alpha);}
+else
+ {create_small_thunderstorm(lat +get_lat(x,y,phi), lon + get_lon(x,y,phi), alt, alpha);}
+
+if (rand() > 0.5) # we do a second thunderstorm
+ {
+ x = 2.0 * (rand()-0.5) * 12000;
+ y = 2.0 * (rand()-0.5) * 12000;
+ if (rand() > 0.8)
+ {create_medium_thunderstorm(lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt, alpha);}
+ else
+ {create_small_thunderstorm(lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt, alpha);}
+ }
+
+# the convective layer
+
+var strength = 0.3;
+var n = int(4000 * strength) * 0.5;
+local_weather.cumulus_exclusion_layer(lat, lon, alt, n, 20000.0, 20000.0, alpha, 0.3,2.5 , size(elat), elat, elon, erad);
+
+
+# some turbulence in the convection layer
+
+local_weather.create_effect_volume(3, lat, lon, 20000.0, 20000.0, alpha, 0.0, alt+3000.0, -1, -1, -1, 0.4, -1,0 ,-1);
+
+
+}
var create_stratocumulus_bank = func (lat, lon, alt, alpha) {
diff --git a/Shaders/clouds-box.frag b/Shaders/clouds-box.frag
new file mode 100644
index 000000000..4222fbfc8
--- /dev/null
+++ b/Shaders/clouds-box.frag
@@ -0,0 +1,11 @@
+uniform sampler2D baseTexture;
+varying float fogFactor;
+
+void main(void)
+{
+ vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);
+ vec4 finalColor = base * gl_Color;
+ gl_FragColor.rgb = mix(gl_Fog.color.rgb, finalColor.rgb, fogFactor );
+ gl_FragColor.a = mix(0.0, finalColor.a, fogFactor);
+}
+
diff --git a/Shaders/clouds-box.vert b/Shaders/clouds-box.vert
new file mode 100644
index 000000000..a58e716f5
--- /dev/null
+++ b/Shaders/clouds-box.vert
@@ -0,0 +1,72 @@
+// -*-C++-*-
+#version 120
+
+varying float fogFactor;
+
+//attribute vec3 usrAttr3;
+//attribute vec3 usrAttr4;
+
+//float textureIndexX = usrAttr3.r;
+//float textureIndexY = usrAttr3.g;
+//float wScale = usrAttr3.b;
+//float hScale = usrAttr4.r;
+//float shade = usrAttr4.g;
+//float cloud_height = usrAttr4.b;
+
+void main(void)
+{
+
+ float shade = 0.9;
+ float cloud_height = 1000.0;
+
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+ //gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0);
+ vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);
+ vec4 l = gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,1.0);
+ vec3 u = normalize(ep.xyz - l.xyz);
+
+ // Find a rotation matrix that rotates 1,0,0 into u. u, r and w are
+ // the columns of that matrix.
+ vec3 absu = abs(u);
+ vec3 r = normalize(vec3(-u.y, u.x, 0));
+ vec3 w = cross(u, r);
+
+ // Do the matrix multiplication by [ u r w pos]. Assume no
+ // scaling in the homogeneous component of pos.
+ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
+ gl_Position.xyz = gl_Vertex.x * u;
+ gl_Position.xyz += gl_Vertex.y * r * 1.0;
+ gl_Position.xyz += gl_Vertex.z * w * 1.0;
+ //gl_Position.xyz += gl_Vertex.y * r * wScale;
+ //gl_Position.xyz += gl_Vertex.z * w * hScale;
+ gl_Position.xyz += gl_Color.xyz;
+
+ // Determine a lighting normal based on the vertex position from the
+ // center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker.
+ float n = dot(normalize(-gl_LightSource[0].position.xyz),
+ normalize(mat3x3(gl_ModelViewMatrix) * (- gl_Position.xyz)));;
+
+ // Determine the position - used for fog and shading calculations
+ vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);
+ float fogCoord = abs(ecPosition.z);
+ float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height);
+
+ // Final position of the sprite
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Position;
+
+// Determine the shading of the sprite based on its vertical position and position relative to the sun.
+ n = min(smoothstep(-0.5, 0.0, n), fract);
+// Determine the shading based on a mixture from the backlight to the front
+ vec4 backlight = gl_LightSource[0].diffuse * shade;
+
+ gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);
+ gl_FrontColor += gl_FrontLightModelProduct.sceneColor;
+
+ // As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out.
+ gl_FrontColor.a = min(smoothstep(10.0, 100.0, fogCoord), 1 - smoothstep(25000.0, 30000.0, fogCoord));
+ gl_BackColor = gl_FrontColor;
+
+ // Fog doesn't affect clouds as much as other objects.
+ fogFactor = exp( -gl_Fog.density * fogCoord * 0.2);
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+}
diff --git a/Shaders/rain-layer.frag b/Shaders/rain-layer.frag
new file mode 100644
index 000000000..b51ff3af4
--- /dev/null
+++ b/Shaders/rain-layer.frag
@@ -0,0 +1,10 @@
+uniform sampler2D baseTexture;
+varying float fogFactor;
+
+void main(void)
+{
+ vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);
+ vec4 finalColor = base * gl_Color;
+ gl_FragColor.rgb = mix(gl_Fog.color.rgb, finalColor.rgb, fogFactor );
+ gl_FragColor.a = mix(0.0, finalColor.a, fogFactor);
+}
diff --git a/Shaders/rain-layer.vert b/Shaders/rain-layer.vert
new file mode 100644
index 000000000..f505e15f7
--- /dev/null
+++ b/Shaders/rain-layer.vert
@@ -0,0 +1,54 @@
+// -*-C++-*-
+#version 120
+
+varying float fogFactor;
+
+float shade = 0.8;
+float cloud_height = 1000.0;
+
+void main(void)
+{
+
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+ //gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0);
+ vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);
+ vec4 l = gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,1.0);
+ vec3 u = normalize(ep.xyz - l.xyz);
+
+ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
+ gl_Position.x = gl_Vertex.x;
+ gl_Position.y += gl_Vertex.y;
+ gl_Position.z += gl_Vertex.z;
+ gl_Position.xyz += gl_Color.xyz;
+
+
+
+ // Determine a lighting normal based on the vertex position from the
+ // center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker.
+ float n = dot(normalize(-gl_LightSource[0].position.xyz),
+ normalize(mat3x3(gl_ModelViewMatrix) * (- gl_Position.xyz)));;
+
+ // Determine the position - used for fog and shading calculations
+ vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);
+ float fogCoord = abs(ecPosition.z);
+ float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height);
+
+
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Position;
+
+// Determine the shading of the sprite based on its vertical position and position relative to the sun.
+ n = min(smoothstep(-0.5, 0.0, n), fract);
+// Determine the shading based on a mixture from the backlight to the front
+ vec4 backlight = gl_LightSource[0].diffuse * shade;
+
+ gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);
+ gl_FrontColor += gl_FrontLightModelProduct.sceneColor;
+
+ // As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out.
+ gl_FrontColor.a = min(smoothstep(100.0, 250.0, fogCoord), 1 - smoothstep(40000.0, 45000.0, fogCoord));
+ gl_BackColor = gl_FrontColor;
+
+ // Fog doesn't affect rain as much as other objects.
+ fogFactor = exp( -gl_Fog.density * fogCoord * 0.4);
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+}
diff --git a/gui/dialogs/local_weather.xml b/gui/dialogs/local_weather.xml
index e0130e620..6561adba8 100644
--- a/gui/dialogs/local_weather.xml
+++ b/gui/dialogs/local_weather.xml
@@ -10,8 +10,6 @@
false
-
-
10570
@@ -64,7 +62,7 @@
0525
-
+
100
@@ -103,7 +101,7 @@
dialog-apply
-
+
203
@@ -233,7 +231,7 @@
0440
-
+
100
@@ -428,7 +426,7 @@
1650
-
+
245
@@ -442,7 +440,7 @@
3200
-
+
410
@@ -651,7 +649,7 @@
0195
-
+
100
diff --git a/gui/dialogs/local_weather_tiles.xml b/gui/dialogs/local_weather_tiles.xml
index 770a45620..0f08aebb7 100644
--- a/gui/dialogs/local_weather_tiles.xml
+++ b/gui/dialogs/local_weather_tiles.xml
@@ -6,20 +6,20 @@
local_weather_tiles310
- 330
+ 360false5
- 300
+ 33010
- 275
+ 30528025true
@@ -43,13 +43,13 @@
5
- 240
+ 27067
- 240
+ 2704025/local-weather/tmp/tile-orientation-deg
@@ -57,13 +57,13 @@
105
- 240
+ 270125
- 240
+ 2703025/local-weather/tmp/windspeed-kt
@@ -71,17 +71,86 @@
155
- 240
+ 270
+
+
240
- 240
+ 2705025/local-weather/tmp/tile-alt-offset-ft
+
+
+
+ 5
+ 240
+
+
+
+
+
+
+ 65
+ 240
+ 50
+ 20
+ 0.0
+ 1.0
+ /local-weather/tmp/gust-frequency-hz
+
+ dialog-apply
+
+
+
+
+
+
+ 112
+ 240
+
+
+
+
+
+ 167
+ 240
+ 50
+ 20
+ 0.0
+ 1.5
+ /local-weather/tmp/gust-relative-strength
+
+ dialog-apply
+
+
+
+
+ 215
+ 240
+
+
+
+
+
+ 240
+ 240
+ 50
+ 20
+ 0.0
+ 45.0
+ /local-weather/tmp/gust-angular-variation-deg
+
+ dialog-apply
+
+
+
+
+
5