1 // realwx_ctrl.cxx -- Process real weather data
3 // Written by David Megginson, started February 2002.
4 // Rewritten by Torsten Dreyer, August 2010
6 // Copyright (C) 2002 David Megginson - david@megginson.com
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include "realwx_ctrl.hxx"
28 #include "tiedpropertylist.hxx"
29 #include "metarproperties.hxx"
30 #include "metarairportfilter.hxx"
31 #include "fgmetar.hxx"
33 #include <Main/fg_props.hxx>
35 #include <simgear/structure/exception.hxx>
36 #include <simgear/misc/strutils.hxx>
38 #if defined(ENABLE_THREADS)
39 #include <OpenThreads/Thread>
40 #include <simgear/threads/SGQueue.hxx>
44 namespace Environment {
46 class BasicRealWxController : public RealWxController
49 BasicRealWxController( SGPropertyNode_ptr rootNode );
50 virtual ~BasicRealWxController ();
53 virtual void reinit ();
59 SGPropertyNode_ptr _rootNode;
60 SGPropertyNode_ptr _longitude_n;
61 SGPropertyNode_ptr _latitude_n;
62 SGPropertyNode_ptr _ground_elevation_n;
65 TiedPropertyList _tiedProperties;
66 MetarProperties _metarProperties;
69 /* -------------------------------------------------------------------------------- */
72 ~/enabled: bool Enables/Disables the realwx controller
73 ~/metar[1..n]: string Target property path for metar data
76 BasicRealWxController::BasicRealWxController( SGPropertyNode_ptr rootNode ) :
78 _longitude_n( fgGetNode( "/position/longitude-deg", true )),
79 _latitude_n( fgGetNode( "/position/latitude-deg", true )),
80 _ground_elevation_n( fgGetNode( "/position/ground-elev-m", true )),
82 _metarProperties( fgGetNode( rootNode->getStringValue("metar", "/environment/metar"), true ) )
86 BasicRealWxController::~BasicRealWxController()
90 void BasicRealWxController::init()
92 update(0); // fetch data ASAP
95 void BasicRealWxController::reinit()
99 void BasicRealWxController::bind()
101 _tiedProperties.setRoot( _rootNode );
102 _tiedProperties.Tie( "enabled", &_enabled );
105 void BasicRealWxController::unbind()
107 _tiedProperties.Untie();
110 /* -------------------------------------------------------------------------------- */
112 class NoaaMetarRealWxController : public BasicRealWxController {
114 NoaaMetarRealWxController( SGPropertyNode_ptr rootNode );
115 virtual ~NoaaMetarRealWxController();
116 virtual void update (double delta_time_sec);
118 class MetarLoadRequest {
120 MetarLoadRequest( const string & stationId ) {
121 _stationId = stationId;
122 _proxyHost = fgGetNode("/sim/presets/proxy/host", true)->getStringValue();
123 _proxyPort = fgGetNode("/sim/presets/proxy/port", true)->getStringValue();
124 _proxyAuth = fgGetNode("/sim/presets/proxy/authentication", true)->getStringValue();
133 double _metarTimeToLive;
134 double _positionTimeToLive;
135 double _minimumRequestInterval;
137 SGPropertyNode_ptr _metarDataNode;
138 SGPropertyNode_ptr _metarValidNode;
139 SGPropertyNode_ptr _metarStationIdNode;
142 #if defined(ENABLE_THREADS)
143 class MetarLoadThread : public OpenThreads::Thread {
145 MetarLoadThread( NoaaMetarRealWxController & controller );
146 void requestMetar( const MetarLoadRequest & metarRequest );
147 bool hasMetar() { return _responseQueue.size() > 0; }
148 string getMetar() { return _responseQueue.pop(); }
151 NoaaMetarRealWxController & _controller;
152 SGBlockingQueue <MetarLoadRequest> _requestQueue;
153 SGBlockingQueue <string> _responseQueue;
156 MetarLoadThread * _metarLoadThread;
160 NoaaMetarRealWxController::NoaaMetarRealWxController( SGPropertyNode_ptr rootNode ) :
161 BasicRealWxController(rootNode),
162 _metarTimeToLive(0.0),
163 _positionTimeToLive(0.0),
164 _minimumRequestInterval(0.0),
165 _metarDataNode(_metarProperties.get_root_node()->getNode("data",true)),
166 _metarValidNode(_metarProperties.get_root_node()->getNode("valid",true)),
167 _metarStationIdNode(_metarProperties.get_root_node()->getNode("station-id",true))
169 #if defined(ENABLE_THREADS)
170 _metarLoadThread = new MetarLoadThread(*this);
171 _metarLoadThread->start();
175 NoaaMetarRealWxController::~NoaaMetarRealWxController()
177 #if defined(ENABLE_THREADS)
178 if( _metarLoadThread ) {
179 MetarLoadRequest request("");
180 _metarLoadThread->requestMetar(request);
181 _metarLoadThread->join();
182 delete _metarLoadThread;
184 #endif // ENABLE_THREADS
187 void NoaaMetarRealWxController::update( double dt )
192 if( _metarLoadThread->hasMetar() )
193 _metarDataNode->setStringValue( _metarLoadThread->getMetar() );
195 _metarTimeToLive -= dt;
196 _positionTimeToLive -= dt;
197 _minimumRequestInterval -= dt;
199 bool valid = _metarValidNode->getBoolValue();
200 string stationId = valid ? _metarStationIdNode->getStringValue() : "";
203 if( _metarTimeToLive <= 0.0 ) {
205 _metarTimeToLive = 900;
206 _positionTimeToLive = 0;
209 if( _positionTimeToLive <= 0.0 || valid == false ) {
210 _positionTimeToLive = 60.0;
212 SGGeod pos = SGGeod::fromDeg(_longitude_n->getDoubleValue(), _latitude_n->getDoubleValue());
214 FGAirport * nearestAirport = FGAirport::findClosest(pos, 10000.0, MetarAirportFilter::instance() );
215 if( nearestAirport == NULL ) {
216 SG_LOG(SG_ALL,SG_WARN,"RealWxController::update can't find airport with METAR within 10000NM" );
220 if( stationId != nearestAirport->ident() ) {
222 stationId = nearestAirport->ident();
228 if( _minimumRequestInterval <= 0 && stationId.length() > 0 ) {
229 MetarLoadRequest request( stationId );
230 _metarLoadThread->requestMetar( request );
231 _minimumRequestInterval = 10;
237 /* -------------------------------------------------------------------------------- */
239 #if defined(ENABLE_THREADS)
240 NoaaMetarRealWxController::MetarLoadThread::MetarLoadThread( NoaaMetarRealWxController & controller ) :
241 _controller(controller)
245 void NoaaMetarRealWxController::MetarLoadThread::requestMetar( const MetarLoadRequest & metarRequest )
247 if( _requestQueue.size() > 10 ) {
248 SG_LOG(SG_ALL,SG_ALERT,
249 "NoaaMetarRealWxController::MetarLoadThread::requestMetar() more than 10 outstanding METAR requests, dropping "
250 << metarRequest._stationId );
254 _requestQueue.push( metarRequest );
257 void NoaaMetarRealWxController::MetarLoadThread::run()
260 const MetarLoadRequest request = _requestQueue.pop();
262 if( request._stationId.size() == 0 )
265 SGSharedPtr<FGMetar> result = NULL;
268 result = new FGMetar( request._stationId, request._proxyHost, request._proxyPort, request._proxyAuth );
269 } catch (const sg_io_exception& e) {
270 SG_LOG( SG_GENERAL, SG_WARN, "NoaaMetarRealWxController::fetchMetar(): can't get METAR for "
271 << request._stationId << ":" << e.getFormattedMessage().c_str() );
278 string reply = result->getData();
279 std::replace(reply.begin(), reply.end(), '\n', ' ');
280 string metar = simgear::strutils::strip( reply );
281 if( metar.length() > 0 )
282 _responseQueue.push( metar );
287 /* -------------------------------------------------------------------------------- */
289 RealWxController * RealWxController::createInstance( SGPropertyNode_ptr rootNode )
291 // string dataSource = rootNode->getStringValue("data-source", "noaa" );
292 // if( dataSource == "nwx" ) {
293 // return new NwxMetarRealWxController( rootNode );
295 return new NoaaMetarRealWxController( rootNode );
299 /* -------------------------------------------------------------------------------- */
301 } // namespace Environment