diff --git a/Docs/KLSV-10_00.jpg b/Docs/KLSV-10_00.jpg new file mode 100644 index 000000000..fee97d66b Binary files /dev/null and b/Docs/KLSV-10_00.jpg differ diff --git a/Docs/KLSV-12_00.jpg b/Docs/KLSV-12_00.jpg new file mode 100644 index 000000000..103b3729d Binary files /dev/null and b/Docs/KLSV-12_00.jpg differ diff --git a/Docs/KLSV-15_00.jpg b/Docs/KLSV-15_00.jpg new file mode 100644 index 000000000..17f3128fb Binary files /dev/null and b/Docs/KLSV-15_00.jpg differ diff --git a/Docs/KLSV-17_30.jpg b/Docs/KLSV-17_30.jpg new file mode 100644 index 000000000..941fda61a Binary files /dev/null and b/Docs/KLSV-17_30.jpg differ diff --git a/Docs/KLSV-19_00.jpg b/Docs/KLSV-19_00.jpg new file mode 100644 index 000000000..ed8e31b5f Binary files /dev/null and b/Docs/KLSV-19_00.jpg differ diff --git a/Docs/KLSV-5_00.jpg b/Docs/KLSV-5_00.jpg new file mode 100644 index 000000000..5d58b283f Binary files /dev/null and b/Docs/KLSV-5_00.jpg differ diff --git a/Docs/KLSV-7_00.jpg b/Docs/KLSV-7_00.jpg new file mode 100644 index 000000000..421b0a469 Binary files /dev/null and b/Docs/KLSV-7_00.jpg differ diff --git a/Docs/README.local_weather.html b/Docs/README.local_weather.html index f9bff84cd..dfc7c382e 100644 --- a/Docs/README.local_weather.html +++ b/Docs/README.local_weather.html @@ -7,7 +7,7 @@ -

Local Weather Package - v0.61

+

Local Weather Package - v0.7

1. Introduction

@@ -15,9 +15,9 @@ The aim of a local weather system is to simulate weather phenomena tied to speci 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.

-The local weather package ultimately aims to provide the functionality to simulate such local phenomena. In version 0.61, the package supplies various cloud placement algorithms, as well as local control over most major weather parameters (visibility, pressure, temperature, rain, snow, thermal lift...) through interpolation routines and event volumes. For long-range flights, it automatically provides transitions between different weather patterns. However, basically all features currently present can and will eventually be improved.

+The local weather package ultimately aims to provide the functionality to simulate such local phenomena. In version 0.7, the package supplies various cloud placement algorithms, as well as local control over most major weather parameters (visibility, pressure, temperature, rain, snow, thermal lift, turbulence...) through interpolation routines and event volumes. The package contains a fairly detailed algorithm to generate convective clouds and thermals with a realistic distribution. For long-range flights, it 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.

-As of version 0.61, the system does not contain dynamics (development of convective clouds, wind displacement of clouds...). Since wind and dynamics are closely related, any wind parameters can currently not be controlled from the local weather algorithms.

+As of version 0.7, the system does not contain dynamics (development of convective clouds, wind displacement of clouds...). Since wind and dynamics are closely related, any wind parameters can currently not be controlled from the local weather algorithms.

2. Installation

@@ -49,13 +49,13 @@ 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. 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). 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.

-Clouds are placed in a constant altitude alt (this is going to be changed in the future) 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. 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 barrier cloud system

@@ -91,13 +91,18 @@ The second menu is used to place complete weather tiles based on low-level calls The dropdown menu is used to select the type of weather tile to build. The menu contains two groups of tiles - the first six are classified by airmass, whereas the last two are scenarios intended for soaring. In addition, two parameters can be entered. The first is the tile orientation. Some tiles, most notably incoming fronts, have a direction along which the weather changes. The tiles are set up in such a way that fronts come from north, changing orientation rotates the whole tile to the specified direction. As soon as wind is implemented in the local weather system, the tile orientation will essentially also govern the wind direction (clearly, there is a relation between from where a front comes and the wind direction). Currently, the functionality of tile orientation is there, but mostly untested and at the moment not particularly useful.

-The second parameter, the altitude offset, is as of now a provisorium. Cloud layer placement calls are specified for absolute altitudes and calibrated at sea level. As a result, layers are placed too low in mountainous terrain. Eventually, the system is to receive a terrain presampling function to determine just where exactly low cloud layers should be placed when a weather tile is set up. Until this is in place, the user must manually specify a suitable altitude offset for all cloud layers.

+The second 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.

-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. Currently this change between airmasses does not include transitions across fronts. Moreover, it does not cover arctic or tropical weather conditions - those will be covered in a future release. Note that 'realistic weather' does not work for the two soaring scenarios.

+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. Currently this change between airmasses does not include transitions across fronts. Moreover, it does not cover transitions to arctic or tropical weather conditions - those will be covered in a future release. Note that 'realistic weather' does not work for the two soaring scenarios, 'repeat tile' does not work for any tile which is part of a front.

-The menu then contains four options. 'Terrain presampling' is currently not yet functional. 'Worker threads' is an option to distribute the work flow. Usually, the local weather package will compute a task till it is done before starting the next. Thus, creating a new weather tile may lead to a few seconds freeze, before Flightgear continues normally. With 'worker threads' selected, computations will be split across several frames. The advantage is that Flightgear stays responsive during loading and unloading of weather tiles, and in general the flight continues smoothly, albeit with reduced framerate. However, selecting this option does not guarantee that an operation is finished by the time another is beginning - thus there may be situations in which the loading of a new tile blocks unloading of an old one and so on, in essence leading to processes competing for access to the weather array, resulting in an extended period of very low framerates. Dependent on system performance, this may or may not be acceptable to the user. 'asymmetric range' is an experimental performance-improving option (see below). Finally, '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 a (significant) loss of framerate. Rendering multiple tiles of dense Cumulus development with detailed clouds will quite possibly freeze even a powerful system.

+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.

+ +Below the menu are four 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.

+'Worker threads' is an option to distribute the work flow. Usually, the local weather package will compute a task till it is done before starting the next. Thus, creating a new weather tile may lead to a few seconds freeze, before Flightgear continues normally. With 'worker threads' selected, computations will be split across several frames. The advantage is that Flightgear stays responsive during loading and unloading of weather tiles, and in general the flight continues smoothly, albeit with reduced framerate. However, selecting this option does not guarantee that an operation is finished by the time another is beginning - thus there may be situations in which the loading of a new tile blocks unloading of an old one and so on, in essence leading to processes competing for access to the weather array, resulting in an extended period of very low framerates. Dependent on system performance, this may or may not be acceptable to the user. 'asymmetric range' is an experimental performance-improving option (see below). Finally, '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 a (significant) loss of framerate. Rendering multiple tiles of dense Cumulus development with detailed clouds will quite possibly freeze even a powerful system.

+ +The slider 'Thermal properties' is only 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. Unless thermals are placed, no other weather tile is affected by the settings.

The following pictures show the results of tile setups 'Low-pressure-border' and 'High-pressure-border':

@@ -119,6 +124,12 @@ The package contains a number of different cloud models, both static ones for Ci These are rendered by a different technique: While the default Cumulus models consist of multiple layers rotated around the center of the model, the detailed Cumulus clouds consist of multiple (up to 24) individual cloudlets, rotating each around its own center, randomly distributed into a box. This not only improves the visual appearance, but also leads to a more realistic distribution of cloud sizes and shapes in the sky. In addition, when circling below the cloud (as done when soaring) the effect of the cloudlet rotation is less pronounced. The price to pay is that rendering detailed clouds costs about a factor 4 more performance, so they may not be suitable for all systems.

+More complex clouds are rendered in sandwitched layers of several different textures. An example are Cumulonimbus towers, which use diffuse textures on the bottom, changing to more structured textures in the upper part of the cloud. With up to 2000 cloudlets, skies with multiple thunderstorms may not render with sufficient framerates on every system.

+ +

+ +

+ The general problem is finding a good balance between spending a lot of CPU time to make a single cloud model appear perfect, and the performance degradation that occurs if hundreds of clouds are placed in the sky. The basic aim is to provide realistic appearance for clouds from a standard view position (in cockpit looking forward), to retain acceptable appearance from other positions and to allow large cloud layers.

@@ -171,9 +182,11 @@ 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).

+

7. 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. Finally, all necessary loops must be started. 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.

+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.

The first important call sets up the conditions to be interpolated:

@@ -193,7 +206,7 @@ With default settings, the local weather package generates a 40x40 km weather ti * a further reduction in the amount of simultaneously generated tiles can be achieved by changing the ranges. These are exposed in the property tree as local-weather/config/distance-to-load-tile-m and local-weather/config/distance-to-remove-tile-m. Note that the removal range must be larger than the loading range - otherwise just generated tiles will be immediately unloaded! Ranges below 20 km will guarantee that only one tile is loaded at a time, but will create empty spots when no tile is loaded. A range above 28 km will guarantee that the aircraft never flies in an empty tile, but empty sky in front will be visible. Finally, ranges above 56 km guarantee that all 9 tiles (a region of 120x120 km) are managed at all times - but will most likely cause a severe drop in framerate for most scenarios.

-* if this does not help, try avoiding scenarios with large cloud count. As a rule, low pressure areas have high cloud count, high pressure areas have a low cloud count.

+* if this does not help, try avoiding scenarios with large cloud count. As a rule, low pressure areas have high cloud count, high pressure areas have a low cloud count. Do not use 'detailed clouds', which tend to generate large cloud counts.

* a drastic solution is to set the visual ranges lower. Currently, cloud models are set to be visible up to 30 km. Changing the visibility range in the range animation of all cloud model xml wrappers will improve performance accordingly. To achieve a nice fading into the background instead of a sudden disappearance, it is recommended to adjust the visibility range in the shaders accordingly. It would probably be good to expose the visual range as a property, but currently it's not yet done, as passing a property to the shader requires Flightgear CVS and cannot be done in 2.0.0.

@@ -209,18 +222,130 @@ Performance for overcast layers currently is a limiting issue and there are a fe * 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, loading and unloading weather tiles may take a long time / cause severe drops in framerate. Please refer to performance tuning to solve such problems. In general, overcast layers 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. 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.

* The thermals in the soaring scenarios need a CVS patch to work.

-

10. Credits

+

Appendix A: An introduction to the algorithms

+ +This section describes the more complicated cloud placement algorithms in some detail. It is intended for readers who are interested in understanding (and possibly modifying) what creates the weather they get to see. + +

The convective algorithm and the properties of thermals

+ +The convective algorithm is used to place Cumulus clouds as well as thermals. Thermals are by default not placed to save CPU time unless a tile designed for soaring is selected, but they can be generated for any weather tile by setting local-weather/tmp/generate-thermal-lift-flag to either 1 (constant-strength thermals) or 2 (detailed thermal model).

+ +At the core of the convective algorithm is the concept of locally available thermal energy. The source of this energy is solar radiation. The flux of solar energy depends on the angle of incident sunlight with the terrain surface. It is possibly (though computationally very expensive) to compute this quantity, but the algorithm uses a proxy instead. The daily angle of the sun at the equator assuming flat terrain is modelled as 0.5 * (1.0-cos(t/24.0*2pi)) with t expressed in hours, a function that varies between zero at midnight and 1 at noon. There is a geographical correction to this formula which goes with cos(latitude), taking care of the fact that the sun does not reach the zenith at higher latitudes. Both the yearly summer/winter variation of the solar position in the sky and the terrain slope are neglected.

+ +However, the incident energy does not equal the available energy, since some of this energy is reflected back into space, either by high clouds, or by the terrain itself. The reflection by high clouds is not explicitly included in the algorithm - but since in creating a weather tile, one must setup both the high altitude clouds and the convective system, it can easily be included approximately by calling the convective system with a strength that is reduced according to the density of high-altitude clouds. The reflection by the terrain is encoded in the probability p that a given landcover will lead to a thermal. p ranges from 0.35 for rock or concrete surface which heat very well in the sun to 0.01 over water or ice which reflect most of the energy back into space.

+ +The algorithm now tries to place a number n of clouds in a random position where n is a function of the user-specified strength of development, modified by the daily and geographical factors as described above. However, a cloud is only placed at a position with probability p, so a call to the convective system over city terrain will lead to significantly more clouds than a call with the same strength over water.

+ +The next task is to determine how the available thermal energy is released in convection across different thermals. There can be for example many weak thermals, or few strong thermals for the same energy. The empirical observation is that the number of thermals and clouds peaks around noon, whereas the strength of thermals peaks in the afternoon. The algorithm thus assigns a strength 1.5 * rand() + (2.0 * p)) to each cloud, which is again modified by a sinusoidal function with a peak shifted from noon to 15:30.

+ +Based on this strength parameter s, a cloud model is chosen, and the maximal thermal lift (in ft/s) is calculated as 3 + 1 * (s -1) (note that this means that not every cloud is associated with lift). By default, the radius of thermals is assumed to range from 500 to 1000 m. The slider 'thermal properites' in the menu allows to modify the balance between radius and lift from these values. Since the flow profile in a thermal is approximately quadratic, requiring the same flux means that increasing the maximal lift by a factor f leads to a radius reduced by 1/f. Moving the thermal properties slider to 'rough day' thus generates narrow thermals with large maximal lift and sink (which are more difficult to fly), moving it to low convection instead generates large thermals with weak lift.

+ +The following series of pictures, taken over KLSV (Nellis AFB, Las Vegas) illustrates the algorithm at work.

+ +At 7:00 am, the thermal activity is weak, and there is no lift available in thermals yet.

+ +

+ +

+ +Some activity starts around 10:00 am the average available lift is 0.3 m/s, the more active clouds tend to be above city terrain. + +

+ +

+ +At 12:00 noon, the maximal cloud number is reached. The average available lift is 1 m/s, in peaks up to 2 m/s. + +

+ +

+ +The maximum of lift strength is reached close to 15:00 pm. The average lift is now 1.5 m/s, in peaks up to 3 m/s, and the strong convection leads to beginning overdevelopment, some clouds reach beyond the first inversion layer and tower higher up. At this point, the clouds may also overdevelop into a thunderstorm (which is not modelled explicitly by the convective algorithm as it requires somewhat different modelling, but is taken into account in the weather tiles).

+ +

+ +

+ +At 17:30 pm, the lift is still strong, 1.5 m/s on average and 2.5 m/s in peaks, but compared with the situation at noon, there are fewer clouds with stronger lift.

+ +

+ +

+ +At sunset around 19:00 pm, the number of clouds decreases quickly, but there is still a lot of residual thermal energy (the ground has not cooled down yet), therefore thermal lift of on average 1 m/s is still available even without solar energy input. + +

+ +

+ +While not accurate in every respect, the model works fairly well to reproduce the actual time dependence of convective clouds and thermal lift during the day.

+ +

The terrain presampling and cloud altitude determination algorithm

+ +While the meaning of a cloud layer altitude is rather obvious in level terrain, this quickly becomes a highly non-trivial question in mountaineous terrain where the elevation of the terrain is more difficult to define. Observation of weather patterns in mountain regions suggests that clouds follow changes in terrain elevation to some degree, but not all cloud types do to the same degree. While convective clouds follow a change in elevation more readily even on small distance scales, layered clouds don't do so. The purpose of the terrain presampling and cloud altitude determination algorithm is to capture this behaviour as closely as possible.

+ +In nature, what determines the altitude of various clouds are the properties of air layers. In general, clouds become visible at the condensation altitude, i.e. when temperature and dew point merge and the relative humidity of air exceeds 100%. In conditions where there is a lot of vertical air movement (i.e. for Cumulus clouds), the conditions are much more local than in situations with lack of vertical movement (i.e. for layered clouds).

+ +In the algorithm, various proxies for the structure of air layers and hence the condensation altitude are used. It is assumed that air layers must follow the general slope of the terrain (because there is nowhere else to go), but can (at least to some degree) flow around isolated obstacles. To get the general layout of the terrain, the algorithm first samples the altitude of an 80x80 km square around the 40x40 weather tile to be created. The choice of a larger sampling area reduces the sensitvity of the outcome to purely local terrain features and prevent pronounced transitions from one tile to the next. The result of this sampling is a distribution of probability to find the terrain at a given altitude:

+ +

+ +

+ +For instance, the terrain around Geneva is mostly flat around 1000 ft (where the peak of the distribution lies) with some mountains up to 4500 ft nearby. Based on such distributions, the algorithm next determines the minimum altitude alt_min, the maximum altitude alt_max, the altitude below which 20% of the terrain are found alt_20 and the median altitude below which 50% of the terrain are found alt_med.

+ +Cumulus clouds are always placed at a constant altitude above alt_20. This is done to ensure gorges and canyons do not provide a minimum in otherwise flat terrain so that clouds appear down in the gorge as opposed to on the rim where they would naturally occur. Basically, layers are assumed not to trace too fine structures in the terrain, so at least 20% of the terrain are required. In the test case of Grand Canyon, the algorithm correctly places the clouds at rim altitude rather than down in the canyon: + +

+ +

+ +However, convective clouds are given some freedom to adjust to the terrain. The maximally possible upward shift is given by alt_med - alt_20. This is based on the notion that above alt_med, the terrain is not a significant factor any more because the air can simply flow around any obstacle. However, this maximal shift is not always used - if the cloud is placed far above the terrain in the first place, it would not follow the terrain much. Thus, a factor of 1000 ft / altitude above terrain, required to be between 0 and 1, modifies the shift. As a result, a cloud layer placed high above the terrain has no sensitivity to terrain features. The result of this procedure is that clouds show some degree of following terrain elevation, as seen here in Grenoble

+ +

+ +

+ +but they do not follow all terrain features, especially not single isolated peaks as seen here at the example of Mt. Rainier: + +

+ +

+ +Finally, layered clouds have essentially no capability to shift with terrain elevation. Moreover, they are caused by large-scale weather processes, hence they do not usually shift upward over even large mountain massives. Currently, the model places them at 0.5 * (alt_min + alt_20) base altitude in order to retain, even in mountains, the sensitivity to the flat terrain surrounding the massiv. usually this works well, but may have a problem with gorges in flat terrain. The following picture shows a Nimbostratus layer close to Grenoble:

+ +

+ +

+ +

The offline large scale weather pattern

+ +The local weather package generates semi-plausible weather changes even in the absence of METAR information. These weather patterns are encoded in an algorithm governing the rules which weather tiles can have common borders.

+ +Weather tiles are classified chiefly by air pressure. What is currently in the models are three classes for a low pressure system, four different classes for the system of weather fronts and airmasses spiralling into the low pressure system and three classes for a hugh pressure system. The general rule is that low pressure tiles contain layered clouds, overcast skies and rain whereas the high pressure tiles contain clear skies and few convective clouds. The topology assumed for the weather system is apparent in the following diagram: + +

+ +

+ +A transition between classes is possible whenever a class has a common border. However, if a transition actually takes place is probabilistic. Typically, the probability not to make a transition is about 80%. Since changes are only triggered for weather tiles one is actually in, the average distance over which weather patterns persist is 160 km. An exception to this are fronts - weather front tiles trigger changes based on direction rather than probability, so a warmfront will always be a sequence of 4 tiles, a coldfront will always be a small-scale phenomenon crossed within 30 km. + +To avoid unrealistically large changes in pressure when generating a transition and randomly sampling central pressure in tiles from two different pressure classes, a monitoring algorithm limits the pressure difference between tiles to 2 mbar and ensures a slow transition from high pressure to low pressure regions.

+ +The weather algorithm is currently not able to handle the transition to tropical weather in the Hadley cell close to the equator, although tropical weather exists as a standalone tile and can be used in repetitive mode.

+ +

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.

-Thorsten Renk, May 2010 +Thorsten Renk, June 2010 diff --git a/Docs/cloud_altitude_01.jpg b/Docs/cloud_altitude_01.jpg new file mode 100644 index 000000000..2653567e9 Binary files /dev/null and b/Docs/cloud_altitude_01.jpg differ diff --git a/Docs/cloud_altitude_02.jpg b/Docs/cloud_altitude_02.jpg new file mode 100644 index 000000000..41c293d46 Binary files /dev/null and b/Docs/cloud_altitude_02.jpg differ diff --git a/Docs/cloud_altitude_03.jpg b/Docs/cloud_altitude_03.jpg new file mode 100644 index 000000000..3b1d6afe0 Binary files /dev/null and b/Docs/cloud_altitude_03.jpg differ diff --git a/Docs/cloud_altitude_04.jpg b/Docs/cloud_altitude_04.jpg new file mode 100644 index 000000000..ad2561a4a Binary files /dev/null and b/Docs/cloud_altitude_04.jpg differ diff --git a/Docs/clouds-tropical02.jpg b/Docs/clouds-tropical02.jpg new file mode 100644 index 000000000..a9a7c927a Binary files /dev/null and b/Docs/clouds-tropical02.jpg differ diff --git a/Docs/menu2.jpg b/Docs/menu2.jpg index ba50407bb..d377b7a7d 100644 Binary files a/Docs/menu2.jpg and b/Docs/menu2.jpg differ diff --git a/Docs/terrain1.jpg b/Docs/terrain1.jpg new file mode 100644 index 000000000..5cf97e654 Binary files /dev/null and b/Docs/terrain1.jpg differ diff --git a/Docs/weather_patterns.jpg b/Docs/weather_patterns.jpg new file mode 100644 index 000000000..34285e3de Binary files /dev/null and b/Docs/weather_patterns.jpg differ diff --git a/Nasal/local_weather.nas b/Nasal/local_weather.nas index 3f9609731..baf5722d2 100644 --- a/Nasal/local_weather.nas +++ b/Nasal/local_weather.nas @@ -31,6 +31,8 @@ # create_cloud to place a single cloud into the scenery # create_cloud_vec to place a single cloud into an array to be written later # clear_all to remove all clouds, effect volumes and weather stations and stop loops +# create_detailed_cumulus_cloud to place multiple cloudlets into a box based on a size parameter +# create_cumulonimbus_cloud to place multiple cloudlets into a box # create_cumosys wrapper to place a convective cloud system based on terrain coverage # cumulus_loop to place 25 Cumulus clouds each frame # create_cumulus to place a convective cloud system based on terrain coverage @@ -183,6 +185,7 @@ if (i == esize) # we check the number of actives and reset all counters vNode.getChild("number-active-snow").setValue(0); vNode.getChild("number-active-rain").setValue(0); vNode.getChild("number-active-lift").setValue(0); + vNode.getChild("number-active-turb").setValue(0); } #print("n_active: ", active_counter); active_counter = 0; i = 0; @@ -246,12 +249,13 @@ var T = sum_T/sum_norm; # a simple altitude model for visibility - increase it with increasing altitude -vis = vis + 0.3 * getprop("position/altitude-ft"); +vis = vis + 0.5 * getprop("position/altitude-ft"); 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); # now check if an effect volume writes the property and set only if not @@ -262,6 +266,14 @@ if ((flag ==0) and (vis > 0.0)) setVisibility(vis); } +flag = props.globals.getNode("local-weather/effect-volumes/number-active-turb").getValue(); +if ((flag ==0)) + { + cNode.getNode("turbulence").setValue(0.0); + setTurbulence(0.0); + } + + flag = props.globals.getNode("local-weather/effect-volumes/number-active-lift").getValue(); if (flag ==0) { @@ -403,6 +415,16 @@ if (ev.getNode("effects/snow-flag", 1).getValue()==1) setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")+1); } +if (ev.getNode("effects/turbulence-flag", 1).getValue()==1) + { + var turbulence = ev.getNode("effects/turbulence").getValue(); + ev.getNode("restore/turbulence",1).setValue(cNode.getNode("turbulence").getValue()); + cNode.getNode("turbulence").setValue(turbulence); + setTurbulence(turbulence); + ev.getNode("restore/number-entry-turb",1).setValue(getprop(lw~"effect-volumes/number-active-turb")); + setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")+1); + } + if (ev.getNode("effects/thermal-lift-flag", 1).getValue()==1) { var lift = ev.getNode("effects/thermal-lift").getValue(); @@ -475,6 +497,18 @@ if (ev.getNode("effects/snow-flag", 1).getValue()==1) setprop(lw~"effect-volumes/number-active-snow",getprop(lw~"effect-volumes/number-active-snow")-1); } +if (ev.getNode("effects/turbulence-flag", 1).getValue()==1) + { + var n_active = getprop(lw~"effect-volumes/number-active-turb"); + var n_entry = ev.getNode("restore/number-entry-turb").getValue(); + if (n_active ==1){var turbulence = props.globals.getNode(lw~"interpolation/turbulence").getValue();} + else if ((n_active -1) == n_entry) {var turbulence = ev.getNode("restore/turbulence").getValue();} + else {var turbulence = cNode.getNode("turbulence").getValue();} + cNode.getNode("turbulence").setValue(turbulence); + setTurbulence(turbulence); + setprop(lw~"effect-volumes/number-active-turb",getprop(lw~"effect-volumes/number-active-turb")-1); + } + if (ev.getNode("effects/thermal-lift-flag", 1).getValue()==1) { var n_active = getprop(lw~"effect-volumes/number-active-lift"); @@ -545,6 +579,34 @@ setprop("environment/clouds/layer[0]/elevation-ft", 30000.0); setprop("environment/metar/snow-norm",snow); } + +#################################### +# set turbulence to given value +#################################### + +var setTurbulence = func (turbulence) { + +# this is a rather dirty workaround till a better solution becomes available +# essentially we update all entries in config and reinit environment + +var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry"); +foreach (var e; entries_aloft) { + e.getNode("turbulence/magnitude-norm",1).setValue(turbulence); + } + +# turbulence is slightly reduced in boundary layers + +var entries_boundary = props.globals.getNode("environment/config/boundary", 1).getChildren("entry"); +var i = 1; +foreach (var e; entries_boundary) { + e.getNode("turbulence/magnitude-norm",1).setValue(turbulence * 0.25*i); + i = i + 1; + } +fgcommand("reinit", props.Node.new({subsystem:"environment"})); + +} + + #################################### # set temperature to given value #################################### @@ -813,7 +875,14 @@ else if (type == "Cumulus (cloudlet)"){ else if (rn > 0.6) {path = "Models/Weather/cumulus_sl8.xml";} else if (rn > 0.4) {path = "Models/Weather/cumulus_sl9.xml";} else if (rn > 0.2) {path = "Models/Weather/cumulus_sl10.xml";} + #else if (rn > 0.1) {path = "Models/Weather/cumulus_hires_small1.xml";} else {path = "Models/Weather/cumulus_sl11.xml";} + #if (rn > 0.83) {path = "Models/Weather/cumulus_hires_small1.xml";} + #else if (rn > 0.664) {path = "Models/Weather/cumulus_hires_small2.xml";} + #else if (rn > 0.5) {path = "Models/Weather/cumulus_hires_small3.xml";} + #else if (rn > 0.332) {path = "Models/Weather/cumulus_hires_small4.xml";} + #else if (rn > 0.166) {path = "Models/Weather/cumulus_hires_small5.xml";} + #else if (rn > 0.0) {path = "Models/Weather/cumulus_hires_small6.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/cumulus_sl1.xml";} @@ -821,6 +890,28 @@ else if (type == "Cumulus (cloudlet)"){ else if (rn > 0.4) {path = "Models/Weather/cumulus_sl3.xml";} else if (rn > 0.2) {path = "Models/Weather/cumulus_sl4.xml";} else {path = "Models/Weather/cumulus_sl5.xml";} + #if (rn > 0.75) {path = "Models/Weather/cumulus_hires1.xml";} + #else if (rn > 0.5) {path = "Models/Weather/cumulus_hires2.xml";} + #if (rn > 0.0) {path = "Models/Weather/cumulus_hires3a.xml";} + #else if (rn > 0.0) {path = "Models/Weather/cumulus_hires4.xml";} + #else if (rn > 0.0) {path = "Models/Weather/cumulus_hires5.xml";} + } + + } +else if (type == "Cumulonimbus (cloudlet)"){ + if (subtype == "small") { + if (rn > 0.8) {path = "Models/Weather/cumulonimbus_sl1.xml";} + else if (rn > 0.6) {path = "Models/Weather/cumulonimbus_sl2.xml";} + else if (rn > 0.4) {path = "Models/Weather/cumulonimbus_sl3.xml";} + else if (rn > 0.2) {path = "Models/Weather/cumulonimbus_sl4.xml";} + else {path = "Models/Weather/cumulonimbus_sl5.xml";} + } + else if (subtype == "large") { + if (rn > 0.8) {path = "Models/Weather/cumulonimbus_sl1.xml";} + else if (rn > 0.6) {path = "Models/Weather/cumulonimbus_sl2.xml";} + else if (rn > 0.4) {path = "Models/Weather/cumulonimbus_sl3.xml";} + else if (rn > 0.2) {path = "Models/Weather/cumulonimbus_sl4.xml";} + else {path = "Models/Weather/cumulonimbus_sl5.xml";} } } @@ -888,6 +979,20 @@ else if (type == "Cirrocumulus") { else {path = "Models/Weather/cirrocumulus4.xml";} } } +else if (type == "Cirrocumulus (cloudlet)") { + if (subtype == "small") { + if (rn > 0.75) {path = "Models/Weather/cirrocumulus_hires1.xml";} + else if (rn > 0.5) {path = "Models/Weather/cirrocumulus_hires2.xml";} + else if (rn > 0.25) {path = "Models/Weather/cirrocumulus_hires3.xml";} + else {path = "Models/Weather/cirrocumulus_hires4.xml";} + } + else if (subtype == "large") { + if (rn > 0.75) {path = "Models/Weather/cirrocumulus_hires1.xml";} + else if (rn > 0.5) {path = "Models/Weather/cirrocumulus_hires2.xml";} + else if (rn > 0.25) {path = "Models/Weather/cirrocumulus_hires3.xml";} + else {path = "Models/Weather/cirrocumulus_hires4.xml";} + } + } else if (type == "Nimbus") { if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/nimbus_sls1.xml";} @@ -982,6 +1087,16 @@ else if (type == "Fog (thick)") { else {path = "Models/Weather/stratus_thick5.xml";} } } +else if (type == "Test") {path="Models/Weather/test.xml";} +else if (type == "Box_test") { + if (rn > 0.8) {path = "Models/Weather/test1.xml";} + else if (rn > 0.6) {path = "Models/Weather/test2.xml";} + else if (rn > 0.4) {path = "Models/Weather/test3.xml";} + else if (rn > 0.2) {path = "Models/Weather/test4.xml";} + else {path = "Models/Weather/test5.xml";} + + } + else {print("Cloud type ", type, " subtype ",subtype, " not available!");} @@ -1087,6 +1202,14 @@ cloudNode.getNode("cloud-number",1).setValue(0); props.globals.getNode("local-weather/effect-volumes", 1).removeChildren("effect-volume"); +# clear weather stations + +props.globals.getNode("local-weather/interpolation", 1).removeChildren("station"); + +# reset pressure continuity + +weather_tiles.last_pressure = 0.0; + # stop the effect loop and the interpolation loop, make sure thermal generation is off setprop(lw~"effect-loop-flag",0); @@ -1108,22 +1231,26 @@ setprop(lw~"effect-volumes/effect-placement-index",0); setprop(lw~"tiles/tile-counter",0); + } ########################################################### -# detailed Cumulus clouds created from multiple sprites +# detailed Cumulus clouds created from multiple cloudlets ########################################################### var create_detailed_cumulus_cloud = func (lat, lon, alt, size) { #print(size); -if (size>2.0) +if (size > 2.0) + {create_cumulonimbus_cloud(lat, lon, alt, size); return;} + +else if (size>1.5) { - var height = 1200; - var n = 24; - var x = 800.0; + var height = 1000; + var n = 30; + var x = 700.0; var y = 300.0; var edge = 0.3; } @@ -1154,6 +1281,20 @@ randomize_pos("Cumulus (cloudlet)",height,x,y,alpha); } +########################################################### +# detailed small Cumulonimbus clouds created from multiple cloudlets +########################################################### + +var create_cumulonimbus_cloud = func(lat, lon, alt, size) { + +var height = 3000.0; +var alpha = rand() * 180.0; + +create_streak("Cumulonimbus",lat,lon, alt+ 0.5* height,8,0.0,0.0,1,0.0,0.0,alpha,1.0); +randomize_pos("Cumulonimbus",height,1600.0,800.0,alpha); + +} + ########################################################### # wrappers for convective cloud system to distribute # call across several frames if needed @@ -1163,7 +1304,7 @@ var create_cumosys = func (blat, blon, balt, nc, size) { # realistic Cumulus has somewhat larger models, so compensate to get the same coverage if (getprop(lw~"config/detailed-clouds-flag") == 1) - {nc = int(0.5 * nc);} + {nc = int(0.7 * nc);} if (getprop(lw~"tmp/thread-flag") == 1) {setprop(lw~"tmp/convective-status", "computing"); @@ -1204,7 +1345,9 @@ var place_lift_flag = 0; var strength = 0.0; var detail_flag = getprop(lw~"config/detailed-clouds-flag"); -var sec_to_rad = 2.0 * math.pi/86400; +var alpha = getprop(lw~"tmp/tile-orientation-deg") * math.pi/180.0; # the tile orientation + +var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime calc_geo(blat); @@ -1217,12 +1360,20 @@ t = t + getprop("sim/time/local-offset"); # and make a simple sinusoidal model of thermal strength -var t_factor1 = 0.5 * 1.0-math.cos((t * sec_to_rad)); -var t_factor2 = 0.5 * 1.0-math.cos((t * sec_to_rad)-0.52); +# daily variation in number of thermals, peaks at noon +var t_factor1 = 0.5 * (1.0-math.cos((t * sec_to_rad))); -# print("t-factor is now: ",t_factor); +# daily variation in strength of thermals, peaks around 15:30 +var t_factor2 = 0.5 * (1.0-math.cos((t * sec_to_rad)-0.9)); -nc = t_factor1 * nc * math.cos(blat/180.0*math.pi); +#print("t-factor1 is now: ",t_factor1, " ",t_factor2); + +# number of possible thermals equals overall strength times daily variation times geographic variation +# this is a proxy for solar thermal energy + +nc = t_factor1 * nc * math.cos(blat/180.0*math.pi); + +var thermal_conditions = getprop(lw~"config/thermal-properties"); while (i < nc) { @@ -1231,62 +1382,84 @@ while (i < nc) { place_lift_flag = 0; strength = 0.0; + # pick a trial position inside the tile and rotate by tile orientation angle var x = (2.0 * rand() - 1.0) * size; var y = (2.0 * rand() - 1.0) * size; - var lat = blat + y * m_to_lat; - var lon = blon + x * m_to_lon; + var lat = blat + (y * math.cos(alpha) - x * math.sin(alpha)) * m_to_lat; + var lon = blon + (x * math.cos(alpha) + y * math.sin(alpha)) * m_to_lon; + + # now check ground cover type on chosen spot var info = geodinfo(lat, lon); if (info != nil) { + var elevation = info[0] * m_to_ft; if (info[1] != nil){ var landcover = info[1].names[0]; if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];} else {print(p, " ", info[1].names[0]);} }} + # then decide if the thermal energy at the spot generates an updraft and a cloud - if (rand() < p) + if (rand() < p) # we decide to place a cloud at this spot { - if (rand() + (2.0 * p) > 1.0) + strength = (1.5 * rand() + (2.0 * p)) * t_factor2; # the strength of thermal activity at the spot + if (strength > 1.0) { - path = select_cloud_model("Cumulus","large"); place_lift_flag = 1; strength=1.0; + # we place a large cloud, and we generate lift + path = select_cloud_model("Cumulus","large"); place_lift_flag = 1; } else {path = select_cloud_model("Cumulus","small");} - if (getprop(lw~"tmp/generate-thermal-lift-flag") != 3) # see if we produce blue thermals + # check if we have a terrain elevation analysis available and can use a + # detailed placement altitude correction + + if (getprop(lw~"tmp/presampling-flag") == 1) + { + var place_alt = get_convective_altitude(balt, elevation); + } + else {var place_alt = balt;} + + + if (getprop(lw~"tmp/generate-thermal-lift-flag") != 3) # no clouds if we produce blue thermals { if (getprop(lw~"tmp/thread-flag") == 1) { - if (detail_flag == 0){create_cloud_vec("Cumulus",path,lat,lon, balt, 0.0, 0);} - else {create_detailed_cumulus_cloud(lat, lon, balt, p + strength+rand());} + if (detail_flag == 0){create_cloud_vec("Cumulus",path,lat,lon, place_alt, 0.0, 0);} + else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);} } else { - if (detail_flag == 0){create_cloud("Cumulus", path, lat, lon, balt, 0.0, 0);} - else {create_detailed_cumulus_cloud(lat, lon, balt, p + strength+rand());} + if (detail_flag == 0){create_cloud("Cumulus", path, lat, lon, place_alt, 0.0, 0);} + else {create_detailed_cumulus_cloud(lat, lon, place_alt, strength);} } } # now see if we need to create a thermal - first check the flag - if (getprop(lw~"tmp/generate-thermal-lift-flag") == 1) + if (getprop(lw~"tmp/generate-thermal-lift-flag") == 1) # thermal by constant { # now check if convection is strong if (place_lift_flag == 1) { - var lift = 3.0 + 20.0 * p * rand(); + var lift = 3.0 + 10.0 * (strength -1.0); var radius = 500 + 500 * rand(); - create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, balt+500.0, -1, -1, -1, -1, lift, 1); + #print("Lift: ", lift * ft_to_m - 1.0); + create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, -1, lift, 1); } # end if place_lift_flag } # end if generate-thermal-lift-flag - else if ((getprop(lw~"tmp/generate-thermal-lift-flag") == 2) or (getprop(lw~"tmp/generate-thermal-lift-flag") == 3)) + else if ((getprop(lw~"tmp/generate-thermal-lift-flag") == 2) or (getprop(lw~"tmp/generate-thermal-lift-flag") == 3)) # thermal by function { if (place_lift_flag == 1) { - var lift = 3.0 + 20.0 * p * rand(); - var radius = 500 + 500 * rand(); - create_effect_volume(1, lat, lon, 1.2*radius, 1.2*radius, 0.0, 0.0, balt+500.0, -1, -1, -1, -1, lift, -2); + #var lift = 3.0 + 20.0 * p * rand(); + #var radius = 500 + 500 * rand(); + var lift = (3.0 + 10.0 * (strength -1.0))/thermal_conditions; + var radius = (500 + 500 * rand())*thermal_conditions; + #print("Lift: ", lift * ft_to_m - 1.0, " strength: ",strength); + + create_effect_volume(1, lat, lon, 1.1*radius, 1.1*radius, 0.0, 0.0, place_alt+500.0, -1, -1, -1, lift*0.02, lift, -2); } # end if place_lift_flag } # end if generate-thermal-lift-flag @@ -1299,42 +1472,63 @@ while (i < nc) { } ########################################################### -# terrain sampling +# place a Cumulus layer with excluded regions +# to avoid placing cumulus underneath a thunderstorm ########################################################### -var terrain_presampling = func { +var cumulus_exclusion_layer = func (blat, blon, balt, n, size_x, size_y, alpha, s_min, s_max, n_ex, exlat, exlon, exrad) { -var size = 20000.0; -var blat = getprop("position/latitude-deg"); -var blon = getprop("position/longitude-deg"); -var elevation = 0.0; -var n=[]; -setsize(n,20); -calc_geo(blat); +var strength = 0; +var flag = 1; +var phi = alpha * math.pi/180.0; -for(j=0;j<20;j=j+1){n[j]=0;} +var detail_flag = getprop(lw~"config/detailed-clouds-flag"); -for (i=0; i<1000; i=i+1) +if (detail_flag == 1) {var i_max = int(0.25*n);} else {var i_max = int(1.0*n);} + + + +for (var i =0; i< i_max; i=i+1) { - var x = (2.0 * rand() - 1.0) * size; - var y = (2.0 * rand() - 1.0) * size; + var x = (2.0 * rand() - 1.0) * size_x; + var y = (2.0 * rand() - 1.0) * size_y; - var lat = blat + y * m_to_lat; - var lon = blon + x * m_to_lon; + var lat = blat + (y * math.cos(phi) - x * math.sin(phi)) * m_to_lat; + var lon = blon + (x * math.cos(phi) + y * math.sin(phi)) * m_to_lon; - var info = geodinfo(lat, lon); - if (info != nil) {elevation = info[0] * m_to_ft;} - for(j=0;j<20;j=j+1){if (elevation < 500.0 * (j+1)) - {n[j] = n[j]+1; break;}} + flag = 1; + + for (var j=0; j 1.0) {var path = select_cloud_model("Cumulus","large"); } + else {var path = select_cloud_model("Cumulus","small");} + + if (getprop(lw~"tmp/thread-flag") == 1) + { + if (detail_flag == 0){create_cloud_vec("Cumulus",path,lat,lon, balt, 0.0, 0);} + else {create_detailed_cumulus_cloud(lat, lon, balt, strength);} + } + else + { + if (detail_flag == 0){create_cloud("Cumulus", path, lat, lon, balt, 0.0, 0);} + else {create_detailed_cumulus_cloud(lat, lon, balt, strength);} + } + + } # end if flag + + } # end for i -for (i=0;i<20;i=i+1){print(500.0*i," ",n[i]);} } - ########################################################### # place a barrier cloud system ########################################################### @@ -1542,6 +1736,37 @@ if (rainflag ==1){ } # end if (rainflag ==1) } + +########################################################### +# place a cloud box +########################################################### + + +var create_cloudbox = func (type,subtype, blat, blon, balt, dx,dy,dz,n) { + +var phi = 0; + +for (var i=0; i int(0.5 *n_tot)) {alt_med = i * 500.0; break;} + } + +sum = 0; +for (var i=0; i<20;i=i+1) + { + sum = sum + terrain_n[i]; + if (sum > int(0.3 *n_tot)) {alt_20 = i * 500.0; break;} + } + + +for (var i=0; i<20;i=i+1) {alt_mean = alt_mean + terrain_n[i] * i * 500.0;} +alt_mean = alt_mean/n_tot; + +for (var i=0; i<20;i=i+1) {if (terrain_n[i] > 0) {alt_min = i * 500.0; break;}} + +var n_max = 0; +sum = 0; + +for (var i=0; i<19;i=i+1) + { + sum = sum + terrain_n[i]; + if (terrain_n[i] > n_max) {n_max = terrain_n[i];} + if ((n_max > terrain_n[i+1]) and (sum > int(0.3*n_tot))) + {alt_low_min = i * 500; break;} + } + +print("Terrain presampling analysis results:"); +print("total: ",n_tot," mean: ",alt_mean," median: ",alt_med," min: ",alt_min, " alt_20: ", alt_20); + +#if (alt_low_min < alt_med) {alt_offset = alt_low_min;} +#else {alt_offset = alt_med;} + +setprop(lw~"tmp/tile-alt-offset-ft",alt_20); +setprop(lw~"tmp/tile-alt-median-ft",alt_med); +setprop(lw~"tmp/tile-alt-min-ft",alt_min); +setprop(lw~"tmp/tile-alt-layered-ft",0.5 * (alt_min + alt_offset)); + +} + +########################################################### +# detailed altitude determination for convective calls +# clouds follow the terrain to some degree, but not excessively so +########################################################### + +var get_convective_altitude = func (balt, elevation) { + + +var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft"); +var alt_median = getprop(lw~"tmp/tile-alt-median-ft"); + +# get the maximal shift +var alt_variation = alt_median - alt_offset; + +# get the difference between offset and foot point +var alt_diff = elevation - alt_offset; + +# now get the elevation-induced shift + +var fraction = alt_diff / alt_variation; + +if (fraction > 1.0) {fraction = 1.0;} # no placement above maximum shift +if (fraction < 0.0) {fraction = 0.0;} # no downward shift + +# get the cloud base + +var cloudbase = balt - alt_offset; + +var alt_above_terrain = balt - elevation; + +# the shift strength is weakened if the layer is high above base elevation +# the reference altitude is 1000 ft, anything higher has less sensitivity to terrain + +var shift_strength = 1000.0/alt_above_terrain; + +if (shift_strength > 1.0) {shift_strength = 1.0;} # no enhancement for very low layers +if (shift_strength < 0.0) {shift_strength = 0.0;} # this shouldn't happen, but just in case... + +return balt + shift_strength * alt_diff * fraction; + +} + +########################################################### +# terrain presampling listener dispatcher +########################################################### + +var manage_presampling = func { + +var status = getprop(lw~"tmp/presampling-status"); + +# we only take action when the analysis is done +if (status != "finished") {return;} + +if (getprop(lw~"tiles/tile-counter") == 0) # we deal with a tile setup call from the menu + { + set_tile(); + } +else # the tile setup call came from weather_tile_management + { + var lat = getprop(lw~"tiles/tmp/latitude-deg"); + var lon = getprop(lw~"tiles/tmp/longitude-deg"); + var code = getprop(lw~"tiles/tmp/code"); + + weather_tile_management.generate_tile(code, lat, lon, 0); + } + + +# set status to idle again + +setprop(lw~"tmp/presampling-status", "idle"); + +} + + + ########################################################### # create an effect volume @@ -1758,6 +2212,8 @@ s.getNode("pressure-sea-level-inhg",1).setValue(p); var streak_wrapper = func { +setprop(lw~"tmp/thread-flag", 0); + var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); var type = getprop("/local-weather/tmp/cloud-type"); @@ -1783,6 +2239,8 @@ randomize_pos(type,rnd_alt,rnd_pos_x, rnd_pos_y, dir); var convection_wrapper = func { +setprop(lw~"tmp/thread-flag", 0); + var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); var alt = getprop("/local-weather/tmp/conv-alt"); @@ -1796,6 +2254,8 @@ create_cumosys(lat,lon,alt,n, size*1000.0); var barrier_wrapper = func { +setprop(lw~"tmp/thread-flag", 0); + var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); var alt = getprop("/local-weather/tmp/bar-alt"); @@ -1825,6 +2285,8 @@ create_cloud(type, path, lat, lon, alt, heading, 0); var layer_wrapper = func { +setprop(lw~"tmp/thread-flag", 0); + var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); var type = getprop(lw~"tmp/layer-type"); @@ -1842,6 +2304,25 @@ create_layer(type, lat, lon, alt, thick, rx, ry, phi, density, edge, rain_flag, } +var box_wrapper = func { + +setprop(lw~"tmp/thread-flag", 0); + +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); +var alt = getprop("position/altitude-ft"); +var x = getprop(lw~"tmp/box-x-m"); +var y = getprop(lw~"tmp/box-y-m"); +var z = getprop(lw~"tmp/box-alt-ft"); +var n = getprop(lw~"tmp/box-n"); + +var type = "Box_test"; +var subtype = "unspecified"; + +create_cloudbox(type,subtype,lat, lon, alt, x,y,z,n); + +} + #################################### # tile setup call wrapper @@ -1858,6 +2339,19 @@ setprop(lw~"tiles/tmp/longitude-deg",getprop("position/longitude-deg")); weather_tile_management.create_neighbours(getprop("position/latitude-deg"),getprop("position/longitude-deg"),getprop(lw~"tmp/tile-orientation-deg")); +# now see if we need to presample the terrain + +if ((getprop(lw~"tmp/presampling-flag") == 1) and (getprop(lw~"tmp/presampling-status") == "idle")) + { + terrain_presampling_start(getprop("position/latitude-deg"), getprop("position/longitude-deg"), 1000, 40000, getprop(lw~"tmp/tile-orientation-deg")); + return; + } + +# see if we use METAR for weather setup + +if ((getprop(lw~"METAR/available-flag") == 1) and (getprop(lw~"tmp/tile-management") == "METAR")) + {type = "METAR";} + setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); if (type == "High-pressure-core") @@ -1872,6 +2366,22 @@ else if (type == "Low-pressure") {weather_tiles.set_low_pressure_tile();} else if (type == "Low-pressure-core") {weather_tiles.set_low_pressure_core_tile();} +else if (type == "Cold-sector") + {weather_tiles.set_cold_sector_tile();} +else if (type == "Tropical") + {weather_tiles.set_tropical_weather_tile();} +else if (type == "Coldfront") + {weather_tiles.set_coldfront_tile();} +else if (type == "Warmfront-1") + {weather_tiles.set_warmfront1_tile();} +else if (type == "Warmfront-2") + {weather_tiles.set_warmfront2_tile();} +else if (type == "Warmfront-3") + {weather_tiles.set_warmfront3_tile();} +else if (type == "Warmfront-4") + {weather_tiles.set_warmfront4_tile();} +else if (type == "METAR") + {weather_tiles.set_METAR_tile();} else if (type == "Altocumulus sky") {weather_tiles.set_altocumulus_tile();setprop(lw~"tiles/code","altocumulus_sky");} else if (type == "Broken layers") @@ -1902,7 +2412,10 @@ else if (getprop(lw~"tmp/tile-management") != "single tile") { if (getprop(lw~"tile-loop-flag") == 0) - {setprop(lw~"tile-loop-flag",1); weather_tile_management.tile_management_loop();} + { + setprop(lw~"tiles/tile[4]/code",getprop(lw~"tiles/code")); + setprop(lw~"tile-loop-flag",1); + weather_tile_management.tile_management_loop();} } # start the interpolation loop @@ -1925,10 +2438,13 @@ if (getprop(lw~"effect-loop-flag") == 0) var startup = func { print("Loading local weather routines..."); +# get local Cartesian geometry + var lat = getprop("position/latitude-deg"); var lon = getprop("position/longitude-deg"); calc_geo(lat); + # copy weather properties at startup to local weather setprop(lw~"interpolation/visibility-m",getprop(ec~"boundary/entry[0]/visibility-m")); @@ -1936,11 +2452,13 @@ setprop(lw~"interpolation/pressure-sea-level-inhg",getprop(ec~"boundary/entry[0] setprop(lw~"interpolation/temperature-degc",getprop(ec~"boundary/entry[0]/temperature-degc")); setprop(lw~"interpolation/wind-from-heading-deg",getprop(ec~"boundary/entry[0]/wind-from-heading-deg")); setprop(lw~"interpolation/wind-speed-kt",getprop(ec~"boundary/entry[0]/wind-speed-kt")); +setprop(lw~"interpolation/turbulence",getprop(ec~"boundary/entry[0]/turbulence/magnitude-norm")); setprop(lw~"interpolation/rain-norm",0.0); setprop(lw~"interpolation/snow-norm",0.0); setprop(lw~"interpolation/thermal-lift",0.0); + # before interpolation starts, these are also initially current setprop(lw~"current/visibility-m",getprop(lwi~"visibility-m")); @@ -1948,18 +2466,34 @@ setprop(lw~"current/pressure-sea-level-inhg",getprop(lw~"interpolation/pressure- setprop(lw~"current/temperature-degc",getprop(lw~"interpolation/temperature-degc")); setprop(lw~"current/wind-from-heading-deg",getprop(lw~"interpolation/wind-from-heading-deg")); setprop(lw~"current/wind-speed-kt",getprop(lw~"interpolation/wind-speed-kt")); - setprop(lw~"current/rain-norm",getprop(lw~"interpolation/rain-norm")); setprop(lw~"current/snow-norm",getprop(lw~"interpolation/snow-norm")); setprop(lw~"current/thermal-lift",getprop(lw~"interpolation/thermal-lift")); +setprop(lw~"current/turbulence",getprop(lwi~"turbulence")); -# try to set up a menu from Nasal - -#setprop("/sim/menubar/default/menu[4]/item[8]/label","Test"); -#setprop("/sim/menubar/default/menu[4]/item[8]/binding/command","dialog-show"); -#setprop("/sim/menubar/default/menu[4]/item[8]/binding/dialog-name","local_weather_tiles"); -#setprop("/sim/menubar/default/menu[4]/item[8]/enabled","true"); +# create default properties for METAR system, should be overwritten by real-weather-fetch +setprop(lw~"METAR/latitude-deg",lat); +setprop(lw~"METAR/longitude-deg",lon); +setprop(lw~"METAR/altitude-ft",0.0); +setprop(lw~"METAR/wind-direction-deg",0.0); +setprop(lw~"METAR/wind-strength-kt",10.0); +setprop(lw~"METAR/visibility-m",17000.0); +setprop(lw~"METAR/rain-norm",0.0); +setprop(lw~"METAR/snow-norm",0.0); +setprop(lw~"METAR/temperature-degc",10.0); +setprop(lw~"METAR/dewpoint-degc",7.0); +setprop(lw~"METAR/pressure-inhg",29.92); +setprop(lw~"METAR/thunderstorm-flag",0); +setprop(lw~"METAR/layer[0]/cover-oct",4); +setprop(lw~"METAR/layer[0]/alt-agl-ft", 3000.0); +setprop(lw~"METAR/layer[1]/cover-oct",0); +setprop(lw~"METAR/layer[1]/alt-agl-ft", 20000.0); +setprop(lw~"METAR/layer[2]/cover-oct",0); +setprop(lw~"METAR/layer[2]/alt-agl-ft", 20000.0); +setprop(lw~"METAR/layer[3]/cover-oct",0); +setprop(lw~"METAR/layer[3]/alt-agl-ft", 20000.0); +setprop(lw~"METAR/available-flag",1); # set listener for worker threads @@ -1967,6 +2501,7 @@ setlistener(lw~"tmp/thread-status", func {var s = size(clouds_type); cloud_plac setlistener(lw~"tmp/convective-status", func {var s = size(clouds_type); cloud_placement_loop(s); }); setlistener(lw~"tmp/effect-thread-status", func {var s = size(effects_geo); effect_placement_loop(s); }); setlistener(lw~"tmp/convective-status", func {var s = size(effects_geo); effect_placement_loop(s); }); +setlistener(lw~"tmp/presampling-status", func {manage_presampling(); }); } @@ -1976,19 +2511,26 @@ setlistener(lw~"tmp/convective-status", func {var s = size(effects_geo); effect var test = func { -#var lat = getprop("position/latitude-deg"); -#var lon = getprop("position/longitude-deg"); - -#create_layer("Nimbus", lat, lon, 3000.0, 500.0, 10000.0, 5000.0, 45.0, 1.0, 0.2,1,1.0); -#create_layer(type, lat, lon, alt, thick, rx, ry, phi, density, edge, rain_flag, rain_density); - -thread.newthread(terrain_presampling); - -#create_effect_volume(2, lat, lon, 9000.0, 4000.0, 45.0, 0.0, 3500.0, 1500, 0.4, -1, -1, -1, 0); +var lat = getprop("position/latitude-deg"); +var lon = getprop("position/longitude-deg"); + + +# terrain_presampling_start(lat, lon, 1000, 20000, 0.0); + +# test: 8 identical position tuples for KSFO +var p=[ 37.6189722, -122.3748889, 37.6189722, -122.3748889, + 37.6289722, -122.3748889, 37.6189722, -122.3648889, + 37.6389722, -122.3748889, 37.6189722, -122.3548889, + 37.6489722, -122.3748889, 37.6189722, -122.3448889 ]; + +var x=geodinfo(p, 10000); # passing in vector with position tuples + +foreach(var e;x) { + print("Elevation:",e); # showing results +} + -#if (getprop(lw~"effect-loop-flag") == 0) -#{setprop(lw~"effect-loop-flag",1); effect_volume_loop();} } @@ -2013,7 +2555,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, 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.25, 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, 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}; # a hash map of average vertical cloud model sizes @@ -2021,31 +2563,33 @@ var cloud_vertical_size_map = {Altocumulus: 700.0, Cumulus: 600.0, Nimbus: 1000. # storage arrays for cloud generation -clouds_type = []; -clouds_path = []; -clouds_lat = []; -clouds_lon = []; -clouds_alt = []; -clouds_orientation = []; +var clouds_type = []; +var clouds_path = []; +var clouds_lat = []; +var clouds_lon = []; +var clouds_alt = []; +var clouds_orientation = []; # storage arrays for effect volume generation -effects_geo = []; -effects_lat = []; -effects_lon = []; -effects_r1 = []; -effects_r2 = []; -effects_phi = []; -effects_alt_low = []; -effects_alt_high = []; -effects_vis = []; -effects_rain = []; -effects_snow = []; -effects_turb = []; -effects_lift = []; -effects_lift_flag = []; +var effects_geo = []; +var effects_lat = []; +var effects_lon = []; +var effects_r1 = []; +var effects_r2 = []; +var effects_phi = []; +var effects_alt_low = []; +var effects_alt_high = []; +var effects_vis = []; +var effects_rain = []; +var effects_snow = []; +var effects_turb = []; +var effects_lift = []; +var effects_lift_flag = []; +# storage array for terrain presampling +var terrain_n = []; # set all sorts of default properties for the menu @@ -2086,24 +2630,32 @@ setprop(lw~"tmp/layer-density",1.0); setprop(lw~"tmp/layer-edge",0.2); setprop(lw~"tmp/layer-rain-flag",1); setprop(lw~"tmp/layer-rain-density",1.0); +setprop(lw~"tmp/box-x-m",300.0); +setprop(lw~"tmp/box-y-m",300.0); +setprop(lw~"tmp/box-alt-ft",300.0); +setprop(lw~"tmp/box-n",4); setprop(lw~"tmp/tile-type", "High-pressure"); setprop(lw~"tmp/tile-orientation-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/generate-thermal-lift-flag", 0); -setprop(lw~"tmp/presampling-flag", 0); +setprop(lw~"tmp/presampling-flag", 1); setprop(lw~"tmp/asymmetric-tile-loading-flag", 0); -setprop(lw~"tmp/thread-flag", 0); +setprop(lw~"tmp/thread-flag", 1); setprop(lw~"tmp/last-reading-pos-del",0); setprop(lw~"tmp/last-reading-pos-mod",0); setprop(lw~"tmp/thread-status", "idle"); setprop(lw~"tmp/convective-status", "idle"); +setprop(lw~"tmp/presampling-status", "idle"); # set config values setprop(lw~"config/distance-to-load-tile-m",35000.0); setprop(lw~"config/distance-to-remove-tile-m",37000.0); -setprop(lw~"config/detailed-clouds-flag",0); +setprop(lw~"config/detailed-clouds-flag",1); +setprop(lw~"config/thermal-properties",1.0); # set the default loop flags to loops inactive @@ -2130,6 +2682,8 @@ setprop(lw~"effect-volumes/number-active-snow",0); setprop(lw~"effect-volumes/number-active-turb",0); setprop(lw~"effect-volumes/number-active-lift",0); + + # create properties for tile management setprop(lw~"tiles/tile-counter",0); diff --git a/Nasal/weather_tile_management.nas b/Nasal/weather_tile_management.nas index 95dbd50ab..1ed7bf158 100644 --- a/Nasal/weather_tile_management.nas +++ b/Nasal/weather_tile_management.nas @@ -12,7 +12,7 @@ var tile_management_loop = func { var tNode = props.globals.getNode(lw~"tiles", 1).getChildren("tile"); var viewpos = geo.aircraft_position(); # using viewpos here triggers massive tile ops for tower view... -var code = getprop(lw~"tiles/code"); +var code = getprop(lw~"tiles/tile[4]/code"); var i = 0; var d_min = 100000.0; var i_min = 0; @@ -46,8 +46,9 @@ foreach (var t; tNode) { { setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1); print("Building tile unique index ",getprop(lw~"tiles/tile-counter")); - generate_tile(code, tpos.lat(), tpos.lon(),0); + generate_tile(code, tpos.lat(), tpos.lon(),i); t.getNode("generated-flag").setValue(1); + t.getNode("code",1).setValue(getprop(lw~"tiles/code")); t.getNode("tile-index",1).setValue(getprop(lw~"tiles/tile-counter")); } @@ -68,6 +69,8 @@ foreach (var t; tNode) { change_active_tile(i_min); } + + if (getprop(lw~"tile-loop-flag") ==1) {settimer(tile_management_loop, 5.0);} } @@ -77,10 +80,31 @@ if (getprop(lw~"tile-loop-flag") ==1) {settimer(tile_management_loop, 5.0);} # tile generation call ################################### -var generate_tile = func (code, lat, lon, index) { +var generate_tile = func (code, lat, lon, dir_index) { setprop(lw~"tiles/tmp/latitude-deg", lat); setprop(lw~"tiles/tmp/longitude-deg",lon); +setprop(lw~"tiles/tmp/code",code); + +# now see if we need to presample the terrain + +if ((getprop(lw~"tmp/presampling-flag") == 1) and (getprop(lw~"tmp/presampling-status") == "idle")) + { + local_weather.terrain_presampling_start(lat, lon, 1000, 40000, getprop(lw~"tmp/tile-orientation-deg")); + return; + } + +# now allow for some change in tile orientation + +var alpha = getprop(lw~"tmp/tile-orientation-deg"); +alpha = alpha + 2.0 * (rand()-0.5) * 10.0; + +# account for the systematic spin of weather systems around a low pressure core dependent on hemisphere +if (lat >0.0) {alpha = alpha -3.0;} +else {alpha = alpha +3.0;} + + +setprop(lw~"tmp/tile-orientation-deg",alpha); if (getprop(lw~"tmp/tile-management") == "repeat tile") { @@ -97,6 +121,9 @@ if (getprop(lw~"tmp/tile-management") == "repeat tile") else if (code == "low_pressure_border") {weather_tiles.set_low_pressure_border_tile();} else if (code == "low_pressure") {weather_tiles.set_low_pressure_tile();} else if (code == "low_pressure_core") {weather_tiles.set_low_pressure_core_tile();} + else if (code == "cold_sector") {weather_tiles.set_cold_sector_tile();} + else if (code == "warm_sector") {weather_tiles.set_warm_sector_tile();} + else if (code == "tropical_weather") {weather_tiles.set_tropical_weather_tile();} } else if (getprop(lw~"tmp/tile-management") == "realistic weather") { @@ -104,38 +131,108 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather") if (code == "low_pressure_core") { - if (rn > 0.3) {weather_tiles.set_low_pressure_core_tile();} + if (rn > 0.2) {weather_tiles.set_low_pressure_core_tile();} else {weather_tiles.set_low_pressure_tile();} } else if (code == "low_pressure") { - if (rn > 0.3) {weather_tiles.set_low_pressure_tile();} - else if (rn > 0.15) {weather_tiles.set_low_pressure_core_tile();} + if (rn > 0.2) {weather_tiles.set_low_pressure_tile();} + else if (rn > 0.1) {weather_tiles.set_low_pressure_core_tile();} else {weather_tiles.set_low_pressure_border_tile();} } else if (code == "low_pressure_border") { - if (rn > 0.3) {weather_tiles.set_low_pressure_border_tile();} - else if (rn > 0.15) {weather_tiles.set_low_pressure_tile();} + if (rn > 0.4) {weather_tiles.set_low_pressure_border_tile();} + else if (rn > 0.3) {weather_tiles.set_cold_sector_tile();} + else if (rn > 0.2) {weather_tiles.set_warm_sector_tile();} + else if (rn > 0.1) {weather_tiles.set_low_pressure_tile();} else {weather_tiles.set_high_pressure_border_tile();} } else if (code == "high_pressure_border") { - if (rn > 0.3) {weather_tiles.set_high_pressure_border_tile();} - else if (rn > 0.15) {weather_tiles.set_high_pressure_tile();} + if (rn > 0.4) {weather_tiles.set_high_pressure_border_tile();} + else if (rn > 0.3) {weather_tiles.set_cold_sector_tile();} + else if (rn > 0.2) {weather_tiles.set_warm_sector_tile();} + else if (rn > 0.1) {weather_tiles.set_high_pressure_tile();} else {weather_tiles.set_low_pressure_border_tile();} } else if (code == "high_pressure") { - if (rn > 0.3) {weather_tiles.set_high_pressure_tile();} - else if (rn > 0.15) {weather_tiles.set_high_pressure_border_tile();} + if (rn > 0.2) {weather_tiles.set_high_pressure_tile();} + else if (rn > 0.1) {weather_tiles.set_high_pressure_border_tile();} else {weather_tiles.set_high_pressure_core_tile();} } else if (code == "high_pressure_core") { - if (rn > 0.3) {weather_tiles.set_high_pressure_core_tile();} + if (rn > 0.2) {weather_tiles.set_high_pressure_core_tile();} else {weather_tiles.set_high_pressure_tile();} } + else if (code == "cold_sector") + { + if (rn > 0.3) {weather_tiles.set_cold_sector_tile();} + else if (rn > 0.2) + { + if ((dir_index ==0) or (dir_index ==1) or (dir_index==2)) + {weather_tiles.set_warmfront1_tile();} + else if ((dir_index ==3) or (dir_index ==5)) + {weather_tiles.set_cold_sector_tile();} + else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8)) + {weather_tiles.set_coldfront_tile();} + } + else if (rn > 0.1) {weather_tiles.set_low_pressure_border_tile();} + else {weather_tiles.set_high_pressure_border_tile();} + } + else if (code == "warm_sector") + { + if (rn > 0.3) {weather_tiles.set_warm_sector_tile();} + else if (rn > 0.2) + { + if ((dir_index ==0) or (dir_index ==1) or (dir_index==2)) + {weather_tiles.set_coldfront_tile();} + else if ((dir_index ==3) or (dir_index ==5)) + {weather_tiles.set_warm_sector_tile();} + else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8)) + {weather_tiles.set_warmfront4_tile();} + } + else if (rn > 0.1) {weather_tiles.set_low_pressure_border_tile();} + else {weather_tiles.set_high_pressure_border_tile();} + } + else if (code == "warmfront1") + { + if ((dir_index ==0) or (dir_index ==1) or (dir_index==2)) + {weather_tiles.set_warmfront2_tile();} + else if ((dir_index ==3) or (dir_index ==5)) + {weather_tiles.set_warmfront1_tile();} + else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8)) + {weather_tiles.set_cold_sector_tile();} + } + else if (code == "warmfront2") + { + if ((dir_index ==0) or (dir_index ==1) or (dir_index==2)) + {weather_tiles.set_warmfront3_tile();} + if ((dir_index ==3) or (dir_index ==5)) + {weather_tiles.set_warmfront2_tile();} + if ((dir_index ==6) or (dir_index ==7) or (dir_index==8)) + {weather_tiles.set_warmfront1_tile();} + } + else if (code == "warmfront3") + { + if ((dir_index ==0) or (dir_index ==1) or (dir_index==2)) + {weather_tiles.set_warmfront4_tile();} + if ((dir_index ==3) or (dir_index ==5)) + {weather_tiles.set_warmfront3_tile();} + if ((dir_index ==6) or (dir_index ==7) or (dir_index==8)) + {weather_tiles.set_warmfront2_tile();} + } + else if (code == "warmfront4") + { + if ((dir_index ==0) or (dir_index ==1) or (dir_index==2)) + {weather_tiles.set_warm_sector_tile();} + if ((dir_index ==3) or (dir_index ==5)) + {weather_tiles.set_warmfront4_tile();} + if ((dir_index ==6) or (dir_index ==7) or (dir_index==8)) + {weather_tiles.set_warmfront3_tile();} + } } # end if mode == realistic weather } @@ -152,8 +249,6 @@ var n = 100; var flag_mod = 0; -#var mvec = props.globals.getNode("models", 1).getChildren("model"); -#var msize = size(mvec); var status = getprop(lw~"tmp/thread-status"); @@ -286,6 +381,7 @@ var lat = t.getNode("latitude-deg").getValue(); var lon = t.getNode("longitude-deg").getValue(); var alpha = getprop(lw~"tmp/tile-orientation-deg"); + if (index == 0) { copy_entry(4,8); @@ -383,6 +479,7 @@ else if (index == 8) create_neighbour(lat,lon,8,alpha); } + } ##################################### @@ -400,6 +497,7 @@ t.getNode("latitude-deg").setValue(f.getNode("latitude-deg").getValue()); t.getNode("longitude-deg").setValue(f.getNode("longitude-deg").getValue()); t.getNode("generated-flag").setValue(f.getNode("generated-flag").getValue()); t.getNode("tile-index").setValue(f.getNode("tile-index").getValue()); +t.getNode("code").setValue(f.getNode("code").getValue()); } ##################################### @@ -426,6 +524,7 @@ t.getNode("latitude-deg",1).setValue(blat + get_lat(x,y,phi)); t.getNode("longitude-deg",1).setValue(blon + get_lon(x,y,phi)); t.getNode("generated-flag",1).setValue(0); t.getNode("tile-index",1).setValue(-1); +t.getNode("code",1).setValue(""); } ##################################### @@ -446,24 +545,28 @@ setprop(lw~"tiles/tile[0]/latitude-deg",blat + get_lat(x,y,phi)); setprop(lw~"tiles/tile[0]/longitude-deg",blon + get_lon(x,y,phi)); setprop(lw~"tiles/tile[0]/generated-flag",0); setprop(lw~"tiles/tile[0]/tile-index",-1); +setprop(lw~"tiles/tile[0]/code",""); x = 0.0; y = 40000.0; setprop(lw~"tiles/tile[1]/latitude-deg",blat + get_lat(x,y,phi)); setprop(lw~"tiles/tile[1]/longitude-deg",blon + get_lon(x,y,phi)); setprop(lw~"tiles/tile[1]/generated-flag",0); setprop(lw~"tiles/tile[1]/tile-index",-1); +setprop(lw~"tiles/tile[1]/code",""); x = 40000.0; y = 40000.0; setprop(lw~"tiles/tile[2]/latitude-deg",blat + get_lat(x,y,phi)); setprop(lw~"tiles/tile[2]/longitude-deg",blon + get_lon(x,y,phi)); setprop(lw~"tiles/tile[2]/generated-flag",0); setprop(lw~"tiles/tile[2]/tile-index",-1); +setprop(lw~"tiles/tile[2]/code",""); x = -40000.0; y = 0.0; setprop(lw~"tiles/tile[3]/latitude-deg",blat + get_lat(x,y,phi)); setprop(lw~"tiles/tile[3]/longitude-deg",blon + get_lon(x,y,phi)); setprop(lw~"tiles/tile[3]/generated-flag",0); setprop(lw~"tiles/tile[3]/tile-index",-1); +setprop(lw~"tiles/tile[3]/code",""); # this is the current tile x = 0.0; y = 0.0; @@ -471,30 +574,36 @@ setprop(lw~"tiles/tile[4]/latitude-deg",blat + get_lat(x,y,phi)); setprop(lw~"tiles/tile[4]/longitude-deg",blon + get_lon(x,y,phi)); setprop(lw~"tiles/tile[4]/generated-flag",1); setprop(lw~"tiles/tile[4]/tile-index",1); +setprop(lw~"tiles/tile[4]/code",""); + x = 40000.0; y = 0.0; setprop(lw~"tiles/tile[5]/latitude-deg",blat + get_lat(x,y,phi)); setprop(lw~"tiles/tile[5]/longitude-deg",blon + get_lon(x,y,phi)); setprop(lw~"tiles/tile[5]/generated-flag",0); setprop(lw~"tiles/tile[5]/tile-index",-1); +setprop(lw~"tiles/tile[5]/code",""); x = -40000.0; y = -40000.0; setprop(lw~"tiles/tile[6]/latitude-deg",blat + get_lat(x,y,phi)); setprop(lw~"tiles/tile[6]/longitude-deg",blon + get_lon(x,y,phi)); setprop(lw~"tiles/tile[6]/generated-flag",0); setprop(lw~"tiles/tile[6]/tile-index",-1); +setprop(lw~"tiles/tile[6]/code",""); x = 0.0; y = -40000.0; setprop(lw~"tiles/tile[7]/latitude-deg",blat + get_lat(x,y,phi)); setprop(lw~"tiles/tile[7]/longitude-deg",blon + get_lon(x,y,phi)); setprop(lw~"tiles/tile[7]/generated-flag",0); setprop(lw~"tiles/tile[7]/tile-index",-1); +setprop(lw~"tiles/tile[7]/code",""); x = 40000.0; y = -40000.0; setprop(lw~"tiles/tile[8]/latitude-deg",blat + get_lat(x,y,phi)); setprop(lw~"tiles/tile[8]/longitude-deg",blon + get_lon(x,y,phi)); setprop(lw~"tiles/tile[8]/generated-flag",0); setprop(lw~"tiles/tile[8]/tile-index",-1); +setprop(lw~"tiles/tile[8]/code",""); } ################### diff --git a/Nasal/weather_tiles.nas b/Nasal/weather_tiles.nas index 28911b3f0..1dde8fcf4 100644 --- a/Nasal/weather_tiles.nas +++ b/Nasal/weather_tiles.nas @@ -11,6 +11,7 @@ if (getprop(lw~"tmp/thread-flag") == 1){setprop(lw~"tmp/thread-status","computin var tile_finished = func { setprop(lw~"clouds/placement-index",0); +setsize(elat,0); setsize(elon,0); setsize(erad,0); if (getprop(lw~"tmp/thread-flag") == 1){setprop(lw~"tmp/thread-status","placing");} } @@ -44,11 +45,32 @@ 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, 14.0, 12.0, 29.78); +local_weather.set_weather_station(blat, blon, 20000.0, 14.0, 12.0, 29.78); + + +create_2_8_cirrocumulus(blat, blon, 12000.0, alpha); + +#x = 2.0 * (rand()-0.5) * 12000; +#y = 2.0 * (rand()-0.5) * 12000; +#create_medium_thunderstorm(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), 3000.0+alt_offset, alpha); + +#x = 2.0 * (rand()-0.5) * 12000; +#y = 2.0 * (rand()-0.5) * 12000; +#create_medium_thunderstorm(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), 3000.0+alt_offset, alpha); + +# create_big_thunderstorm(blat, blon, 3000.0+alt_offset, alpha); + + + + +#var strength = 1.0; +#var n = int(4000 * strength) * 0.2; +#local_weather.cumulus_exclusion_layer(blat, blon, 3000.0+alt_offset, n, 20000.0, 20000.0, alpha, 0.0, 2.5, size(elat), elat, elon, erad); + +# some turbulence below the convection layer + +#local_weather.create_effect_volume(3, blat, blon, 20000.0, 20000.0, alpha, 0.0, 3500.0+alt_offset, -1, -1, -1, 0.4, -1,0 ); -create_4_8_cirrostratus_patches(blat, blon, 25000, alpha); -# create_stratocumulus_bank(blat, blon, 3000+alt_offset, alpha); -# create_4_8_stratus_patches(blat, blon, 3000+alt_offset, alpha); tile_finished(); } @@ -85,7 +107,7 @@ var vis = 35000.0 + rand() * 20000.0; var T = 20.0 + rand() * 10.0; var spread = 5.0 + 3.0 * rand(); var D = T - spread; -var p = 1033.0 + rand() * 10.0; +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); @@ -134,7 +156,7 @@ var vis = 25000.0 + rand() * 15000.0; var T = 15.0 + rand() * 10.0; var spread = 4.0 + 2.0 * rand(); var D = T - spread; -var p = 1023.0 + rand() * 10.0; +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); @@ -222,7 +244,7 @@ var vis = 20000.0 + rand() * 12000.0; var T = 12.0 + rand() * 10.0; var spread = 3.0 + 2.0 * rand(); var D = T - spread; -var p = 1013.0 + rand() * 10.0; +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); @@ -338,7 +360,12 @@ var lon = 0.0; var alpha = getprop(lw~"tmp/tile-orientation-deg"); var phi = alpha * math.pi/180.0; -var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft"); + +if (getprop(lw~"tmp/presampling-flag") == 0) + {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} +else + {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} + # get tile center coordinates @@ -352,13 +379,13 @@ var vis = 15000.0 + rand() * 10000.0; var T = 10.0 + rand() * 10.0; var spread = 2.0 + 2.0 * rand(); var D = T - spread; -var p = 1003.0 + rand() * 10.0; +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); # altitude for the lowest layer -alt = spread * 1000.0; +var alt = spread * 1000.0; # now a random selection of different possible cloud configuration scenarios @@ -421,7 +448,12 @@ var lon = 0.0; var alpha = getprop(lw~"tmp/tile-orientation-deg"); var phi = alpha * math.pi/180.0; -var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft"); + + +if (getprop(lw~"tmp/presampling-flag") == 0) + {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} +else + {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} # get tile center coordinates @@ -435,13 +467,13 @@ var vis = 10000.0 + rand() * 10000.0; var T = 5.0 + rand() * 10.0; var spread = 1.0 + 2.0 * rand(); var D = T - spread; -var p = 993.0 + rand() * 10.0; +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); # altitude for the lowest layer -alt = spread * 1000.0; +var alt = spread * 1000.0; var rn = rand(); @@ -527,7 +559,11 @@ var lon = 0.0; var alpha = getprop(lw~"tmp/tile-orientation-deg"); var phi = alpha * math.pi/180.0; -var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft"); + +if (getprop(lw~"tmp/presampling-flag") == 0) + {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} +else + {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} # get tile center coordinates @@ -541,7 +577,7 @@ var vis = 8000.0 + rand() * 7000.0; var T = 3.0 + rand() * 7.0; var spread = 1.0 + 1.0 * rand(); var D = T - spread; -var p = 983.0 + rand() * 10.0; +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); @@ -549,7 +585,7 @@ local_weather.set_weather_station(blat, blon, vis, T, D, p * hp_to_inhg); # set a closed Nimbostratus layer -alt = spread * 1000.0 + local_weather.cloud_vertical_size_map["Nimbus"] * 0.5 * m_to_ft; +var alt = spread * 1000.0 + local_weather.cloud_vertical_size_map["Nimbus"] * 0.5 * m_to_ft; #print("alt: ",spread*1000); @@ -573,6 +609,739 @@ tile_finished(); } + +#################################### +# cold sector +#################################### + +var set_cold_sector_tile = func { + +tile_start(); + +setprop(lw~"tiles/code","cold_sector"); + +var x = 0.0; +var y = 0.0; +var lat = 0.0; +var lon = 0.0; + +var alpha = getprop(lw~"tmp/tile-orientation-deg"); +var phi = alpha * math.pi/180.0; +var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft"); + +# get tile center coordinates + +var blat = getprop(lw~"tiles/tmp/latitude-deg"); +var blon = getprop(lw~"tiles/tmp/longitude-deg"); +calc_geo(blat); + +# get probabilistic values for the weather parameters + +var vis = 35000.0 + rand() * 20000.0; +var T = 8.0 + rand() * 8.0; +var spread = 3.0 + 2.0 * rand(); +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); + +# altitude for the lowest layer +var alt = spread * 1000.0; + +var rn = rand(); + + +if (rn > 0.0) + { + # cloud scenario 1: strong Cumulus development + var strength = 0.8 + rand() * 0.2; + local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0); + } + +tile_finished(); + +} + + +#################################### +# Warm sector +#################################### + +var set_warm_sector_tile = func { + +tile_start(); + +setprop(lw~"tiles/code","warm_sector"); + +var x = 0.0; +var y = 0.0; +var lat = 0.0; +var lon = 0.0; + +var alpha = getprop(lw~"tmp/tile-orientation-deg"); +var phi = alpha * math.pi/180.0; +var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft"); + +# get tile center coordinates + +var blat = getprop(lw~"tiles/tmp/latitude-deg"); +var blon = getprop(lw~"tiles/tmp/longitude-deg"); +calc_geo(blat); + +# get probabilistic values for the weather parameters + +var vis = 15000.0 + rand() * 10000.0; +var T = 16.0 + rand() * 10.0; +var spread = 2.0 + 2.0 * rand(); +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); + +# altitude for the lowest layer +var alt = spread * 1000.0; + +var rn = rand(); + + +if (rn > 0.0) + { + # cloud scenario 1: weak Cumulus development, some Cirrostratus + var strength = 0.3 + rand() * 0.2; + local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0); + create_4_8_cirrostratus_patches(blat, blon, alt+alt_offset+25000.0, alpha); + } + +tile_finished(); + +} + + + +#################################### +# Tropical weather +#################################### + +var set_tropical_weather_tile = func { + +tile_start(); + +setprop(lw~"tiles/code","tropical_weather"); + +var x = 0.0; +var y = 0.0; +var lat = 0.0; +var lon = 0.0; + +var sec_to_rad = 2.0 * math.pi/86400; # conversion factor for sinusoidal dependence on daytime + +# get the local time of the day in seconds + +var t = getprop("sim/time/utc/day-seconds"); +t = t + getprop("sim/time/local-offset"); + +var alpha = getprop(lw~"tmp/tile-orientation-deg"); +var phi = alpha * math.pi/180.0; +var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft"); + +# get tile center coordinates + +var blat = getprop(lw~"tiles/tmp/latitude-deg"); +var blon = getprop(lw~"tiles/tmp/longitude-deg"); +calc_geo(blat); + +# get probabilistic values for the weather parameters + +var vis = 10000.0 + rand() * 10000.0; +var T = 20.0 + rand() * 15.0; +var spread = 3.0 + 2.0 * rand(); +var D = T - spread; +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); + +# altitude for the lowest layer +var alt = spread * 1000.0; + +# tropical weather has a strong daily variation, call thunderstorm only in the correct afternoon time window + +var t_factor = 0.5 * (1.0-math.cos((t * sec_to_rad)-0.9)); + +var rn = rand(); + +if (rn > (t_factor * t_factor * t_factor * t_factor)) # call a normal convective cloud system +{ +var strength = 1.0 + rand() * 0.2; +local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0); +} + +else +{ + +# a random selection of different possible thunderstorm cloud configuration scenarios + +rn = rand(); + +if (rn > 0.2) + { + # cloud scenario 1: 1-2 medium sized storms + + x = 2.0 * (rand()-0.5) * 12000; + y = 2.0 * (rand()-0.5) * 12000; + + if (rand() > 0.6) + {create_medium_thunderstorm(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt+alt_offset, alpha);} + else + {create_small_thunderstorm(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt+alt_offset, 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(blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset, alpha);} + else + {create_small_thunderstorm(blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset, alpha);} + } + } +else if (rn > 0.0) + { + # cloud scenario 1: Single big storm + + x = 2.0 * (rand()-0.5) * 12000; + y = 2.0 * (rand()-0.5) * 12000; + + create_big_thunderstorm(blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset, alpha); + } + + +# the convective layer + +var strength = 0.5 * t_factor; +var n = int(4000 * strength) * 0.2; +local_weather.cumulus_exclusion_layer(blat, blon, alt+alt_offset, n, 20000.0, 20000.0, alpha, 0.3,1.4 , size(elat), elat, elon, erad); +local_weather.cumulus_exclusion_layer(blat, blon, alt+alt_offset, n, 20000.0, 20000.0, alpha, 1.9,2.5 , size(elat), elat, elon, erad); + +# some turbulence in the convection layer + +local_weather.create_effect_volume(3, blat, blon, 20000.0, 20000.0, alpha, 0.0, alt+3000.0+alt_offset, -1, -1, -1, 0.4, -1,0 ); + +} # end thundercloud placement + +tile_finished(); + +} + + +#################################### +# Coldfront +#################################### + + +var set_coldfront_tile = func { + +tile_start(); + +setprop(lw~"tiles/code","coldfront"); + +var x = 0.0; +var y = 0.0; +var lat = 0.0; +var lon = 0.0; + + + +var alpha = getprop(lw~"tmp/tile-orientation-deg"); +var phi = alpha * math.pi/180.0; +var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft"); + +# get tile center coordinates + +var blat = getprop(lw~"tiles/tmp/latitude-deg"); +var blon = getprop(lw~"tiles/tmp/longitude-deg"); +calc_geo(blat); + +# get probabilistic values for the weather parameters + +var vis = 20000.0 + rand() * 10000.0; +var T = 20.0 + rand() * 8.0; +var spread = 3.0 + 2.0 * rand(); +var D = T - spread; +var p = 1005 + rand() * 10.0; p = adjust_p(p); + +# first weather info for tile (lat, lon, visibility, temperature, dew point, pressure) + +# 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); + +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); + +# 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); + +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); + +# altitude for the lowest layer +var alt = spread * 1000.0; + +# thunderstorms first + +for (var i =0; i < 3; i=i+1) + { + x = 2.0 * (rand()-0.5) * 15000; + y = 2.0 * (rand()-0.5) * 2000 + 5000.0; + if (rand() > 0.7) + {create_medium_thunderstorm(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt+alt_offset, alpha);} + else + {create_small_thunderstorm(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), alt+alt_offset, alpha);} + } + +# next the dense cloud layer underneath the thunderstorms + +x = 0.0; +y = 5000.0; + +var strength = 0.3; +var n = int(4000 * strength) * 0.2; +local_weather.cumulus_exclusion_layer(blat+get_lat(x,y,phi), blon+ get_lon(x,y,phi), alt+alt_offset, n, 20000.0, 10000.0, alpha, 2.1,2.5 , size(elat), elat, elon, erad); + +# then leading and traling Cumulus + +x = 0.0; +y = 15500.0; + +strength = 1.0; +n = int(4000 * strength) * 0.15; +local_weather.cumulus_exclusion_layer(blat+get_lat(x,y,phi), blon+ get_lon(x,y,phi), alt+alt_offset, n, 20000.0, 2000.0, alpha, 0.5,1.4 , size(elat), elat, elon, erad); + +x = 0.0; +y = -5500.0; + +strength = 1.0; +n = int(4000 * strength) * 0.15; +local_weather.cumulus_exclusion_layer(blat+get_lat(x,y,phi), blon+ get_lon(x,y,phi), alt+alt_offset, n, 20000.0, 2000.0, alpha, 0.5,1.4 , size(elat), elat, elon, erad); + +# finally some thin stratus underneath the Cumulus + +x = 0.0; +y = 13000.0; +local_weather.create_streak("Stratus (thin)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,20,2000.0,0.2,3,1500.0,0.2,alpha,1.0); +local_weather.randomize_pos("Stratus (thin)",0.0,1200.0,1200.0,0.0); + +x = 0.0; +y = -3000.0; +local_weather.create_streak("Stratus (thin)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,20,2000.0,0.2,3,1500.0,0.2,alpha,1.0); +local_weather.randomize_pos("Stratus (thin)",0.0,1200.0,1200.0,0.0); + +# some turbulence in the convection layer + +x=0.0; y = 5000.0; +local_weather.create_effect_volume(3, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 20000.0, 11000.0, alpha, 0.0, alt+3000.0+alt_offset, -1, -1, -1, 0.4, -1,0 ); + +# some rain and reduced visibility in its core + +x=0.0; y = 5000.0; +local_weather.create_effect_volume(3, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 20000.0, 8000.0, alpha, 0.0, alt+alt_offset, 10000.0, 0.1, -1, -1, -1,0 ); + +tile_finished(); + +} + + +#################################### +# Warmfront 1 +#################################### + + +var set_warmfront1_tile = func { + +tile_start(); + +setprop(lw~"tiles/code","warmfront1"); + +var x = 0.0; +var y = 0.0; +var lat = 0.0; +var lon = 0.0; + + + +var alpha = getprop(lw~"tmp/tile-orientation-deg"); +var phi = alpha * math.pi/180.0; + +if (getprop(lw~"tmp/presampling-flag") == 0) + {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} +else + {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} + +# get tile center coordinates + +var blat = getprop(lw~"tiles/tmp/latitude-deg"); +var blon = getprop(lw~"tiles/tmp/longitude-deg"); +calc_geo(blat); + +# get probabilistic values for the weather parameters + +var vis = 20000.0 + rand() * 5000.0; +var T = 10.0 + rand() * 8.0; +var spread = 3.0 + 3.0 * rand(); +var D = T - spread; +var p = 1005 + rand() * 10.0; p = adjust_p(p); + +# first weather info for tile (lat, lon, visibility, temperature, dew point, pressure) + +# 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); + +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); + +# 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); + +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); + +# altitude for the lowest layer +var alt = spread * 1000.0; + +# high Cirrus leading + +x = 2.0 * (rand()-0.5) * 3000; +y = 2.0 * (rand()-0.5) * 3000 - 12000.0; + + +local_weather.create_streak("Cirrus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 25000.0+alt+alt_offset,3,11000.0,0.0, 1,8000.0,0.0,alpha ,1.0); +local_weather.randomize_pos("Cirrus",1500.0,800.0,10000.0,alpha); + +# followed by random patches of Cirrostratus + +for (var i=0; i<6; i=i+1) + { + var x = 2.0 * (rand()-0.5) * 15000; + var y = 2.0 * (rand()-0.5) * 10000 + 10000; + var beta = (rand() -0.5) * 180.0; + local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 18000 + alt + alt_offset,4,2300.0,0.2,4,2300.0,0.2,alpha+beta,1.0); +local_weather.randomize_pos("Cirrostratus",300.0,600.0,600.0,alpha+beta); + } + +tile_finished(); + +} + + +#################################### +# Warmfront 2 +#################################### + + +var set_warmfront2_tile = func { + +tile_start(); + +setprop(lw~"tiles/code","warmfront2"); + +var x = 0.0; +var y = 0.0; +var lat = 0.0; +var lon = 0.0; + + + +var alpha = getprop(lw~"tmp/tile-orientation-deg"); +var phi = alpha * math.pi/180.0; + +if (getprop(lw~"tmp/presampling-flag") == 0) + {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} +else + {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} + +# get tile center coordinates + +var blat = getprop(lw~"tiles/tmp/latitude-deg"); +var blon = getprop(lw~"tiles/tmp/longitude-deg"); +calc_geo(blat); + +# get probabilistic values for the weather parameters + +var vis = 15000.0 + rand() * 5000.0; +var T = 13.0 + rand() * 8.0; +var spread = 2.0 + 2.0 * rand(); +var D = T - spread; +var p = 1005 + rand() * 10.0; p = adjust_p(p); + +# first weather info for tile (lat, lon, visibility, temperature, dew point, pressure) + +# 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); + +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); + +# 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); + +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); + +# altitude for the lowest layer +var alt = spread * 1000.0; + + +# followed by random patches of Cirrostratus + +for (var i=0; i<3; i=i+1) + { + var x = 2.0 * (rand()-0.5) * 18000; + var y = 2.0 * (rand()-0.5) * 5000 - 15000; + var beta = (rand() -0.5) * 180.0; + local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 15000 + alt + alt_offset,4,2300.0,0.2,4,2300.0,0.2,alpha+beta,1.0); +local_weather.randomize_pos("Cirrostratus",300.0,600.0,600.0,alpha+beta); + } + +# patches of thin Altostratus + +for (var i=0; i<14; i=i+1) + { + var x = 2.0 * (rand()-0.5) * 18000; + var y = 2.0 * (rand()-0.5) * 9000 - 10000.0; + var beta = (rand() -0.5) * 180.0; + local_weather.create_streak("Stratus (thin)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset +12000.0,4,950.0,0.2,6,950.0,0.2,alpha+beta,1.0); +local_weather.randomize_pos("Stratus (thin)",300.0,500.0,500.0,alpha+beta); + } + +# patches of structured Stratus + +for (var i=0; i<10; i=i+1) + { + var x = 2.0 * (rand()-0.5) * 9000; + var y = 2.0 * (rand()-0.5) * 9000 + 2000; + var beta = (rand() -0.5) * 180.0; + local_weather.create_streak("Stratus (structured)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset+9000.0,5,900.0,0.2,7,900.0,0.2,alpha+beta,1.0); +local_weather.randomize_pos("Stratus (structured)",300.0,500.0,500.0,alpha+beta); + } + + +# merging with a broken Stratus layer + +var x = 0.0; +var y = 8000.0; + +local_weather.create_streak("Stratus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset +5000.0,30,0.0,0.2,10,0.0,0.2,alpha,1.0); +local_weather.randomize_pos("Stratus",1000.0,20000.0,12000.0,alpha); + +tile_finished(); + +} + + +#################################### +# Warmfront 3 +#################################### + + +var set_warmfront3_tile = func { + +tile_start(); + +setprop(lw~"tiles/code","warmfront3"); + +var x = 0.0; +var y = 0.0; +var lat = 0.0; +var lon = 0.0; + + + +var alpha = getprop(lw~"tmp/tile-orientation-deg"); +var phi = alpha * math.pi/180.0; + +if (getprop(lw~"tmp/presampling-flag") == 0) + {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} +else + {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} + +# get tile center coordinates + +var blat = getprop(lw~"tiles/tmp/latitude-deg"); +var blon = getprop(lw~"tiles/tmp/longitude-deg"); +calc_geo(blat); + +# get probabilistic values for the weather parameters + +var vis = 12000.0 + rand() * 3000.0; +var T = 15.0 + rand() * 7.0; +var spread = 2.0 + 1.0 * rand(); +var D = T - spread; +var p = 1005 + rand() * 10.0; p = adjust_p(p); + +# first weather info for tile (lat, lon, visibility, temperature, dew point, pressure) + +# 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); + +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); + +# 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); + +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); + +# altitude for the lowest layer +var alt = spread * 1000.0; + + +# closed Stratus layer + +var x = 0.0; +var y = -8000.0; + +local_weather.create_streak("Stratus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset +1000.0,32,1250.0,0.2,20,1250.0,0.2,alpha,1.0); +local_weather.randomize_pos("Stratus",500.0,400.0,400.0,alpha); + + + + +# merging with a Nimbostratus layer + +var x = 0.0; +var y = 8000.0; + +local_weather.create_streak("Nimbus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,32,1250.0,0.0,20,1250.0,0.0,alpha,1.0); +local_weather.randomize_pos("Nimbus",500.0,200.0,200.0,alpha); + + +# some rain beneath the stratus + +x=0.0; y = -10000.0; +local_weather.create_effect_volume(3, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 20000.0, 10000.0, alpha, 0.0, alt+alt_offset+1000, vis * 0.7, 0.1, -1, -1, -1,0 ); + +# heavier rain beneath the Nimbostratus + +x=0.0; y = 10000.0; +local_weather.create_effect_volume(3, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 20000.0, 10000.0, alpha, 0.0, alt+alt_offset, vis * 0.5, 0.3, -1, -1, -1,0 ); + +tile_finished(); + +} + + +#################################### +# Warmfront 4 +#################################### + + +var set_warmfront4_tile = func { + +tile_start(); + +setprop(lw~"tiles/code","warmfront4"); + +var x = 0.0; +var y = 0.0; +var lat = 0.0; +var lon = 0.0; + + + +var alpha = getprop(lw~"tmp/tile-orientation-deg"); +var phi = alpha * math.pi/180.0; + +if (getprop(lw~"tmp/presampling-flag") == 0) + {var alt_offset = getprop(lw~"tmp/tile-alt-offset-ft");} +else + {var alt_offset = getprop(lw~"tmp/tile-alt-layered-ft");} + +# get tile center coordinates + +var blat = getprop(lw~"tiles/tmp/latitude-deg"); +var blon = getprop(lw~"tiles/tmp/longitude-deg"); +calc_geo(blat); + +# get probabilistic values for the weather parameters + +var vis = 12000.0 + rand() * 3000.0; +var T = 17.0 + rand() * 6.0; +var spread = 1.0 + 1.0 * rand(); +var D = T - spread; +var p = 1005 + rand() * 10.0; p = adjust_p(p); + +# first weather info for tile (lat, lon, visibility, temperature, dew point, pressure) + +# 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); + +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); + +# 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); + +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); + +# altitude for the lowest layer +var alt = spread * 1000.0; + +# low Nimbostratus layer + +var x = 0.0; +var y = -5000.0; + +local_weather.create_streak("Nimbus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,32,1250.0,0.0,24,1250.0,0.0,alpha,1.0); +local_weather.randomize_pos("Nimbus",500.0,200.0,200.0,alpha); + +# a little patchy structured Stratus above for effect + +create_2_8_sstratus(blat, blon, alt+alt_offset+3000.0, alpha); + +# eventually breaking up + +var x = 0.0; +var y = 14000.0; + +local_weather.create_streak("Nimbus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,25,1600.0,0.2,9,1400.0,0.3,alpha,1.0); +local_weather.randomize_pos("Nimbus",500.0,200.0,200.0,alpha); + + +# rain beneath the Nimbostratus + +x=0.0; y = -5000.0; +local_weather.create_effect_volume(3, blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 20000.0, 15000.0, alpha, 0.0, alt+alt_offset, vis * 0.5, 0.3, -1, -1, -1,0 ); + +tile_finished(); + +} + + #################################### # Altocumulus sky # cloud count 1400/tile @@ -939,26 +1708,8 @@ tile_finished(); } -#################################### -# Cold front -#################################### -var set_coldfront_tile = func { - - -var lat = getprop("position/latitude-deg"); -var lon = getprop("position/longitude-deg"); -calc_geo(lat); - -local_weather.create_streak("Altocumulus",lat, lon-0.05, 12000.0, 7, 800, 0.4, 13, 1100, 0.4, 30.0, 1.0); -local_weather.randomize_pos("Altocumulus",500.0,500.0,500.0,30.0); -local_weather.create_streak("Cumulus", lat,lon-0.1,3000,19,700.0,0.2,36,800.0,0.2,30.0,1.0); -local_weather.randomize_pos("Cumulus",600.0,600.0,600.0,30.0); -local_weather.create_streak("Cumulonimbus", lat,lon-0.12,2800,1,5000.0,0.2,3,6000.0,0.4,30.0,1.0); -local_weather.randomize_pos("Cumulonimbus",1000.0,1000.0,1000.0,30.0); -} - #################################### # Cirrus sky #################################### @@ -1031,6 +1782,163 @@ tile_finished(); } +#################################### +# METAR +#################################### + +var set_METAR_tile = func { + +tile_start(); + +var x = 0.0; +var y = 0.0; +var lat = 0.0; +var lon = 0.0; + +setprop(lw~"tiles/code","METAR"); # to be replaced + +var alpha = getprop(lw~"METAR/wind-direction-deg"); +var phi = alpha * math.pi/180.0; +var alt_offset = getprop(lw~"METAR/altitude-ft"); + +# get the local time of the day in seconds + +var t = getprop("sim/time/utc/day-seconds"); +t = t + getprop("sim/time/local-offset"); + +# get tile center coordinates + +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(lw~"METAR/latitude-deg"); +var station_lon = getprop(lw~"METAR/longitude-deg"); + +# get the weather parameters + +var vis = getprop(lw~"METAR/visibility-m"); +var T = getprop(lw~"METAR/temperature-degc"); +var D = getprop(lw~"METAR/dewpoint-degc"); +var p = getprop(lw~"METAR/pressure-inhg"); +var rain_norm = getprop(lw~"METAR/rain-norm"); +var snow_norm = getprop(lw~"METAR/snow-norm"); + +# and set the corresponding station +local_weather.set_weather_station(station_lat, station_lon, vis, T, D, p); + +# now get the cloud layer info + +var layers = props.globals.getNode(lw~"METAR", 1).getChildren("layer"); +var n_layers = size(layers); # the system initializes with 4 layers, but who knows... +var n = 0; # start with lowest layer + +# now determine the nature of the lowest layer + +var cumulus_flag = 1; # default assumption - the lowest layer is cumulus +var cover_low = layers[0].getNode("cover-oct").getValue(); +var alt_low = layers[0].getNode("alt-agl-ft").getValue(); + +# first check a few obvious criteria + +if (cover_low == 8) {cumulus_flag = 0;} # overcast sky is unlikely to be Cumulus, and we can't render it anyway +if ((rain_norm > 0.0) or (snow_norm > 0.0)) {cumulus_flag = 0;} # Cumulus usually doesn't rain +if (alt_low > 7000.0) {cumulus_flag = 0;} # Cumulus are low altitude clouds + +# now try matching time evolution of cumuli + +if ((cover_low == 5) or (cover_low == 6) or (cover_low == 7)) # broken + { + if ((t < 39600) or (t > 68400)) {cumulus_flag = 0;} # not before 11:00 and not after 19:00 + } + +if ((cover_low == 3) or (cover_low == 4)) # scattered + { + if ((t < 32400) or (t > 75600)) {cumulus_flag = 0;} # not before 9:00 and not after 21:00 + } + +# now see if there is a layer shading convective development + +var coverage_above = layers[1].getNode("cover-oct").getValue(); +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;} + +# 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 ((cover_low < 4) and (t > 39600) and (t < 68400)) {var strength = 0.4;} + else {var strength = 1.0;} + local_weather.create_cumosys(blat,blon, alt_low+alt_offset,get_n(strength), 20000.0); + n = n + 1; # do not start parsing with lowest layer + } + + +for (var i = n; i 4)) + {create_6_8_stratus(blat, blon, altitude+alt_offset, alpha);} + else if ((cover == 3) or (cover == 4)) + { + var rn = rand(); + if (rn > 0.75) + {create_4_8_stratus(blat, blon, altitude+alt_offset, alpha);} + else if (rn > 0.5) + {create_4_8_stratus_patches(blat, blon, altitude+alt_offset, alpha);} + else if (rn > 0.25) + {create_4_8_sstratus_patches(blat, blon, altitude+alt_offset, alpha);} + else if (rn > 0.0) + {create_4_8_sstratus_undulatus(blat, blon, altitude+alt_offset, alpha);} + } + else + { + var rn = rand(); + if (rn > 0.5) + {create_2_8_stratus(blat, blon, altitude+alt_offset, alpha);} + else if (rn > 0.0) + {create_2_8_sstratus(blat, blon, altitude+alt_offset, alpha);} + } + } # end if altitude + else if ((altitude > 9000.0) and (altitude < 20000.0)) # select thin cloud layers + { + if (cover == 8) + {create_8_8_cirrostratus(blat, blon, altitude+alt_offset, alpha);} + else if (cover > 2) + { + rn = rand(); + if (rn > 0.5) + {create_4_8_tstratus_patches(blat, blon, altitude+alt_offset, alpha);} + else if (rn > 0.0) + {create_4_8_tstratus_undulatus(blat, blon, altitude+alt_offset, alpha);} + } + else + {create_2_8_tstratus(blat, blon, altitude+alt_offset, alpha);} + } # end if altitude + } # end for + +# now that the weather info is used, set the flag so that offline mode starts unless a new METAR +# is available for the next tile + +setprop(lw~"METAR/available-flag",0); + + +tile_finished(); +} + #################################### # mid-level cloud setup calls #################################### @@ -1041,6 +1949,11 @@ local_weather.create_streak("Stratus",lat, lon, alt,32,1250.0,0.0,32,1250.0,0.0, local_weather.randomize_pos("Stratus",500.0,400.0,400.0,alpha); } +var create_8_8_cirrostratus = func (lat, lon, alt, alpha) { + +local_weather.create_streak("Cirrostratus",lat, lon, alt,30,1250.0,0.0,30,1250.0,0.0,alpha,1.0); +local_weather.randomize_pos("Cirrostratus",500.0,400.0,400.0,alpha); +} var create_8_8_nimbus = func (lat, lon, alt, alpha) { @@ -1245,6 +2158,21 @@ local_weather.randomize_pos("Cirrostratus",300.0,600.0,600.0,alpha+beta); } +var create_2_8_cirrocumulus = func (lat, lon, alt, alpha) { + +var phi = alpha * math.pi/180.0; + +for (var i=0; i<3; i=i+1) + { + var x = 2.0 * (rand()-0.5) * 12000; + var y = 2.0 * (rand()-0.5) * 12000; + 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,3,4000.0,0.2,3,4000.0,0.2,alpha+beta,1.0); +local_weather.randomize_pos("Cirrocumulus (cloudlet)",300.0,1000.0,1000.0,alpha+beta); + } + +} + var create_stratocumulus_bank = func (lat, lon, alt, alpha) { @@ -1254,9 +2182,9 @@ var y = 2.0 * (rand()-0.5) * 10000; var tri = 1.5 + 1.5*rand(); var beta = (rand() -0.5) * 60.0; -local_weather.create_streak("Cumulus",lat+get_lat(x,y+4000,phi), lon+get_lon(x,y+4000,phi), alt,15,400.0,0.2,20,400.0,0.2,alpha+90.0+beta,tri); +local_weather.create_streak("Cumulus",lat+get_lat(x,y+6000,phi), lon+get_lon(x,y+6000,phi), alt,15,600.0,0.2,20,600.0,0.2,alpha+90.0+beta,tri); local_weather.randomize_pos("Cumulus",500.0,400.0,400.0,alpha+90.0+beta); -local_weather.create_streak("Cumulus",lat+get_lat(x,y-4000,phi), lon+get_lon(x,y-4000,phi), alt,15,400.0,0.2,20,400.0,0.2,alpha+270.0+beta,tri); +local_weather.create_streak("Cumulus",lat+get_lat(x,y-6000,phi), lon+get_lon(x,y-6000,phi), alt,15,600.0,0.2,20,600.0,0.2,alpha+270.0+beta,tri); local_weather.randomize_pos("Cumulus",500.0,400.0,400.0,alpha+270.0+beta); } @@ -1267,6 +2195,63 @@ local_weather.randomize_pos(type,height,x1,x2,alpha); } +var create_small_thunderstorm = func(lat, lon, alt, alpha) { + +var scale = 0.7 + rand() * 0.3; + +local_weather.create_layer("Stratus", lat, lon, alt, 1000.0, 4000.0 * scale, 4000.0 * scale, 0.0, 1.0, 0.3, 1, 1.0); + +local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt+2000, 15000.0, 3000.0 * scale, 3000.0 * scale, 0.0, 2.0, 0.0, 0, 0.0); + +# set the exclusion region for the Cumulus layer +append(elat, lat); append(elon, lon); append(erad, 4000.0 * scale * 1.2); + +# set precipitation, visibility, updraft and turbulence in the cloud + +local_weather.create_effect_volume(1, lat, lon, 4000.0 * 0.7 * scale, 4000.0 * 0.7 * scale , 0.0, 0.0, 20000.0, 600.0, 0.8, -1, 0.6, 15.0,1 ); + +} + +var create_medium_thunderstorm = func(lat, lon, alt, alpha) { + +var scale = 0.7 + rand() * 0.3; + +local_weather.create_layer("Nimbus", lat, lon, alt, 500.0, 6000.0 * scale, 6000.0 * scale, 0.0, 1.0, 0.3, 1, 1.5); +local_weather.create_layer("Stratus", lat, lon, alt+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0); +local_weather.create_layer("Fog (thick)", lat, lon, alt+4000, 6000.0, 3400.0 * scale, 3400.0 * scale, 0.0, 1.5, 0.3, 0, 0.0); +local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt+10000, 10000.0, 3600.0 * scale, 3600.0 * scale, 0.0, 1.2, 0.0, 0, 0.0); + +# set the exclusion region for the Cumulus layer +append(elat, lat); append(elon, lon); append(erad, 6000.0 * scale * 1.2); + +# set precipitation, visibility, updraft and turbulence in the cloud + +local_weather.create_effect_volume(1, lat, lon, 6000.0 * 0.7 * scale, 6000.0 * 0.7 * scale , 0.0, 0.0, 20000.0, 500.0, 1.0, -1, 0.8, 20.0,1 ); + +} + +var create_big_thunderstorm = func(lat, lon, alt, alpha) { + +# var radius = 4000 + rand() * 4000; +var phi = alpha * math.pi/180.0; + +var scale = 0.8; + +local_weather.create_layer("Nimbus", lat, lon, alt, 500.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.25, 1, 1.5); +local_weather.create_layer("Stratus", lat, lon, alt+1500, 1000.0, 7200.0 * scale, 7200.0 * scale, 0.0, 1.0, 0.3, 0, 0.0); +local_weather.create_layer("Fog (thick)", lat, lon, alt+5000, 3000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 0.7, 0.3, 0, 0.0); +local_weather.create_layer("Fog (thick)", lat+get_lat(0,-1000,phi), lon+get_lon(0,-1000,phi), alt+12000, 4000.0, 6300.0 * scale, 6300.0 * scale, 0.0, 0.7, 0.3, 0, 0.0); +local_weather.create_layer("Stratus", lat+get_lat(0,-2000,phi), lon+get_lon(0,-2000,phi), alt+17000, 1000.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0); +local_weather.create_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0); +local_weather.create_layer("Stratus (thin)", lat+get_lat(0,-4000,phi), lon+get_lon(0,-4000,phi), alt+24000, 1000.0, 11500.0 * scale, 11500.0 * scale, 0.0, 2.0, 0.3, 0, 0.0); + +# set the exclusion region for the Cumulus layer +append(elat, lat); append(elon, lon); append(erad, 7500.0 * scale * 1.2); + +local_weather.create_effect_volume(1, lat, lon, 7500.0 * 0.7 * scale, 7500.0 * 0.7 * scale , 0.0, 0.0, 20000.0, 500.0, 1.0, -1, 1.0, 25.0,1 ); + +} + ################### # helper functions ################### @@ -1293,6 +2278,27 @@ var get_n = func(strength) { return int(4000 * strength); } + +################################## +# continuity condition of pressure +################################## + +var adjust_p = func (p) { + +if (last_pressure == 0.0) {last_pressure = p; return p;} + +var pressure_difference = p - last_pressure; + +if (pressure_difference > 2.0) {var pout = last_pressure + 3.0;} +else if (pressure_difference < -2.0) {var pout = last_pressure - 3.0;} +else {var pout = p;} + +last_pressure = pout; + +return pout; +} + + ################### # global variables ################### @@ -1306,7 +2312,12 @@ var m_to_ft = 1.0/ft_to_m; var inhg_to_hp = 33.76389; var hp_to_inhg = 1.0/inhg_to_hp; +var last_pressure = 0.0; + var lon_to_m = 0.0; # needs to be calculated dynamically var m_to_lon = 0.0; # we do this on startup var lw = "/local-weather/"; +var elat = []; +var elon = []; +var erad = []; diff --git a/gui/dialogs/local_weather_tiles.xml b/gui/dialogs/local_weather_tiles.xml index e1d072ca7..5a57b6215 100644 --- a/gui/dialogs/local_weather_tiles.xml +++ b/gui/dialogs/local_weather_tiles.xml @@ -6,7 +6,7 @@ local_weather_tiles 300 - 300 + 330 false @@ -19,7 +19,7 @@ 10 - 220 + 270 280 25 true @@ -30,6 +30,13 @@ Low-pressure-border Low-pressure Low-pressure-core + Cold-sector + Coldfront + Warmfront-1 + Warmfront-2 + Warmfront-3 + Warmfront-4 + Tropical --- @@ -39,7 +46,7 @@ Blue thermals - + Test tile dialog-apply @@ -48,13 +55,13 @@ 10 - 170 + 220 115 - 170 + 220 40 25 /local-weather/tmp/tile-orientation-deg @@ -62,13 +69,13 @@ 155 - 170 + 220 240 - 170 + 220 50 25 /local-weather/tmp/tile-alt-offset-ft @@ -76,13 +83,13 @@ 10 - 130 + 180 - + 150 - 130 + 180 140 25 true @@ -90,6 +97,7 @@ single tile repeat tile realistic weather + METAR dialog-apply @@ -97,7 +105,7 @@ 10 - 90 + 140 15 15 @@ -109,7 +117,7 @@ 150 - 90 + 140 15 15 @@ -121,7 +129,7 @@ 10 - 60 + 110 15 15 @@ -133,7 +141,7 @@ 150 - 60 + 110 15 15 @@ -143,6 +151,37 @@ + + 10 + 75 + + + + + 10 + 50 + + + + + 80 + 50 + 90 + 20 + 0.3 + 1.5 + /local-weather/config/thermal-properties + + dialog-apply + + + + + 180 + 50 + + + 10 10