Date started: 28.05.99
Called by: main program
- ---------- Copyright (C) 1999 Christian Mayer (vader@t-online.de) ----------
+ -------- Copyright (C) 1999 Christian Mayer (fgfs@christianmayer.de) --------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
20.06.1999 Christian Mayer added lots of consts
11.10.1999 Christian Mayer changed set<> to map<> on Bernie Bright's
suggestion
+19.10.1999 Christian Mayer change to use PLIB's sg instead of Point[2/3]D
+ and lots of wee code cleaning
+14.12.1999 Christian Mayer Changed the internal structure to use Dave
+ Eberly's spherical interpolation code. This
+ stops our dependancy on the (ugly) voronoi
+ code and simplyfies the code structure a lot.
+07.05.2000 Tony Peden Added functionality to get the weather data
+ on 'the bus'
+18.05.2000 Christian Mayer Minor clean-ups. Changed the code to use
+ FGWeatherUtils.h for unit conversion
+18.07.2001 Christian Mayer Added the posibility to limit the amount of
+ stations for a faster init.
*****************************************************************************/
/****************************************************************************/
/* INCLUDES */
/****************************************************************************/
-#include "FGLocalWeatherDatabase.h"
-#include "FGVoronoi.h"
-#include "fg_constants.h"
+#include <simgear/compiler.h>
+#include <simgear/constants.h>
#include <Aircraft/aircraft.hxx>
-#include <Include/fg_constants.h>
+#include <Main/fg_props.hxx>
-/****************************************************************************/
-/********************************** CODE ************************************/
-/****************************************************************************/
+#include "FGLocalWeatherDatabase.h"
-/****************************************************************************/
-/* return the index (better: ID) of the area with point p */
-/****************************************************************************/
-unsigned int FGLocalWeatherDatabase::AreaWith(const Point2D& p) const
-{
-
- for (FGMicroWeatherList::size_type i = 0; i != WeatherAreas.size(); i++)
- {
- if (WeatherAreas[i].hasPoint(p) == true)
- return i+1;
- }
+#include "FGWeatherParse.h"
- return 0; //nothing found
-}
+#include "FGWeatherUtils.h"
/****************************************************************************/
-/* make tiles out of points on a 2D plane */
+/********************************** CODE ************************************/
/****************************************************************************/
-void FGLocalWeatherDatabase::tileLocalWeather(const FGPhysicalProperties2DVector& EntryList)
-{
- FGVoronoiInputList input;
-
- for (FGPhysicalProperties2DVector::const_iterator it1 = EntryList.begin(); it1 != EntryList.end(); it1++)
- input.push_back(FGVoronoiInput(it1->p, *it1));
-
- FGVoronoiOutputList output = Voronoiate(input);
- for (FGVoronoiOutputList::iterator it2 = output.begin(); it2 != output.end(); it2++)
- WeatherAreas.push_back(FGMicroWeather(it2->value, it2->boundary));
-}
-
-/****************************************************************************/
-/* Constructor and Destructor */
-/****************************************************************************/
FGLocalWeatherDatabase* FGLocalWeatherDatabase::theFGLocalWeatherDatabase = 0;
FGLocalWeatherDatabase *WeatherDatabase;
-FGLocalWeatherDatabase::FGLocalWeatherDatabase(const Point3D& posititon, const WeatherPrecition& visibility, const DatabaseWorkingType& type)
+void FGLocalWeatherDatabase::init( const WeatherPrecision visibility,
+ const DatabaseWorkingType type,
+ const string &root )
{
cerr << "Initializing FGLocalWeatherDatabase\n";
cerr << "-----------------------------------\n";
if (theFGLocalWeatherDatabase)
{
- //FG_LOG( FG_GENERAL, FG_ALERT, "Error: only one local weather allowed" );
cerr << "Error: only one local weather allowed";
exit(-1);
}
setWeatherVisibility(visibility);
- //WeatherVisibility = visibility;
- DatabaseStatus = type;
- global = 0; //just get sure...
- last_known_position = posititon;
+ DatabaseStatus = type;
+ database_logic = 0; //just get sure...
- theFGLocalWeatherDatabase = this;
+ Thunderstorm = false;
+ //I don't need to set theThunderstorm as Thunderstorm == false
switch(DatabaseStatus)
{
case use_global:
{
- global = new FGGlobalWeatherDatabase; //initialize GlobalDatabase
- global->setDatabaseStatus(FGGlobalWeatherDatabase_working);
- tileLocalWeather(global->getAll(posititon, WeatherVisibility, 3));
+ cerr << "Error: there's no global database anymore!\n";
+ exit(-1);
+ }
+ break;
+
+ case use_internet:
+ {
+ FGWeatherParse *parsed_data = new FGWeatherParse();
+ sgVec2 *p;
+ unsigned int *f;
+ string path_to_weather = root + "/weather/current.txt.gz";
+ parsed_data->input( path_to_weather.c_str() );
+ unsigned int n = parsed_data->stored_stations();
+ int m = fgGetInt("/environment/weather/max-stations", -1);
+
+ if ( ( m < 0 ) || ( m > n ) )
+ {
+ m = n;
+
+ p = new sgVec2[n];
+ f = new unsigned int[n];
+
+ // fill the database
+ for (unsigned int i = 0; i < n; i++)
+ {
+ f[i] = i;
+ database_data[i] = parsed_data->getFGPhysicalProperties(i);
+ parsed_data->getPosition(i, p[i]);
+
+ if ( (i%100) == 0)
+ cerr << ".";
+ }
+ }
+ else
+ { // we have to limit the amount of stations
+
+ //store the "distance" between the station and the current
+ //position. As the distance is calculated from the lat/lon
+ //values it's not worth much - but it's good enough for
+ //comparison
+ map<float, unsigned int> squared_distance;
+ sgVec2 cur_pos;
+
+ cur_pos[0] = cache->last_known_position[0];
+ cur_pos[1] = cache->last_known_position[1];
+
+ unsigned int i;
+ for( i = 0; i < n; i++ )
+ {
+ sgVec2 pos;
+ parsed_data->getPosition(i, pos);
+ squared_distance[sgDistanceSquaredVec2(cur_pos, pos)] = i;
+ }
+
+ p = new sgVec2 [m];
+ f = new unsigned int[m];
+
+ map<float, unsigned int>::const_iterator ci;
+ ci = squared_distance.begin();
+
+ // fill the database
+ for ( i = 0; i < m; i++ )
+ {
+ f[i] = i;
+ database_data.push_back( parsed_data->getFGPhysicalProperties(ci->second) );
+ parsed_data->getPosition(ci->second, p[i]);
+
+ if ( (i%100) == 0)
+ cerr << ".";
+
+ ci++;
+ }
+ }
+
+ // free the memory of the parsed data to ease the required memory
+ // for the very memory consuming spherical interpolation
+ delete parsed_data;
+
+ //and finally init the interpolation
+ cerr << "\nInitialiating Interpolation. (2-3 minutes on a PII-350 for ca. 3500 stations)\n";
+ database_logic = new SphereInterpolate(m, p, f);
+
+ //and free my allocations:
+ delete[] p;
+ delete[] f;
+ cerr << "Finished weather init.\n";
+
}
break;
case manual:
case default_mode:
{
- vector<Point2D> emptyList;
- WeatherAreas.push_back(FGMicroWeather(FGPhysicalProperties2D(), emptyList)); //in these cases I've only got one tile
+ double x[2] = {0.0, 0.0}; //make an standard weather that's the same at the whole world
+ double y[2] = {0.0, 0.0}; //make an standard weather that's the same at the whole world
+ double z[2] = {1.0, -1.0}; //make an standard weather that's the same at the whole world
+ unsigned int f[2] = {0, 0};
+ database_data.push_back( FGPhysicalProperties() ); // == database_date[0]
+ database_logic = new SphereInterpolate(2,x,y,z,f);
}
break;
default:
cerr << "FGLocalWeatherDatabase error: Unknown database type specified!\n";
};
+
+ cache->latitude_deg = fgGetNode("/position/latitude-deg" );
+ cache->longitude_deg = fgGetNode("/position/longitude-deg");
+ cache->altitude_ft = fgGetNode("/position/altitude-ft" );
+
}
-FGLocalWeatherDatabase::~FGLocalWeatherDatabase()
+void FGLocalWeatherDatabase::bind()
{
- //Tidying up:
+ fgTie("/environment/weather/wind-north-mps", this, &FGLocalWeatherDatabase::get_wind_north);
+ fgTie("/environment/weather/wind-east-mps", this, &FGLocalWeatherDatabase::get_wind_east);
+ fgTie("/environment/weather/wind-up-mps", this, &FGLocalWeatherDatabase::get_wind_up);
+ fgTie("/environment/weather/temperature-K", this, &FGLocalWeatherDatabase::get_temperature);
+ fgTie("/environment/weather/air-pressure-Pa", this, &FGLocalWeatherDatabase::get_air_pressure);
+ fgTie("/environment/weather/vapor-pressure-Pa", this, &FGLocalWeatherDatabase::get_vapor_pressure);
+ fgTie("/environment/weather/air-density", this, &FGLocalWeatherDatabase::get_air_density);
+
- //delete every stored area
- WeatherAreas.erase(WeatherAreas.begin(), WeatherAreas.end());
+ SGPropertyNode * station_nodes = fgGetNode("/environment/weather");
+ if (station_nodes == 0) {
+ cerr << "No weatherstations (/environment/weather)!!";
+ return;
+ }
+
+ int index = 0;
+ for(vector<FGPhysicalProperties>::iterator it = database_data.begin(); it != database_data.end(); it++)
+ {
+ SGPropertyNode * station = station_nodes->getNode("station", index, true);
+
+ station -> tie("air-pressure-Pa",
+ SGRawValueMethods<FGAirPressureItem,WeatherPrecision>(
+ database_data[0].AirPressure,
+ &FGAirPressureItem::getValue,
+ &FGAirPressureItem::setValue)
+ ,false);
+
+ int i;
+ for( i = 0; i < database_data[index].Wind.size(); i++)
+ {
+ SGPropertyNode * wind = station->getNode("wind", i, true);
+ wind -> tie("north-mps",
+ SGRawValueMethodsIndexed<FGPhysicalProperties,WeatherPrecision>(
+ database_data[index], i,
+ &FGPhysicalProperties::getWind_x,
+ &FGPhysicalProperties::setWind_x)
+ ,false);
+ wind -> tie("east-mps",
+ SGRawValueMethodsIndexed<FGPhysicalProperties,WeatherPrecision>(
+ database_data[index], i,
+ &FGPhysicalProperties::getWind_y,
+ &FGPhysicalProperties::setWind_y)
+ ,false);
+ wind -> tie("up-mps",
+ SGRawValueMethodsIndexed<FGPhysicalProperties,WeatherPrecision>(
+ database_data[index], i,
+ &FGPhysicalProperties::getWind_z,
+ &FGPhysicalProperties::setWind_z)
+ ,false);
+ wind -> tie("altitude-m",
+ SGRawValueMethodsIndexed<FGPhysicalProperties,WeatherPrecision>(
+ database_data[index], i,
+ &FGPhysicalProperties::getWind_a,
+ &FGPhysicalProperties::setWind_a)
+ ,false);
+ }
+
+ for( i = 0; i < database_data[index].Temperature.size(); i++)
+ {
+ SGPropertyNode * temperature = station->getNode("temperature", i, true);
+ temperature -> tie("value-K",
+ SGRawValueMethodsIndexed<FGPhysicalProperties,WeatherPrecision>(
+ database_data[index], i,
+ &FGPhysicalProperties::getTemperature_x,
+ &FGPhysicalProperties::setTemperature_x)
+ ,false);
+ temperature -> tie("altitude-m",
+ SGRawValueMethodsIndexed<FGPhysicalProperties,WeatherPrecision>(
+ database_data[index], i,
+ &FGPhysicalProperties::getTemperature_a,
+ &FGPhysicalProperties::setTemperature_a)
+ ,false);
+ }
- //delete global database if necessary
- if (DatabaseStatus == use_global)
- delete global;
+ for( i = 0; i < database_data[index].VaporPressure.size(); i++)
+ {
+ SGPropertyNode * vaporpressure = station->getNode("vapor-pressure", i, true);
+ vaporpressure -> tie("value-Pa",
+ SGRawValueMethodsIndexed<FGPhysicalProperties,WeatherPrecision>(
+ database_data[index], i,
+ &FGPhysicalProperties::getVaporPressure_x,
+ &FGPhysicalProperties::setVaporPressure_x)
+ ,false);
+ vaporpressure -> tie("altitude-m",
+ SGRawValueMethodsIndexed<FGPhysicalProperties,WeatherPrecision>(
+ database_data[index], i,
+ &FGPhysicalProperties::getVaporPressure_a,
+ &FGPhysicalProperties::setVaporPressure_a)
+ ,false);
+ }
+
+ index++;
+ }
+}
+
+void FGLocalWeatherDatabase::unbind()
+{
+ fgUntie("/environment/weather/wind-north-mps");
+ fgUntie("/environment/weather/wind-east-mps");
+ fgUntie("/environment/weather/wind-up-mps");
+ fgUntie("/environment/weather/temperature-K");
+ fgUntie("/environment/weather/air-pressure-Pa");
+ fgUntie("/environment/weather/vapor-pressure-Pa");
+ fgUntie("/environment/weather/air-density");
+}
+
+FGLocalWeatherDatabase::~FGLocalWeatherDatabase()
+{
+ //Tidying up:
+ delete database_logic;
}
/****************************************************************************/
/* reset the whole database */
/****************************************************************************/
-void FGLocalWeatherDatabase::reset(const DatabaseWorkingType& type)
+void FGLocalWeatherDatabase::reset(const DatabaseWorkingType type)
{
- //delete global database if necessary
- if ((DatabaseStatus == use_global) && (type != use_global))
- delete global;
-
- DatabaseStatus = type;
- if (DatabaseStatus == use_global)
- tileLocalWeather(global->getAll(last_known_position, WeatherVisibility, 3));
-
- //delete every stored area
- WeatherAreas.erase(WeatherAreas.begin(), WeatherAreas.end());
+ cerr << "FGLocalWeatherDatabase::reset isn't supported yet\n";
}
/****************************************************************************/
/* update the database. Since the last call we had dt seconds */
/****************************************************************************/
-void FGLocalWeatherDatabase::update(const WeatherPrecition& dt)
+void FGLocalWeatherDatabase::update(const WeatherPrecision dt)
{
- if (DatabaseStatus==use_global)
- global->update(dt);
+ //if (DatabaseStatus==use_global)
+ // global->update(dt);
}
-void FGLocalWeatherDatabase::update(const Point3D& p) //position has changed
+void FGLocalWeatherDatabase::update(const sgVec3& p) //position has changed
{
- last_known_position = p;
- //cerr << "****\nupdate inside\n";
- //cerr << "Parameter: " << p << "\n";
- //cerr << "****\n";
+ //uncomment this when you are using the GlobalDatabase
+ /*
+ cerr << "****\nupdate(p) inside\n";
+ cerr << "Parameter: " << p[0] << "/" << p[1] << "/" << p[2] << "\n";
+ cerr << FGPhysicalProperties2D(get(p2d), p2d);
+ cerr << "****\n";
+ */
+
}
-void FGLocalWeatherDatabase::update(const Point3D& p, const WeatherPrecition& dt) //time and/or position has changed
+void FGLocalWeatherDatabase::update(const sgVec3& p, const WeatherPrecision dt) //time and/or position has changed
{
- last_known_position = p;
-
- if (DatabaseStatus==use_global)
- global->update(dt);
}
/****************************************************************************/
/* Get the physical properties on the specified point p out of the database */
/****************************************************************************/
-FGPhysicalProperty FGLocalWeatherDatabase::get(const Point3D& p) const
-{
- unsigned int a = AreaWith(p);
- if (a != 0)
- return WeatherAreas[a-1].get(p.elev());
- else //point is outside => ask GlobalWeatherDatabase
- return global->get(p);
-}
-
FGPhysicalProperty FGLocalWeatherDatabase::get(const sgVec3& p) const
{
- Point3D temp(p[0], p[1], p[2]);
+ // check for bogous altitudes. Dunno why, but FGFS want's to know the
+ // weather at an altitude of roughly -3000 meters...
+ if (p[2] < -500.0f)
+ return FGPhysicalProperty(DatabaseEvaluate(p), -500.0f);
- unsigned int a = AreaWith(temp);
- if (a != 0)
- return WeatherAreas[a-1].get(temp.elev());
- else //point is outside => ask GlobalWeatherDatabase
- return global->get(temp);
+ return FGPhysicalProperty(DatabaseEvaluate(p), p[2]);
}
+#ifdef macintosh
+ /* fix a problem with mw compilers in that they don't know the
+ difference between the next two methods. Since the first one
+ doesn't seem to be used anywhere, I commented it out. This is
+ supposed to be fixed in the forthcoming CodeWarrior Release
+ 6. */
+#else
FGPhysicalProperties FGLocalWeatherDatabase::get(const sgVec2& p) const
{
- Point3D temp(p[0], p[1], 0.0);
-
- unsigned int a = AreaWith(temp);
- if (a != 0)
- return WeatherAreas[a-1].get();
- else //point is outside => ask GlobalWeatherDatabase
- return global->get(p);
+ return DatabaseEvaluate(p);
}
+#endif
-WeatherPrecition FGLocalWeatherDatabase::getAirDensity(const Point3D& p) const
+WeatherPrecision FGLocalWeatherDatabase::getAirDensity(const sgVec3& p) const
{
- FGPhysicalProperty dummy;
- unsigned int a = AreaWith(p);
- if (a != 0)
- dummy = WeatherAreas[a-1].get(p.elev());
- else //point is outside => ask GlobalWeatherDatabase
- dummy = global->get(p);
+ FGPhysicalProperty dummy(DatabaseEvaluate(p), p[2]);
return
(dummy.AirPressure*FG_WEATHER_DEFAULT_AIRDENSITY*FG_WEATHER_DEFAULT_TEMPERATURE) /
(dummy.Temperature*FG_WEATHER_DEFAULT_AIRPRESSURE);
}
-WeatherPrecition FGLocalWeatherDatabase::getAirDensity(const sgVec3& p) const
-{
- Point3D temp(p[0], p[1], p[2]);
-
- FGPhysicalProperty dummy;
- unsigned int a = AreaWith(temp);
- if (a != 0)
- dummy = WeatherAreas[a-1].get(temp.elev());
- else //point is outside => ask GlobalWeatherDatabase
- dummy = global->get(temp);
-
- return
- (dummy.AirPressure*FG_WEATHER_DEFAULT_AIRDENSITY*FG_WEATHER_DEFAULT_TEMPERATURE) /
- (dummy.Temperature*FG_WEATHER_DEFAULT_AIRPRESSURE);
-}
-
-/****************************************************************************/
-/* Add a weather feature at the point p and surrounding area */
-/****************************************************************************/
-void FGLocalWeatherDatabase::addWind(const WeatherPrecition alt, const Point3D& x, const Point2D& p)
-{
- unsigned int a = AreaWith(p);
- if (a != 0)
- WeatherAreas[a-1].addWind(alt, x);
-}
-
-void FGLocalWeatherDatabase::addTurbulence(const WeatherPrecition alt, const Point3D& x, const Point2D& p)
-{
- unsigned int a = AreaWith(p);
- if (a != 0)
- WeatherAreas[a-1].addTurbulence(alt, x);
-}
-
-void FGLocalWeatherDatabase::addTemperature(const WeatherPrecition alt, const WeatherPrecition x, const Point2D& p)
-{
- unsigned int a = AreaWith(p);
- if (a != 0)
- WeatherAreas[a-1].addTemperature(alt, x);
-}
-void FGLocalWeatherDatabase::addAirPressure(const WeatherPrecition alt, const WeatherPrecition x, const Point2D& p)
+void FGLocalWeatherDatabase::setSnowRainIntensity(const WeatherPrecision x, const sgVec2& p)
{
- unsigned int a = AreaWith(p);
- if (a != 0)
- WeatherAreas[a-1].addAirPressure(alt, x);
+ /* not supported yet */
}
-void FGLocalWeatherDatabase::addVaporPressure(const WeatherPrecition alt, const WeatherPrecition x, const Point2D& p)
+void FGLocalWeatherDatabase::setSnowRainType(const SnowRainType x, const sgVec2& p)
{
- unsigned int a = AreaWith(p);
- if (a != 0)
- WeatherAreas[a-1].addVaporPressure(alt, x);
+ /* not supported yet */
}
-void FGLocalWeatherDatabase::addCloud(const WeatherPrecition alt, const FGCloudItem& x, const Point2D& p)
+void FGLocalWeatherDatabase::setLightningProbability(const WeatherPrecision x, const sgVec2& p)
{
- unsigned int a = AreaWith(p);
- if (a != 0)
- WeatherAreas[a-1].addCloud(alt, x);
+ /* not supported yet */
}
-void FGLocalWeatherDatabase::setSnowRainIntensity(const WeatherPrecition& x, const Point2D& p)
+void FGLocalWeatherDatabase::setProperties(const FGPhysicalProperties2D& x)
{
- unsigned int a = AreaWith(p);
- if (a != 0)
- WeatherAreas[a-1].setSnowRainIntensity(x);
+ /* not supported yet */
}
-void FGLocalWeatherDatabase::setSnowRainType(const SnowRainType& x, const Point2D& p)
+void fgUpdateWeatherDatabase(void)
{
- unsigned int a = AreaWith(p);
- if (a != 0)
- WeatherAreas[a-1].setSnowRainType(x);
-}
+ sgVec3 position;
+
+ sgSetVec3(position,
+ current_aircraft.fdm_state->get_Latitude(),
+ current_aircraft.fdm_state->get_Longitude(),
+ current_aircraft.fdm_state->get_Altitude() * SG_FEET_TO_METER);
+
+ WeatherDatabase->update( position );
+
+ // get the data on 'the bus' for the FDM
-void FGLocalWeatherDatabase::setLightningProbability(const WeatherPrecition& x, const Point2D& p)
-{
- unsigned int a = AreaWith(p);
- if (a != 0)
- WeatherAreas[a-1].setLightningProbability(x);
-}
+ /* FGPhysicalProperty porperty = WeatherDatabase->get(position);
-void FGLocalWeatherDatabase::addProperties(const FGPhysicalProperties2D& x)
-{
- if (DatabaseStatus==use_global)
- {
- global->add(x);
-
- //BAD, BAD, BAD thing I'm doing here: I'm adding to the global database a point that
- //changes my voronoi diagram but I don't update it! instead I'm changing one local value
- //that could be anywhere!!
- //This only *might* work when the plane moves so far so fast that the diagram gets new
- //calculated soon...
- unsigned int a = AreaWith(x.p);
- if (a != 0)
- WeatherAreas[a-1].setStoredWeather(x);
- }
- else
- {
- unsigned int a = AreaWith(x.p);
- if (a != 0)
- WeatherAreas[a-1].setStoredWeather(x);
- }
-}
+ current_aircraft.fdm_state->set_Static_temperature( Kelvin2Rankine(porperty.Temperature) );
+ current_aircraft.fdm_state->set_Static_pressure( Pascal2psf(porperty.AirPressure) );
-void FGLocalWeatherDatabase::setProperties(const FGPhysicalProperties2D& x)
-{
- if (DatabaseStatus==use_global)
- {
- global->change(x);
-
- //BAD, BAD, BAD thing I'm doing here: I'm adding to the global database a point that
- //changes my voronoi diagram but I don't update it! Instead I'm changing one local value
- //that could be anywhere!!
- //This only *might* work when the plane moves so far so fast that the diagram gets newly
- //calculated soon...
- unsigned int a = AreaWith(x.p);
- if (a != 0)
- WeatherAreas[a-1].setStoredWeather(x);
- }
- else
- {
- unsigned int a = AreaWith(x.p);
- if (a != 0)
- WeatherAreas[a-1].setStoredWeather(x);
- }
-}
+ current_aircraft.fdm_state->set_Density( SIdensity2JSBsim( Density(porperty.AirPressure, porperty.Temperature) ) );
+
+#define MSTOFPS 3.2808 //m/s to ft/s
+ current_aircraft.fdm_state->set_Velocities_Local_Airmass(porperty.Wind[1]*MSTOFPS,
+ porperty.Wind[0]*MSTOFPS,
+ porperty.Wind[2]*MSTOFPS); */
-void fgUpdateWeatherDatabase(void)
-{
- //cerr << "FGLocalWeatherDatabase::update()\n";
- WeatherDatabase->update( Point3D(
- current_aircraft.fdm_state->get_Latitude(),
- current_aircraft.fdm_state->get_Longitude(),
- current_aircraft.fdm_state->get_Altitude() * FEET_TO_METER) );
+
}