+++ /dev/null
-// Implementation of FGATC - ATC subsystem base class.
-//
-// Written by David Luff, started February 2002.
-//
-// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include "ATC.hxx"
-
-#include <iostream>
-
-#include <simgear/sound/soundmgr_openal.hxx>
-#include <simgear/sound/sample_group.hxx>
-#include <simgear/structure/exception.hxx>
-
-#include <Main/globals.hxx>
-#include <Main/fg_props.hxx>
-#include <ATC/CommStation.hxx>
-#include <Airports/airport.hxx>
-
-FGATC::FGATC() :
- freq(0),
- _currentStation(NULL),
- range(0),
- _voice(true),
- _playing(false),
- _sgr(NULL),
- _type(INVALID),
- _display(false)
-#ifdef OLD_ATC_MGR
- ,freqClear(true),
- receiving(false),
- respond(false),
- responseID(""),
- runResponseCounter(false),
- _runReleaseCounter(false),
- responseReqd(false),
- // Transmission timing stuff
- pending_transmission(""),
- _timeout(0),
- _pending(false),
- _transmit(false),
- _transmitting(false),
- _counter(0.0),
- _max_count(5.0)
-#endif
-{
- SGSoundMgr *smgr = globals->get_soundmgr();
- _sgr = smgr->find("atc", true);
- _sgr->tie_to_listener();
-
- _masterVolume = fgGetNode("/sim/sound/atc/volume", true);
- _enabled = fgGetNode("/sim/sound/atc/enabled", true);
- _atc_external = fgGetNode("/sim/sound/atc/external-view", true);
- _internal = fgGetNode("/sim/current-view/internal", true);
-}
-
-FGATC::~FGATC() {
-}
-
-#ifndef OLD_ATC_MGR
-// Derived classes wishing to use the response counter should
-// call this from their own Update(...).
-void FGATC::update(double dt) {
-
- // TODO This doesn't really do anything specific to this instance.
- // All FGATCs share the same "_sgr" sound group. So this really should
- // only be done once for all FGATCs.
-#ifdef ENABLE_AUDIO_SUPPORT
- bool active = _atc_external->getBoolValue() ||
- _internal->getBoolValue();
-
- if ( active && _enabled->getBoolValue() ) {
- _sgr->set_volume( _masterVolume->getFloatValue() );
- _sgr->resume(); // no-op if already in resumed state
- } else {
- _sgr->suspend();
- }
-#endif
-}
-#endif
-
-void FGATC::SetStation(flightgear::CommStation* sta) {
- if (_currentStation == sta)
- return;
- _currentStation = sta;
-
- if (sta)
- {
- switch (sta->type()) {
- case FGPositioned::FREQ_ATIS: _type = ATIS; break;
- case FGPositioned::FREQ_AWOS: _type = AWOS; break;
- default:
- sta = NULL;
- break;
- }
- }
-
- if (sta == NULL)
- {
- range = 0;
- ident = "";
- name = "";
- freq = 0;
-
- SetNoDisplay();
- update(0); // one last update
- }
- else
- {
- _geod = sta->geod();
- _cart = sta->cart();
-
- range = sta->rangeNm();
- ident = sta->airport()->ident();
- name = sta->airport()->name();
- freq = sta->freqKHz();
- SetDisplay();
- }
-}
-
-// Render a transmission
-// Outputs the transmission either on screen or as audio depending on user preference
-// The refname is a string to identify this sample to the sound manager
-// The repeating flag indicates whether the message should be repeated continuously or played once.
-void FGATC::Render(std::string& msg, const float volume,
- const std::string& refname, const bool repeating) {
- if ((!_display) ||(volume < 0.05))
- {
- NoRender(refname);
- return;
- }
-
- if (repeating)
- fgSetString("/sim/messages/atis", msg.c_str());
- else
- fgSetString("/sim/messages/atc", msg.c_str());
-
-#ifdef ENABLE_AUDIO_SUPPORT
- bool useVoice = _voice && fgGetBool("/sim/sound/voice") && fgGetBool("/sim/sound/atc/enabled");
- SGSoundSample *simple = _sgr->find(refname);
- if(useVoice) {
- if (simple && (_currentMsg == msg))
- {
- simple->set_volume(volume);
- }
- else
- {
- _currentMsg = msg;
- size_t len;
- void* buf = NULL;
- FGATCVoice* vPtr = GetVoicePointer();
- if (vPtr)
- buf = vPtr->WriteMessage((char*)msg.c_str(), &len);
- NoRender(refname);
- if(buf) {
- try {
-// >>> Beware: must pass a (new) object to the (add) method,
-// >>> because the (remove) method is going to do a (delete)
-// >>> whether that's what you want or not.
- simple = new SGSoundSample(&buf, len, 8000);
- simple->set_volume(volume);
- _sgr->add(simple, refname);
- _sgr->play(refname, repeating);
- } catch ( sg_io_exception &e ) {
- SG_LOG(SG_ATC, SG_ALERT, e.getFormattedMessage());
- }
- }
- }
- }
- else
- if (simple)
- {
- NoRender(refname);
- }
-#else
- bool useVoice = false;
-#endif // ENABLE_AUDIO_SUPPORT
-
- if (!useVoice)
- {
- // first rip the underscores and the pause hints out of the string - these are for the convenience of the voice parser
- for(unsigned int i = 0; i < msg.length(); ++i) {
- if((msg.substr(i,1) == "_") || (msg.substr(i,1) == "/")) {
- msg[i] = ' ';
- }
- }
- }
- _playing = true;
-}
-
-
-// Cease rendering a transmission.
-void FGATC::NoRender(const std::string& refname) {
- if(_playing) {
- if(_voice) {
-#ifdef ENABLE_AUDIO_SUPPORT
- _sgr->stop(refname);
- _sgr->remove(refname);
-#endif
- }
- _playing = false;
- }
-}
-
-#ifdef OLD_ATC_MGR
-// Derived classes wishing to use the response counter should
-// call this from their own Update(...).
-void FGATC::Update(double dt) {
-
-#ifdef ENABLE_AUDIO_SUPPORT
- bool active = _atc_external->getBoolValue() ||
- _internal->getBoolValue();
-
- if ( active && _enabled->getBoolValue() ) {
- _sgr->set_volume( _masterVolume->getFloatValue() );
- _sgr->resume(); // no-op if already in resumed state
- } else {
- _sgr->suspend();
- }
-#endif
-
- if(runResponseCounter) {
- //cout << responseCounter << '\t' << responseTime << '\n';
- if(responseCounter >= responseTime) {
- runResponseCounter = false;
- respond = true;
- //cout << "RESPOND\n";
- } else {
- responseCounter += dt;
- }
- }
-
- if(_runReleaseCounter) {
- if(_releaseCounter >= _releaseTime) {
- freqClear = true;
- _runReleaseCounter = false;
- } else {
- _releaseCounter += dt;
- }
- }
-
- // Transmission stuff cribbed from AIPlane.cxx
- if(_pending) {
- if(GetFreqClear()) {
- //cout << "TUNED STATION FREQ CLEAR\n";
- SetFreqInUse();
- _pending = false;
- _transmit = true;
- _transmitting = false;
- } else {
- if(_timeout > 0.0) { // allows count down to be avoided by initially setting it to zero
- _timeout -= dt;
- if(_timeout <= 0.0) {
- _timeout = 0.0;
- _pending = false;
- // timed out - don't render.
- }
- }
- }
- }
-
- if(_transmit) {
- _counter = 0.0;
- _max_count = 5.0; // FIXME - hardwired length of message - need to calculate it!
-
- //cout << "Transmission = " << pending_transmission << '\n';
- if(_display) {
- //Render(pending_transmission, ident, false);
- Render(pending_transmission);
- }
- _transmit = false;
- _transmitting = true;
- } else if(_transmitting) {
- if(_counter >= _max_count) {
- //NoRender(plane.callsign); commented out since at the moment NoRender is designed just to stop repeating messages,
- // and this will be primarily used on single messages.
- _transmitting = false;
- //if(tuned_station) tuned_station->NotifyTransmissionFinished(plane.callsign);
- // TODO - need to let the plane the transmission is aimed at that it's finished.
- // However, for now we'll just release the frequency since if we don't it all goes pear-shaped
- _releaseCounter = 0.0;
- _releaseTime = 0.9;
- _runReleaseCounter = true;
- }
- _counter += dt;
- }
-}
-
-void FGATC::ReceiveUserCallback(int code) {
- SG_LOG(SG_ATC, SG_WARN, "WARNING - whichever ATC class was intended to receive callback code " << code << " didn't get it!!!");
-}
-
-void FGATC::SetResponseReqd(const string& rid) {
- receiving = false;
- responseReqd = true;
- respond = false; // TODO - this ignores the fact that more than one plane could call this before response
- // Shouldn't happen with AI only, but user could confuse things??
- responseID = rid;
- runResponseCounter = true;
- responseCounter = 0.0;
- responseTime = 1.8; // TODO - randomize this slightly.
-}
-
-void FGATC::NotifyTransmissionFinished(const string& rid) {
- //cout << "Transmission finished, callsign = " << rid << '\n';
- receiving = false;
- responseID = rid;
- if(responseReqd) {
- runResponseCounter = true;
- responseCounter = 0.0;
- responseTime = 1.2; // TODO - randomize this slightly, and allow it to be dependent on the transmission and how busy the ATC is.
- respond = false; // TODO - this ignores the fact that more than one plane could call this before response
- // Shouldn't happen with AI only, but user could confuse things??
- } else {
- freqClear = true;
- }
-}
-
-// Generate the text of a message from its parameters and the current context.
-string FGATC::GenText(const string& m, int c) {
- return("");
-}
-
-ostream& operator << (ostream& os, atc_type atc) {
- switch(atc) {
- case(AWOS): return(os << "AWOS");
- case(ATIS): return(os << "ATIS");
- case(GROUND): return(os << "GROUND");
- case(TOWER): return(os << "TOWER");
- case(APPROACH): return(os << "APPROACH");
- case(DEPARTURE): return(os << "DEPARTURE");
- case(ENROUTE): return(os << "ENROUTE");
- case(INVALID): return(os << "INVALID");
- }
- return(os << "ERROR - Unknown switch in atc_type operator << ");
-}
-
-std::istream& operator >> ( std::istream& fin, ATCData& a )
-{
- double f;
- char ch;
- char tp;
-
- fin >> tp;
-
- switch(tp) {
- case 'I':
- a.type = ATIS;
- break;
- case 'T':
- a.type = TOWER;
- break;
- case 'G':
- a.type = GROUND;
- break;
- case 'A':
- a.type = APPROACH;
- break;
- case '[':
- a.type = INVALID;
- return fin >> skipeol;
- default:
- SG_LOG(SG_ATC, SG_ALERT, "Warning - unknown type \'" << tp << "\' found whilst reading ATC frequency data!\n");
- a.type = INVALID;
- return fin >> skipeol;
- }
-
- double lat, lon, elev;
-
- fin >> lat >> lon >> elev >> f >> a.range >> a.ident;
- a.geod = SGGeod::fromDegM(lon, lat, elev);
- a.name = "";
- fin >> ch;
- if(ch != '"') a.name += ch;
- while(1) {
- //in >> noskipws
- fin.unsetf(std::ios::skipws);
- fin >> ch;
- if((ch == '"') || (ch == 0x0A)) {
- break;
- } // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the "
- a.name += ch;
- }
- fin.setf(std::ios::skipws);
- //cout << "Comm name = " << a.name << '\n';
-
- a.freq = (int)(f*100.0 + 0.5);
-
- // cout << a.ident << endl;
-
- // generate cartesian coordinates
- a.cart = SGVec3d::fromGeod(a.geod);
- return fin >> skipeol;
-}
-#endif
+++ /dev/null
-// FGATC - abstract base class for the various actual atc classes
-// such as FGATIS, FGTower etc.
-//
-// Written by David Luff, started Feburary 2002.
-//
-// Copyright (C) 2002 David C. Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifndef _FG_ATC_HXX
-#define _FG_ATC_HXX
-
-#include <simgear/constants.h>
-#include <simgear/compiler.h>
-#include <simgear/props/props.hxx>
-#include <simgear/misc/sgstream.hxx>
-#include <simgear/math/sg_geodesy.hxx>
-#include <simgear/debug/logstream.hxx>
-#include <simgear/structure/SGSharedPtr.hxx>
-
-#include <iosfwd>
-#include <string>
-
-#include "ATCVoice.hxx"
-
-class SGSampleGroup;
-
-namespace flightgear
-{
- class CommStation;
-}
-
-// Possible types of ATC type that the radios may be tuned to.
-// INVALID implies not tuned in to anything.
-enum atc_type {
- AWOS,
- ATIS,
- GROUND,
- TOWER,
- APPROACH,
- DEPARTURE,
- ENROUTE,
- INVALID /* must be last element; see ATC_NUM_TYPES */
-};
-
-#ifdef OLD_ATC_MGR
-const int ATC_NUM_TYPES = 1 + INVALID;
-
-// DCL - new experimental ATC data store
-struct ATCData {
- ATCData() : type(INVALID), cart(0, 0, 0), freq(0), range(0) {}
- atc_type type;
- SGGeod geod;
- SGVec3d cart;
- unsigned short int freq;
- unsigned short int range;
- std::string ident;
- std::string name;
-};
-
-// perhaps we could use an FGRunway instead of this.
-// That wouldn't cache the orthopos though.
-struct RunwayDetails {
- RunwayDetails() : end1ortho(0, 0, 0), end2ortho(0, 0, 0), hdg(0), length(-1), width(-1) {}
- SGGeod threshold_pos;
- SGVec3d end1ortho; // ortho projection end1 (the threshold ATM)
- SGVec3d end2ortho; // ortho projection end2 (the take off end in the current hardwired scheme)
- double hdg; // true runway heading
- double length; // In *METERS*
- double width; // ditto
- std::string rwyID;
- int patternDirection; // -1 for left, 1 for right
-};
-
-std::ostream& operator << (std::ostream& os, atc_type atc);
-#endif
-
-class FGATC {
- friend class FGATISMgr;
-public:
-
- FGATC();
- virtual ~FGATC();
-
- virtual void init()=0;
-
- // Run the internal calculations
- // Derived classes should call this method from their own Update methods if they
- // wish to use the response timer functionality.
- virtual void update(double dt);
-
- // Indicate that this instance should output to the display if appropriate
- inline void SetDisplay() { _display = true; }
-
- // Indicate that this instance should not output to the display
- inline void SetNoDisplay() { _display = false; }
-
-#ifdef OLD_ATC_MGR
- // Receive a coded callback from the ATC menu system based on the user's selection
- virtual void ReceiveUserCallback(int code);
-
- // Generate the text of a message from its parameters and the current context.
- virtual std::string GenText(const std::string& m, int c);
-
- // Returns true if OK to transmit on this frequency
- inline bool GetFreqClear() { return freqClear; }
- // Indicate that the frequency is in use
- inline void SetFreqInUse() { freqClear = false; receiving = true; }
- // Transmission to the ATC is finished and a response is required
- void SetResponseReqd(const std::string& rid);
- // Transmission finished - let ATC decide if a response is reqd and clear freq if necessary
- void NotifyTransmissionFinished(const std::string& rid);
- // Transmission finished and no response required
- inline void ReleaseFreq() { freqClear = true; receiving = false; } // TODO - check that the plane releasing the freq is the right one etc.
- // The above 3 funcs under development!!
- // The idea is that AI traffic or the user ATC dialog box calls FreqInUse() when they begin transmitting,
- // and that the tower control sets freqClear back to true following a reply.
- // AI traffic should check FreqClear() is true prior to transmitting.
- // The user will just have to wait for a gap in dialog as in real life.
-
-
-
- inline int get_freq() const { return freq; }
- inline void set_freq(const int fq) {freq = fq;}
- inline int get_range() const { return range; }
- inline void set_range(const int rg) {range = rg;}
-#endif
- // Return the type of ATC station that the class represents
- inline atc_type GetType() { return _type; }
-
- // Set the core ATC data
- void SetStation(flightgear::CommStation* sta);
-
- inline const std::string& get_ident() { return ident; }
- inline void set_ident(const std::string& id) { ident = id; }
- inline const std::string& get_name() { return name; }
- inline void set_name(const std::string& nm) { name = nm; }
-
-protected:
-
- // Render a transmission
- // Outputs the transmission either on screen or as audio depending on user preference
- // The refname is a string to identify this sample to the sound manager
- // The repeating flag indicates whether the message should be repeated continuously or played once.
- void Render(std::string& msg, const float volume = 1.0,
- const std::string& refname = "", bool repeating = false);
-
- // Cease rendering all transmission from this station.
- // Requires the sound manager refname if audio, else "".
- void NoRender(const std::string& refname);
-
- virtual FGATCVoice* GetVoicePointer() = 0;
-
- SGGeod _geod;
- SGVec3d _cart;
- int freq;
- flightgear::CommStation* _currentStation;
-
- int range;
- std::string ident; // Code of the airport its at.
- std::string name; // Name transmitted in the broadcast.
- std::string _currentMsg; // Current message being transmitted
-
- // Rendering related stuff
- bool _voice; // Flag - true if we are using voice
- bool _playing; // Indicates a message in progress
-
- SGSharedPtr<SGSampleGroup> _sgr; // default sample group;
-
-#ifdef OLD_ATC_MGR
- bool freqClear; // Flag to indicate if the frequency is clear of ongoing dialog
- bool receiving; // Flag to indicate we are receiving a transmission
-
-
- double responseTime; // Time to take from end of request transmission to beginning of response
- // The idea is that this will be slightly random.
-
- bool respond; // Flag to indicate now is the time to respond - ie set following the count down of the response timer.
- std::string responseID; // ID of the plane to respond to
- bool runResponseCounter; // Flag to indicate the response counter should be run
- double responseCounter; // counter to implement the above
- // Derived classes only need monitor this flag, and use the response ID, as long as they call FGATC::Update(...)
- bool _runReleaseCounter; // A timer for releasing the frequency after giving the message enough time to display
- bool responseReqd; // Flag to indicate we should be responding to a request/report
- double _releaseTime;
- double _releaseCounter;
- std::string pending_transmission; // derived classes set this string before calling Transmit(...)
-#endif
- atc_type _type;
- bool _display; // Flag to indicate whether we should be outputting to the ATC display.
-
-private:
-
-#ifdef OLD_ATC_MGR
- // Transmission timing stuff.
- double _timeout;
- bool _pending;
- bool _transmit; // we are to transmit
- bool _transmitting; // we are transmitting
- double _counter;
- double _max_count;
-#endif
-
- SGPropertyNode_ptr _masterVolume;
- SGPropertyNode_ptr _enabled;
- SGPropertyNode_ptr _atc_external;
- SGPropertyNode_ptr _internal;
-};
-
-#ifdef OLD_ATC_MGR
-std::istream& operator>> ( std::istream& fin, ATCData& a );
-#endif
-
-#endif // _FG_ATC_HXX
+++ /dev/null
-// ATCProjection.cxx - A convenience projection class for the ATC/AI system.
-//
-// Written by David Luff, started 2002.
-//
-// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include "ATCProjection.hxx"
-#include <math.h>
-#include <simgear/constants.h>
-
-FGATCAlignedProjection::FGATCAlignedProjection() {
- _origin.setLatitudeRad(0);
- _origin.setLongitudeRad(0);
- _origin.setElevationM(0);
- _correction_factor = cos(_origin.getLatitudeRad());
-}
-
-FGATCAlignedProjection::FGATCAlignedProjection(const SGGeod& centre, double heading) {
- _origin = centre;
- _theta = heading * SG_DEGREES_TO_RADIANS;
- _correction_factor = cos(_origin.getLatitudeRad());
-}
-
-FGATCAlignedProjection::~FGATCAlignedProjection() {
-}
-
-void FGATCAlignedProjection::Init(const SGGeod& centre, double heading) {
- _origin = centre;
- _theta = heading * SG_DEGREES_TO_RADIANS;
- _correction_factor = cos(_origin.getLatitudeRad());
-}
-
-SGVec3d FGATCAlignedProjection::ConvertToLocal(const SGGeod& pt) {
- // convert from lat/lon to orthogonal
- double delta_lat = pt.getLatitudeRad() - _origin.getLatitudeRad();
- double delta_lon = pt.getLongitudeRad() - _origin.getLongitudeRad();
- double y = sin(delta_lat) * SG_EQUATORIAL_RADIUS_M;
- double x = sin(delta_lon) * SG_EQUATORIAL_RADIUS_M * _correction_factor;
-
- // Align
- if(_theta != 0.0) {
- double xbar = x;
- x = x*cos(_theta) - y*sin(_theta);
- y = (xbar*sin(_theta)) + (y*cos(_theta));
- }
-
- return SGVec3d(x, y, pt.getElevationM());
-}
-
-SGGeod FGATCAlignedProjection::ConvertFromLocal(const SGVec3d& pt) {
- // de-align
- double thi = _theta * -1.0;
- double x = pt.x()*cos(thi) - pt.y()*sin(thi);
- double y = (pt.x()*sin(thi)) + (pt.y()*cos(thi));
-
- // convert from orthogonal to lat/lon
- double delta_lat = asin(y / SG_EQUATORIAL_RADIUS_M);
- double delta_lon = asin(x / SG_EQUATORIAL_RADIUS_M) / _correction_factor;
-
- return SGGeod::fromRadM(_origin.getLongitudeRad()+delta_lon, _origin.getLatitudeRad()+delta_lat, pt.z());
-}
+++ /dev/null
-// ATCProjection.hxx - A convenience projection class for the ATC/AI system.
-//
-// Written by David Luff, started 2002.
-//
-// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifndef _FG_ATC_PROJECTION_HXX
-#define _FG_ATC_PROJECTION_HXX
-
-#include <simgear/math/SGMath.hxx>
-
-// FGATCAlignedProjection - a class to project an area local to a runway onto an orthogonal co-ordinate system
-// with the origin at the threshold and the runway aligned with the y axis.
-class FGATCAlignedProjection {
-
-public:
- FGATCAlignedProjection();
- FGATCAlignedProjection(const SGGeod& centre, double heading);
- ~FGATCAlignedProjection();
-
- void Init(const SGGeod& centre, double heading);
-
- // Convert a lat/lon co-ordinate (degrees) to the local projection (meters)
- SGVec3d ConvertToLocal(const SGGeod& pt);
-
- // Convert a local projection co-ordinate (meters) to lat/lon (degrees)
- SGGeod ConvertFromLocal(const SGVec3d& pt);
-
-private:
- SGGeod _origin; // lat/lon of local area origin (the threshold)
- double _theta; // the rotation angle for alignment in radians
- double _correction_factor; // Reduction in surface distance per degree of longitude due to latitude. Saves having to do a cos() every call.
-
-};
-
-#endif // _FG_ATC_PROJECTION_HXX
+++ /dev/null
-// FGATCVoice.cxx - a class to encapsulate an ATC voice
-//
-// Written by David Luff, started November 2002.
-//
-// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include "ATCVoice.hxx"
-
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <fstream>
-#include <vector>
-#include <algorithm>
-
-#include <simgear/sound/soundmgr_openal.hxx>
-#include <simgear/sound/sample_openal.hxx>
-#include <simgear/misc/sg_dir.hxx>
-
-#include <simgear/misc/sg_path.hxx>
-#include <simgear/debug/logstream.hxx>
-#include <simgear/misc/sgstream.hxx>
-#include <simgear/math/sg_random.h>
-
-#include <Main/globals.hxx>
-
-using namespace std;
-
-FGATCVoice::FGATCVoice() :
- rawSoundData(0),
- rawDataSize(0),
- SoundData(0)
-{
-}
-
-FGATCVoice::~FGATCVoice() {
- if (rawSoundData)
- free( rawSoundData );
- delete SoundData;
-}
-
-// Load all data for the requested voice.
-// Return true if successful.
-bool FGATCVoice::LoadVoice(const string& voicename)
-{
- rawDataSize = 0;
- if (rawSoundData)
- free(rawSoundData);
- rawSoundData = NULL;
-
- // determine voice directory
- SGPath voicepath = globals->get_fg_root();
- voicepath.append( "ATC" );
- voicepath.append( "voices" );
- voicepath.append( voicename );
-
- simgear::Dir d(voicepath);
- if (!d.exists())
- {
- SG_LOG(SG_ATC, SG_ALERT, "Unable to load ATIS voice. No such directory: " << voicepath.str());
- return false;
- }
-
- // load all files from the voice's directory
- simgear::PathList paths = d.children(simgear::Dir::TYPE_FILE);
- bool Ok = false;
- for (unsigned int i=0; i<paths.size(); ++i)
- {
- if (paths[i].lower_extension() == "vce")
- Ok |= AppendVoiceFile(voicepath, paths[i].file_base());
- }
-
- if (!Ok)
- {
- SG_LOG(SG_ATC, SG_ALERT, "Unable to load ATIS voice. Files are invalid or no files in directory: " << voicepath.str());
- }
-
- // ok when at least some files loaded fine
- return Ok;
-}
-
-// load a voice file and append it to the current word database
-bool FGATCVoice::AppendVoiceFile(const SGPath& basepath, const string& file)
-{
- size_t offset = 0;
-
- SG_LOG(SG_ATC, SG_INFO, "Loading ATIS voice file: " << file);
-
- // path to compressed voice file
- SGPath path(basepath);
- path.append(file + ".wav.gz");
-
- // load wave data
- SGSoundMgr *smgr = globals->get_soundmgr();
- int format, freq;
- void *data;
- size_t size;
- if (!smgr->load(path.str(), &data, &format, &size, &freq))
- return false;
-
- // append to existing data
- if (!rawSoundData)
- rawSoundData = (char*)data;
- else
- {
- rawSoundData = (char*) realloc(rawSoundData, rawDataSize + size);
- // new data starts behind existing sound data
- offset = rawDataSize;
- if (!rawSoundData)
- {
- SG_LOG(SG_ATC, SG_ALERT, "Out of memory. Cannot load file " << path.str());
- rawDataSize = 0;
- return false;
- }
- // append to existing sound data
- memcpy(rawSoundData+offset, data, size);
- free(data);
- data = NULL;
- }
- rawDataSize += size;
-
-#ifdef VOICE_TEST
- cout << "ATCVoice: format: " << format
- << " size: " << rawDataSize << endl;
-#endif
-
- // load and parse index file (.vce)
- return ParseVoiceIndex(basepath, file, offset);
-}
-
-// Load and parse a voice index file (.vce)
-bool FGATCVoice::ParseVoiceIndex(const SGPath& basepath, const string& file, size_t globaloffset)
-{
- // path to voice index file
- SGPath path(basepath);
- path.append(file + ".vce");
-
- // Now load the word data
- std::ifstream fin;
- fin.open(path.c_str(), ios::in);
- if(!fin) {
- SG_LOG(SG_ATC, SG_ALERT, "Unable to open input file " << path.c_str());
- return(false);
- }
- SG_LOG(SG_ATC, SG_INFO, "Opened word data file " << path.c_str() << " OK...");
-
- char numwds[10];
- char wrd[100];
- string wrdstr;
- char wrdOffsetStr[20];
- char wrdLengthStr[20];
- unsigned int wrdOffset; // Offset into the raw sound data that the word sample begins
- unsigned int wrdLength; // Length of the word sample in bytes
- WordData wd;
-
- // first entry: number of words in the index
- fin >> numwds;
- unsigned int numwords = atoi(numwds);
- //cout << numwords << '\n';
-
- // now load each word, its file offset and length
- for(unsigned int i=0; i < numwords; ++i) {
- // read data
- fin >> wrd;
- fin >> wrdOffsetStr;
- fin >> wrdLengthStr;
-
- wrdstr = wrd;
- wrdOffset = atoi(wrdOffsetStr);
- wrdLength = atoi(wrdLengthStr);
-
- // store word in map
- wd.offset = wrdOffset + globaloffset;
- wd.length = wrdLength;
- wordMap[wrdstr] = wd;
-
- // post-process words
- string ws2 = wrdstr;
- for(string::iterator p = ws2.begin(); p != ws2.end(); p++){
- *p = tolower(*p);
- if (*p == '-')
- *p = '_';
- }
-
- // store alternative version of word (lowercase/no hyphen)
- if (wrdstr != ws2)
- wordMap[ws2] = wd;
-
- //cout << wrd << "\t\t" << wrdOffset << "\t\t" << wrdLength << '\n';
- //cout << i << '\n';
- }
-
- fin.close();
- return(true);
-}
-
-
-// Given a desired message, return a string containing the
-// sound-sample data
-void* FGATCVoice::WriteMessage(const string& message, size_t* len) {
-
- // What should we do here?
- // First - parse the message into a list of tokens.
- // Sort the tokens into those we understand and those we don't.
- // Add all the raw lengths of the token sound data, allocate enough space, and fill it with the rqd data.
-
- vector<char> sound;
- const char delimiters[] = " \t.,;:\"\n";
- string::size_type token_start = message.find_first_not_of(delimiters);
- while(token_start != string::npos) {
- string::size_type token_end = message.find_first_of(delimiters, token_start);
- string token;
- if (token_end == string::npos) {
- token = message.substr(token_start);
- token_start = string::npos;
- } else {
- token = message.substr(token_start, token_end - token_start);
- token_start = message.find_first_not_of(delimiters, token_end);
- }
-
- if (token == "/_") continue;
-
- for(string::iterator t = token.begin(); t != token.end(); t++) {
- // canonicalize the token, to match what's in the index
- *t = (*t == '-') ? '_' : tolower(*t);
- }
- SG_LOG(SG_ATC, SG_DEBUG, "voice synth: token: '"
- << token << "'");
-
- atc_word_map_const_iterator wordIt = wordMap.find(token);
- if(wordIt == wordMap.end()) {
- // Oh dear - the token isn't in the sound file
- SG_LOG(SG_ATC, SG_ALERT, "voice synth: word '"
- << token << "' not found");
- } else {
- const WordData& word = wordIt->second;
- /*
- * Sanity check for corrupt/mismatched sound data input - avoids a seg fault
- * (As long as the calling function checks the return value!!)
- * This check should be left in even when the default Flightgear files are known
- * to be OK since it checks for mis-indexing of voice files by 3rd party developers.
- */
- if((word.offset + word.length) > rawDataSize) {
- SG_LOG(SG_ATC, SG_ALERT, "ERROR - mismatch between ATC .wav and .vce file in ATCVoice.cxx\n");
- SG_LOG(SG_ATC, SG_ALERT, "Offset + length: " << word.offset + word.length
- << " exceeds rawdata size: " << rawDataSize << endl);
-
- *len = 0;
- return 0;
- }
- sound.insert(sound.end(), rawSoundData + word.offset, rawSoundData + word.offset + word.length);
- }
- }
-
- // Check for no tokens found else slScheduler can be crashed
- *len = sound.size();
- if (*len == 0) {
- return 0;
- }
-
- char* data = (char*)malloc(*len);
- if (data == 0) {
- SG_LOG(SG_ATC, SG_ALERT, "ERROR - could not allocate " << *len << " bytes of memory for ATIS sound\n");
- *len = 0;
- return 0;
- }
-
- // randomize start position
- unsigned int offsetIn = (unsigned int)(*len * sg_random());
- if (offsetIn > 0 && offsetIn < *len) {
- copy(sound.begin() + offsetIn, sound.end(), data);
- copy(sound.begin(), sound.begin() + offsetIn, data + *len - offsetIn);
- } else {
- copy(sound.begin(), sound.end(), data);
- }
-
- return data;
-}
+++ /dev/null
-// FGATCVoice.hxx - a class to encapsulate an ATC voice
-//
-// Written by David Luff, started November 2002.
-//
-// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifndef _FG_ATC_VOICE
-#define _FG_ATC_VOICE
-
-#include <simgear/compiler.h>
-#include <simgear/structure/SGSharedPtr.hxx>
-
-#include <map>
-#include <string>
-
-class SGSoundSample;
-class SGPath;
-
-struct WordData {
- unsigned int offset; // Offset of beginning of word sample into raw sound sample
- unsigned int length; // Byte length of word sample
-};
-
-typedef std::map < std::string, WordData > atc_word_map_type;
-typedef atc_word_map_type::iterator atc_word_map_iterator;
-typedef atc_word_map_type::const_iterator atc_word_map_const_iterator;
-
-class FGATCVoice {
-
-public:
-
- FGATCVoice();
- ~FGATCVoice();
-
- // Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce).
- // Return true if successful.
- bool LoadVoice(const std::string& voicename);
-
- // Given a desired message, return a pointer to the data buffer and write the buffer length into len.
- // Sets len to something other than 0 if the returned buffer is valid.
- void* WriteMessage(const std::string& message, size_t *len);
-
-private:
- bool AppendVoiceFile(const SGPath& basepath, const std::string& file);
- bool ParseVoiceIndex(const SGPath& basepath, const std::string& file, size_t globaloffset);
-
- // the sound and word position data
- char* rawSoundData;
- size_t rawDataSize;
- SGSharedPtr<SGSoundSample> SoundData;
-
- // A map of words vs. byte position and length in rawSoundData
- atc_word_map_type wordMap;
-
-};
-
-#endif // _FG_ATC_VOICE
+++ /dev/null
-// ATCutils.cxx - Utility functions for the ATC / AI system
-//
-// Written by David Luff, started March 2002.
-//
-// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <sstream>
-#include <cstdlib>
-
-#include <simgear/constants.h>
-#include <simgear/misc/sg_path.hxx>
-#include <simgear/debug/logstream.hxx>
-
-#include <Airports/runways.hxx>
-#include <Main/globals.hxx>
-
-#include "ATCutils.hxx"
-#include "ATCProjection.hxx"
-
-static const string nums[10] = {"zero", "one", "two", "three", "four",
- "five", "six", "seven", "eight", "niner"};
-
-static const string letters[LTRS] = {
- "alpha", "bravo", "charlie", "delta", "echo",
- "foxtrot", "golf", "hotel", "india", "juliet",
- "kilo", "lima", "mike", "november", "oscar",
- "papa", "quebec", "romeo", "sierra", "tango",
- "uniform", "victor", "whiskey", "xray", "yankee", "zulu"
-};
-
-// Convert any number to spoken digits
-string ConvertNumToSpokenDigits(const string &n) {
- //cout << "n = " << n << endl;
- static const string pt = "decimal";
- string str = "";
-
- for(unsigned int i=0; i<n.length(); ++i) {
- //cout << "n.substr(" << i << ",1 = " << n.substr(i,1) << endl;
- if(n.substr(i,1) == " ") {
- // do nothing
- } else if(n.substr(i,1) == ".") {
- str += pt;
- } else {
- str += nums[atoi((n.substr(i,1)).c_str())];
- }
- if(i != (n.length()-1)) { // ie. don't add a space at the end.
- str += " ";
- }
- }
- return(str);
-}
-
-// Convert an integer to a decimal numeral string
-string decimalNumeral(const int& n) {
- std::ostringstream buf;
- buf << n;
- return buf.str();
-}
-
-// Convert an integer to spoken digits
-string ConvertNumToSpokenDigits(const int& n) {
- return ConvertNumToSpokenDigits(decimalNumeral(n));
-}
-
-
-// Assumes we get a string of digits optionally appended with L, R or C
-// eg 1 7L 29R 36
-// Anything else is not guaranteed to be handled correctly!
-string ConvertRwyNumToSpokenString(const string &rwy) {
- string rslt;
- for (size_t ii = 0; ii < rwy.length(); ii++){
- if (rslt.length()) rslt += " ";
- string ch = rwy.substr(ii,1);
- if (isdigit(ch[0])) rslt += ConvertNumToSpokenDigits(atoi(ch.c_str()));
- else if (ch == "R") rslt += "right";
- else if (ch == "C") rslt += "center";
- else if (ch == "L") rslt += "left";
- else {
- rslt += GetPhoneticLetter(ch[0]);
- SG_LOG(SG_ATC, SG_WARN, "WARNING: Unknown suffix '" << ch
- << "' in runway " << rwy << " in ConvertRwyNumToSpokenString(...)");
- }
- }
- return rslt;
-}
-
-
-// Return the phonetic letter of a letter represented as an integer 1->26
-string GetPhoneticLetter(const int i) {
- return(letters[i % LTRS]);
-}
-
-// Return the phonetic letter of a character in the range a-z or A-Z.
-// Currently always returns prefixed by lowercase.
-string GetPhoneticLetter(const char c) {
- return GetPhoneticLetter(int(tolower(c) - 'a'));
-}
-
-// Get the compass direction associated with a heading in degrees
-// Currently returns 8 direction resolution (N, NE, E etc...)
-// Might be modified in future to return 4, 8 or 16 resolution but defaulting to 8.
-string GetCompassDirection(double h) {
- while(h < 0.0) h += 360.0;
- while(h > 360.0) h -= 360.0;
- if(h < 22.5 || h > 337.5) {
- return("North");
- } else if(h < 67.5) {
- return("North-East");
- } else if(h < 112.5) {
- return("East");
- } else if(h < 157.5) {
- return("South-East");
- } else if(h < 202.5) {
- return("South");
- } else if(h < 247.5) {
- return("South-West");
- } else if(h < 292.5) {
- return("West");
- } else {
- return("North-West");
- }
-}
-
-//================================================================================================================
-
-// Given two positions (lat & lon in degrees), get the HORIZONTAL separation (in meters)
-double dclGetHorizontalSeparation(const SGGeod& pos1, const SGGeod& pos2) {
- double x; //East-West separation
- double y; //North-South separation
- double z; //Horizontal separation - z = sqrt(x^2 + y^2)
-
- double lat1 = pos1.getLatitudeRad();
- double lon1 = pos1.getLongitudeRad();
- double lat2 = pos2.getLatitudeRad();
- double lon2 = pos2.getLongitudeRad();
-
- y = sin(fabs(lat1 - lat2)) * SG_EQUATORIAL_RADIUS_M;
- x = sin(fabs(lon1 - lon2)) * SG_EQUATORIAL_RADIUS_M * (cos((lat1 + lat2) / 2.0));
- z = sqrt(x*x + y*y);
-
- return(z);
-}
-
-// Given a point and a line, get the HORIZONTAL shortest distance from the point to a point on the line.
-// Expects to be fed orthogonal co-ordinates, NOT lat & lon !
-// The units of the separation will be those of the input.
-double dclGetLinePointSeparation(double px, double py, double x1, double y1, double x2, double y2) {
- double vecx = x2-x1;
- double vecy = y2-y1;
- double magline = sqrt(vecx*vecx + vecy*vecy);
- double u = ((px-x1)*(x2-x1) + (py-y1)*(y2-y1)) / (magline * magline);
- double x0 = x1 + u*(x2-x1);
- double y0 = y1 + u*(y2-y1);
- vecx = px - x0;
- vecy = py - y0;
- double d = sqrt(vecx*vecx + vecy*vecy);
- if(d < 0) {
- d *= -1;
- }
- return(d);
-}
-
-// Given a position (lat/lon/elev), heading and vertical angle (degrees), and distance (meters), calculate the new position.
-// This function assumes the world is spherical. If geodetic accuracy is required use the functions is sg_geodesy instead!
-// Assumes that the ground is not hit!!! Expects heading and angle in degrees, distance in meters.
-SGGeod dclUpdatePosition(const SGGeod& pos, double heading, double angle, double distance) {
- // FIXME: use SGGeodesy instead ...
-
- //cout << setprecision(10) << pos.lon() << ' ' << pos.lat() << '\n';
- heading *= DCL_DEGREES_TO_RADIANS;
- angle *= DCL_DEGREES_TO_RADIANS;
- double lat = pos.getLatitudeRad();
- double lon = pos.getLongitudeRad();
- double elev = pos.getElevationM();
- //cout << setprecision(10) << lon*DCL_RADIANS_TO_DEGREES << ' ' << lat*DCL_RADIANS_TO_DEGREES << '\n';
-
- double horiz_dist = distance * cos(angle);
- double vert_dist = distance * sin(angle);
-
- double north_dist = horiz_dist * cos(heading);
- double east_dist = horiz_dist * sin(heading);
-
- //cout << distance << ' ' << horiz_dist << ' ' << vert_dist << ' ' << north_dist << ' ' << east_dist << '\n';
-
- double delta_lat = asin(north_dist / (double)SG_EQUATORIAL_RADIUS_M);
- double delta_lon = asin(east_dist / (double)SG_EQUATORIAL_RADIUS_M) * (1.0 / cos(lat)); // I suppose really we should use the average of the original and new lat but we'll assume that this will be good enough.
- //cout << delta_lon*DCL_RADIANS_TO_DEGREES << ' ' << delta_lat*DCL_RADIANS_TO_DEGREES << '\n';
- lat += delta_lat;
- lon += delta_lon;
- elev += vert_dist;
- //cout << setprecision(10) << lon*DCL_RADIANS_TO_DEGREES << ' ' << lat*DCL_RADIANS_TO_DEGREES << '\n';
-
- //cout << setprecision(15) << DCL_DEGREES_TO_RADIANS * DCL_RADIANS_TO_DEGREES << '\n';
-
- return SGGeod::fromRadM(lon, lat, elev);
-}
-
-// Get a heading in degrees from one lat/lon to another.
-// This function assumes the world is spherical. If geodetic accuracy is required use the functions is sg_geodesy instead!
-// Warning - at the moment we are not checking for identical points - currently it returns 0 in this instance.
-double GetHeadingFromTo(const SGGeod& A, const SGGeod& B) {
- double latA = A.getLatitudeRad();
- double lonA = A.getLongitudeRad();
- double latB = B.getLatitudeRad();
- double lonB = B.getLongitudeRad();
- double xdist = sin(lonB - lonA) * (double)SG_EQUATORIAL_RADIUS_M * cos((latA+latB)/2.0);
- double ydist = sin(latB - latA) * (double)SG_EQUATORIAL_RADIUS_M;
- double heading = atan2(xdist, ydist) * DCL_RADIANS_TO_DEGREES;
- return heading < 0.0 ? heading + 360 : heading;
-}
-
-// Given a heading (in degrees), bound it from 0 -> 360
-void dclBoundHeading(double &hdg) {
- while(hdg < 0.0) {
- hdg += 360.0;
- }
- while(hdg > 360.0) {
- hdg -= 360.0;
- }
-}
-
-// smallest difference between two angles in degrees
-// difference is negative if a1 > a2 and positive if a2 > a1
-double GetAngleDiff_deg( const double &a1, const double &a2) {
-
- double a3 = a2 - a1;
- while (a3 < 180.0) a3 += 360.0;
- while (a3 > 180.0) a3 -= 360.0;
-
- return a3;
-}
-
-// Runway stuff
-// Given (lon/lat/elev) and an FGRunway struct, determine if the point lies on the runway
-bool OnRunway(const SGGeod& pt, const FGRunwayBase* rwy) {
- FGATCAlignedProjection ortho;
- SGGeod centre = SGGeod::fromDegM(rwy->longitude(), rwy->latitude(), 0); // We don't need the elev
- ortho.Init(centre, rwy->headingDeg());
-
- SGVec3d xyc = ortho.ConvertToLocal(centre);
- SGVec3d xyp = ortho.ConvertToLocal(pt);
-
- //cout << "Length offset = " << fabs(xyp.y() - xyc.y()) << '\n';
- //cout << "Width offset = " << fabs(xyp.x() - xyc.x()) << '\n';
-
- if((fabs(xyp.y() - xyc.y()) < ((rwy->lengthFt()/2.0) + 5.0))
- && (fabs(xyp.x() - xyc.x()) < (rwy->widthFt()/2.0))) {
- return(true);
- }
-
- return(false);
-}
-
+++ /dev/null
-// ATCutils.hxx - Utility functions for the ATC / AI subsytem
-//
-// Written by David Luff, started March 2002.
-//
-// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#include <Airports/airport.hxx>
-#include <Airports/runways.hxx>
-
-#include <math.h>
-#include <string>
-using std::string;
-
-// These are defined here because I had a problem with SG_DEGREES_TO_RADIANS
-#define DCL_PI 3.1415926535f
-#define DCL_DEGREES_TO_RADIANS (DCL_PI/180.0)
-#define DCL_RADIANS_TO_DEGREES (180.0/DCL_PI)
-
-/*******************************
-*
-* Communication functions
-*
-********************************/
-
-// Convert any number to spoken digits
-string ConvertNumToSpokenDigits(const string &n);
-
-// Convert an integer to spoken digits
-string ConvertNumToSpokenDigits(const int& n);
-string decimalNumeral(const int& n);
-
-// Convert rwy number string to a spoken-style string
-// eg "15L" to "one five left"
-// Assumes we get a string of digits optionally appended with R, L, or C
-// eg 1 7L 29R 36
-string ConvertRwyNumToSpokenString(const string &s);
-
-const int LTRS(26);
-// Return the phonetic letter of a letter represented as an integer 0..25
-string GetPhoneticLetter(const int i);
-
-// Return the phonetic letter of a character in the range a-z or A-Z.
-// Currently always returns prefixed by lowercase.
-string GetPhoneticLetter(char c);
-
-// Get the compass direction associated with a heading in degrees
-// Currently returns 8 direction resolution (N, NE, E etc...)
-// Might be modified in future to return 4, 8 or 16 resolution but defaulting to 8.
-string GetCompassDirection(double h);
-
-/*******************************
-*
-* Positional functions
-*
-********************************/
-
-// Given two positions (lat & lon in degrees), get the HORIZONTAL separation (in meters)
-double dclGetHorizontalSeparation(const SGGeod& pos1, const SGGeod& pos2);
-
-// Given a point and a line, get the HORIZONTAL shortest distance from the point to a point on the line.
-// Expects to be fed orthogonal co-ordinates, NOT lat & lon !
-double dclGetLinePointSeparation(double px, double py, double x1, double y1, double x2, double y2);
-
-// Given a position (lat/lon/elev), heading, vertical angle, and distance, calculate the new position.
-// Assumes that the ground is not hit!!! Expects heading and angle in degrees, distance in meters.
-SGGeod dclUpdatePosition(const SGGeod& pos, double heading, double angle, double distance);
-
-// Get a heading from one lat/lon to another (in degrees)
-double GetHeadingFromTo(const SGGeod& A, const SGGeod& B);
-
-// Given a heading (in degrees), bound it from 0 -> 360
-void dclBoundHeading(double &hdg);
-
-// smallest difference between two angles in degrees
-// difference is negative if a1 > a2 and positive if a2 > a1
-double GetAngleDiff_deg( const double &a1, const double &a2);
-
-/****************
-*
-* Runways
-*
-****************/
-
-// Given (lon/lat/elev) and an FGRunway struct, determine if the point lies on the runway
-bool OnRunway(const SGGeod& pt, const FGRunwayBase* rwy);
-
+++ /dev/null
-// ATISmgr.cxx - Implementation of FGATISMgr - a global Flightgear ATIS manager.
-//
-// Written by David Luff, started February 2002.
-//
-// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk
-// Copyright (C) 2012 Thorsten Brehm
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <simgear/misc/sg_path.hxx>
-#include <simgear/debug/logstream.hxx>
-#include <simgear/structure/exception.hxx>
-
-#include <Main/fg_props.hxx>
-
-#include "ATISmgr.hxx"
-#include "atis.hxx"
-
-FGATISMgr::FGATISMgr() :
- _currentUnit(0),
- _maxCommRadios(4)
-#ifdef ENABLE_AUDIO_SUPPORT
- ,useVoice(true),
- voice(0)
-#endif
-{
- globals->set_ATIS_mgr(this);
-}
-
-FGATISMgr::~FGATISMgr()
-{
- globals->set_ATIS_mgr(NULL);
-
- for (unsigned int unit = 0;unit < radios.size(); ++unit) {
- delete radios[unit];
- }
-
-#ifdef ENABLE_AUDIO_SUPPORT
- delete voice;
-#endif
-}
-
-void FGATISMgr::init()
-{
- for (unsigned int unit = 0;unit < _maxCommRadios; ++unit)
- {
- if (unit < _maxCommRadios/2)
- radios.push_back(new FGATIS("comm", unit));
- else
- radios.push_back(new FGATIS("nav", unit - _maxCommRadios/2));
- }
-}
-
-void FGATISMgr::reinit()
-{
-#ifdef ENABLE_AUDIO_SUPPORT
- if ((voiceName != "")&&
- (voiceName != fgGetString("/sim/atis/voice", "default")))
- {
- voiceName = fgGetString("/sim/atis/voice", "default");
- delete voice;
- voice = NULL;
- useVoice = true;
- }
-#endif
-}
-
-void FGATISMgr::update(double dt)
-{
- // update only runs every now and then (1-2 per second)
- if (++_currentUnit >= _maxCommRadios)
- _currentUnit = 0;
-
- FGATC* commRadio = radios[_currentUnit];
- if (commRadio)
- commRadio->update(dt * _maxCommRadios);
-}
-
-// Return a pointer to an appropriate voice for a given type of ATC
-// creating the voice if necessary - i.e. make sure exactly one copy
-// of every voice in use exists in memory.
-//
-// TODO - in the future this will get more complex and dole out country/airport
-// specific voices, and possible make sure that the same voice doesn't get used
-// at different airports in quick succession if a large enough selection are available.
-FGATCVoice* FGATISMgr::GetVoicePointer(const atc_type& type)
-{
-#ifdef ENABLE_AUDIO_SUPPORT
- // TODO - implement me better - maintain a list of loaded voices and other voices!!
- if(useVoice)
- {
- switch(type)
- {
- case ATIS: case AWOS:
- // Delayed loading for all available voices, needed because the
- // sound manager might not be initialized (at all) at this point.
- // For now we'll do one hard-wired one
-
- /* I've loaded the voice even if /sim/sound/pause is true
- * since I know no way of forcing load of the voice if the user
- * subsequently switches /sim/sound/audible to true.
- * (which is the right thing to do -- CLO) :-)
- */
- if (!voice && fgGetBool("/sim/sound/working")) {
- voice = new FGATCVoice;
- voiceName = fgGetString("/sim/atis/voice", "default");
- try {
- useVoice = voice->LoadVoice(voiceName);
- } catch ( sg_io_exception & e) {
- SG_LOG(SG_ATC, SG_ALERT, "Unable to load voice '" << voiceName << "': "
- << e.getFormattedMessage().c_str());
- useVoice = false;
- delete voice;
- voice = 0;
- }
- }
- return voice;
- case TOWER:
- return NULL;
- case APPROACH:
- return NULL;
- case GROUND:
- return NULL;
- default:
- return NULL;
- }
- }
-#endif
-
- return NULL;
-}
+++ /dev/null
-// ATISmgr.hxx - definition of FGATISMgr
-// - a global management class for FlightGear generated ATIS
-//
-// Written by David Luff, started February 2002.
-//
-// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifndef _FG_ATISMGR_HXX
-#define _FG_ATISMGR_HXX
-
-#include <vector>
-
-#include <simgear/structure/subsystem_mgr.hxx>
-
-#include "ATC.hxx"
-
-class FGATISMgr : public SGSubsystem
-{
-private:
- // A vector containing all comm radios
- std::vector<FGATC*> radios;
-
- unsigned int _currentUnit;
- unsigned int _maxCommRadios;
-
-#ifdef ENABLE_AUDIO_SUPPORT
- bool useVoice; // Flag - true if we are using voice
- FGATCVoice* voice;
- std::string voiceName; // currently loaded voice name
-#endif
-
-public:
- FGATISMgr();
- ~FGATISMgr();
-
- void init();
- void reinit();
- void update(double dt);
-
- // Return a pointer to an appropriate voice for a given type of ATC
- // creating the voice if necessary - i.e. make sure exactly one copy
- // of every voice in use exists in memory.
- //
- // TODO - in the future this will get more complex and dole out country/airport
- // specific voices, and possible make sure that the same voice doesn't get used
- // at different airports in quick succession if a large enough selection are available.
- FGATCVoice* GetVoicePointer(const atc_type& type);
-
-private:
-};
-
-#endif // _FG_ATISMGR_HXX
+++ /dev/null
-include(FlightGearComponent)
-
-set(SOURCES
- ATCProjection.cxx
- )
-
-set(HEADERS
- ATCProjection.hxx
- )
-
-flightgear_component(ATCDCL "${SOURCES}" "${HEADERS}")
+++ /dev/null
-// atis.cxx - routines to generate the ATIS info string
-// This is the implementation of the FGATIS class
-//
-// Written by David Luff, started October 2001.
-// Extended by Thorsten Brehm, October 2012.
-//
-// Copyright (C) 2001 David C Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-/////
-///// TODO: _Cumulative_ sky coverage.
-///// TODO: wind _gust_
-///// TODO: more-sensible encoding of voice samples
-///// u-law? outright synthesis?
-/////
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include "atis.hxx"
-#include "atis_lexicon.hxx"
-
-#include <simgear/compiler.h>
-#include <simgear/math/sg_random.h>
-#include <simgear/misc/sg_path.hxx>
-#include <simgear/misc/strutils.hxx>
-
-#include <stdlib.h> // atoi()
-#include <stdio.h> // sprintf
-#include <string>
-#include <iostream>
-
-#include <boost/tuple/tuple.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/algorithm/string/case_conv.hpp>
-
-#include <Environment/environment_mgr.hxx>
-#include <Environment/environment.hxx>
-#include <Environment/atmosphere.hxx>
-
-#include <Main/fg_props.hxx>
-#include <Main/globals.hxx>
-#include <Airports/runways.hxx>
-#include <Airports/dynamics.hxx>
-
-#include <ATC/CommStation.hxx>
-#include <Navaids/navrecord.hxx>
-
-#include "ATCutils.hxx"
-#include "ATISmgr.hxx"
-
-using std::string;
-using std::map;
-using std::cout;
-using std::cout;
-using boost::ref;
-using boost::tie;
-using flightgear::CommStation;
-
-FGATIS::FGATIS(const std::string& name, int num) :
- _name(name),
- _num(num),
- _cb_attention(this, &FGATIS::attend, fgGetNode("/environment/attention", true)),
- transmission(""),
- trans_ident(""),
- old_volume(0),
- atis_failed(false),
- msg_time(0),
- cur_time(0),
- msg_OK(0),
- _attention(false),
- _check_transmission(true),
- _prev_display(0),
- _time_before_search_sec(0),
- _last_frequency(0)
-{
- _root = fgGetNode("/instrumentation", true)->getNode(_name, num, true);
- _volume = _root->getNode("volume",true);
- _serviceable = _root->getNode("serviceable",true);
-
- if (name != "nav")
- {
- // only drive "operable" for non-nav instruments (nav radio drives this separately)
- _operable = _root->getNode("operable",true);
- _operable->setBoolValue(false);
- }
-
- _electrical = fgGetNode("/systems/electrical/outputs",true)->getNode(_name,num, true);
- _atis = _root->getNode("atis",true);
- _freq = _root->getNode("frequencies/selected-mhz",true);
-
- // current position
- _lon_node = fgGetNode("/position/longitude-deg", true);
- _lat_node = fgGetNode("/position/latitude-deg", true);
- _elev_node = fgGetNode("/position/altitude-ft", true);
-
- // backward compatibility: some properties may not exist (but default to "ON")
- if (!_serviceable->hasValue())
- _serviceable->setBoolValue(true);
- if (!_electrical->hasValue())
- _electrical->setDoubleValue(24.0);
-
-///////////////
-// FIXME: This would be more flexible and more extensible
-// if the mappings were taken from an XML file, not hard-coded ...
-// ... although having it in a .hxx file is better than nothing.
-//
-// Load the remap list from the .hxx file:
- using namespace lex;
-
- # define NIL ""
- # define REMAP(from,to) _remap[#from] = to;
- # include "atis_remap.hxx"
- # undef REMAP
- # undef NIL
-
- #ifdef ATIS_TEST
- SG_LOG(SG_ATC, SG_ALERT, "ATIS initialized");
- #endif
-
- _report.psl = 0;
-}
-
-// Hint:
-// http://localhost:5400/environment/attention?value=1&submit=update
-
-FGATCVoice* FGATIS::GetVoicePointer()
-{
- FGATISMgr* pAtisMgr = globals->get_ATIS_mgr();
- if (!pAtisMgr)
- {
- SG_LOG(SG_ATC, SG_ALERT, "ERROR! No ATIS manager! Oops...");
- return NULL;
- }
-
- return pAtisMgr->GetVoicePointer(ATIS);
-}
-
-void FGATIS::init()
-{
-// Nothing to see here. Move along.
-}
-
-void FGATIS::reinit()
-{
- _time_before_search_sec = 0;
- _check_transmission = true;
-}
-
-void
-FGATIS::attend(SGPropertyNode* node)
-{
- if (node->getBoolValue())
- _attention = true;
-#ifdef ATMO_TEST
- int flag = fgGetInt("/sim/logging/atmo");
- if (flag) {
- FGAltimeter().check_model();
- FGAltimeter().dump_stack();
- }
-#endif
-}
-
-
-// Main update function - checks whether we are displaying or not the correct message.
-void FGATIS::update(double dt) {
- cur_time = globals->get_time_params()->get_cur_time();
- msg_OK = (msg_time < cur_time);
-
-#ifdef ATIS_TEST
- if (msg_OK || _display != _prev_display) {
- cout << "ATIS Update: " << _display << " " << _prev_display
- << " len: " << transmission.length()
- << " oldvol: " << old_volume
- << " dt: " << dt << endl;
- msg_time = cur_time;
- }
-#endif
-
- double volume = 0;
- if ((_electrical->getDoubleValue() > 8) && _serviceable->getBoolValue())
- {
- // radio is switched on and OK
- if (_operable.valid())
- _operable->setBoolValue(true);
-
- _check_transmission |= search(dt);
-
- if (_display)
- {
- volume = _volume->getDoubleValue();
- }
- }
- else
- {
- // radio is OFF
- if (_operable.valid())
- _operable->setBoolValue(false);
- _time_before_search_sec = 0;
- }
-
- if (volume > 0.05)
- {
- bool changed = false;
- if (_check_transmission)
- {
- _check_transmission = false;
- // Check if we need to update the message
- // - basically every hour and if the weather changes significantly at the station
- // If !_prev_display, the radio had been detuned for a while and our
- // "transmission" variable was lost when we were de-instantiated.
- if (genTransmission(!_prev_display, _attention))
- {
- // update output property
- treeOut(msg_OK);
- changed = true;
- }
- }
-
- if (changed || volume != old_volume) {
- // audio output enabled
- Render(transmission, volume, _name, true);
- old_volume = volume;
- }
- _prev_display = _display;
- } else {
- // silence
- NoRender(_name);
- _prev_display = false;
- }
- _attention = false;
-
- FGATC::update(dt);
-}
-
-// Replace all occurrences of a given word.
-// Words in the original string must be separated by hyphens (not spaces).
-// We check for the word as given, and for the all-caps version thereof.
-string replace_word(const string _orig, const string _www, const string _nnn){
-// The following are so we can match words at the beginning
-// and end of the string.
- string orig = "-" + _orig + "-";
- string www = "-" + _www + "-";
- string nnn = "-" + _nnn + "-";
-
- size_t where(0);
- for ( ; (where = orig.find(www, where)) != string::npos ; ) {
- orig.replace(where, www.length(), nnn);
- where += nnn.length();
- }
-
- www = simgear::strutils::uppercase(www);
- for ( ; (where = orig.find(www, where)) != string::npos ; ) {
- orig.replace(where, www.length(), nnn);
- where += nnn.length();
- }
- where = orig.length();
- return orig.substr(1, where-2);
-}
-
-// Normally the interval is 1 hour,
-// but you can shorten it for testing.
-const int minute(60); // measured in seconds
-#ifdef ATIS_TEST
- const int ATIS_interval(2*minute);
-#else
- const int ATIS_interval(60*minute);
-#endif
-
-// FIXME: This is heuristic. It gets the right answer for
-// more than 90% of the world's airports, which is a lot
-// better than nothing ... but it's not 100%.
-// We know "most" of the world uses millibars,
-// but the US, Canada and *some* other places use inches of mercury,
-// but (a) we have not implemented a reliable method of
-// ascertaining which airports are in the US, let alone
-// (b) ascertaining which other places use inches.
-//
-bool Apt_US_CA(const string id)
-{
- // Assume all IDs have length 3 or 4.
- // No counterexamples have been seen.
- if (id.length() == 4) {
- if (id.substr(0,1) == "K") return true;
- if (id.substr(0,2) == "CY") return true;
- }
- for (string::const_iterator ptr = id.begin(); ptr != id.end(); ptr++) {
- if (isdigit(*ptr)) return true;
- }
- return false;
-}
-
-// voice spacers
-static const string BRK = ".\n";
-static const string PAUSE = " / ";
-
-/** Generate the actual broadcast ATIS transmission.
-* 'regen' triggers a regeneration of the /current/ transmission.
-* 'forceUpdate' generates a new transmission, with a new sequence.
-* Returns 1 if we actually generated something.
-*/
-bool FGATIS::genTransmission(const int regen, bool forceUpdate)
-{
- using namespace lex;
-
- // ATIS updated hourly, AWOS updated more frequently
- int interval = _type == ATIS ? ATIS_interval : 2*minute;
-
- // check if pressure has changed significantly and we need to update ATIS
- double Psl = fgGetDouble("/environment/pressure-sea-level-inhg");
- if (fabs(Psl-_report.psl) >= 0.15)
- forceUpdate = true;
-
- FGAirport* apt = FGAirport::findByIdent(ident);
- int sequence = apt->getDynamics()->updateAtisSequence(interval, forceUpdate);
- if (!regen && sequence > LTRS) {
- //xx if (msg_OK) cout << "ATIS: no change: " << sequence << endl;
- //xx msg_time = cur_time;
- return false; // no change since last time
- }
-
- _report.psl = Psl;
- transmission = "";
-
- // collect data and create report
- createReport(apt);
-
- // add facility name
- genFacilityInfo();
-
- if (_type == ATIS) {
- // ATIS phraseology starts with "... airport information"
- transmission += airport_information + " ";
- } else {
- // AWOS
- transmission += Automated_weather_observation + " ";
- }
-
- string phonetic_seq_string = GetPhoneticLetter(sequence); // Add the sequence letter
- transmission += phonetic_seq_string + BRK;
-
- genTimeInfo();
-
- // some warnings may appear at the beginning
- genWarnings(-1);
-
- if (_type == ATIS) // as opposed to AWOS
- genRunwayInfo(apt);
-
- // some warnings may appear after runway info
- genWarnings(0);
-
- // transition level
- genTransitionLevel(apt);
-
- // weather
- if (!_report.concise)
- transmission += Weather + BRK;
-
- genWindInfo();
-
- // clouds and visibility
- {
- string vis_info, cloud_info;
- bool v = genVisibilityInfo(vis_info);
- bool c = genCloudInfo(cloud_info);
- _report.cavok = !(v || c);
- if (!_report.cavok)
- {
- // there is some visibility or cloud restriction
- transmission += vis_info + cloud_info;
- }
- else
- {
- // Abbreviation CAVOK vs full "clouds and visibility..." does not really depend on
- // US vs rest of the world, it really seems to depend on the airport. Just use
- // it as a heuristic.
- if ((_report.US_CA)||(_report.concise))
- transmission += cav_ok + BRK;
- else
- transmission += clouds_and_visibility_OK + BRK;
- }
- }
-
- // precipitation
- genPrecipitationInfo();
-
- // temperature
- genTemperatureInfo();
-
- // pressure
- genPressureInfo();
-
- // TODO check whether "no significant change" applies - somehow...
- transmission += No_sig + BRK; // sounds better with festival than "nosig"
-
- // some warnings may appear at the very end
- genWarnings(1);
-
- if ((!_report.concise)|| _report.US_CA)
- transmission += Advise_on_initial_contact_you_have_information;
- else
- transmission += information;
- transmission += " " + phonetic_seq_string + ".";
-
- if (!_report.US_CA)
- {
- // non-US ATIS ends with "out!"
- transmission += " " + out;
- }
-
- // Pause in between two messages must be 3-5 seconds
- transmission += " / / / / / / / / ";
-
- /////////////////////////////////////////////////////////
- // post-processing
- /////////////////////////////////////////////////////////
- transmission_readable = transmission;
-
- // Take the previous readable string and munge it to
- // be relatively-more acceptable to the primitive tts system.
- // Note that : ; and . are among the token-delimiters recognized
- // by the tts system.
- for (size_t where;;) {
- where = transmission.find_first_of(":.");
- if (where == string::npos) break;
- transmission.replace(where, 1, PAUSE);
- }
-
- return true;
-}
-
-/** Collect (most of) the data and create report.
- */
-void FGATIS::createReport(const FGAirport* apt)
-{
- // check country
- _report.US_CA = Apt_US_CA(ident);
-
- // switch to enable brief ATIS message (really depends on the airport)
- _report.concise = fgGetBool("/sim/atis/concise-reports", false);
-
- _report.ils = false;
-
- // time information
- string time_str = fgGetString("sim/time/gmt-string");
- // Warning - this is fragile if the time string format changes
- _report.hours = time_str.substr(0,2).c_str();
- _report.mins = time_str.substr(3,2).c_str();
-
- // pressure/temperature
- {
- double press, temp;
- double Tsl = fgGetDouble("/environment/temperature-sea-level-degc");
- tie(press, temp) = PT_vs_hpt(_geod.getElevationM(), _report.psl*atmodel::inHg, Tsl + atmodel::freezing);
- #if 0
- SG_LOG(SG_ATC, SG_ALERT, "Field P: " << press << " T: " << temp);
- SG_LOG(SG_ATC, SG_ALERT, "based on elev " << elev
- << " Psl: " << Psl
- << " Tsl: " << Tsl);
- #endif
- _report.qnh = FGAtmo().QNH(_geod.getElevationM(), press);
- _report.temp = int(SGMiscd::round(FGAtmo().fake_T_vs_a_us(_geod.getElevationFt(), Tsl)));
- }
-
- // dew point
- double dpsl = fgGetDouble("/environment/dewpoint-sea-level-degc");
- _report.dewpoint = int(SGMiscd::round(FGAtmo().fake_dp_vs_a_us(dpsl, _geod.getElevationFt())));
-
- // precipitation
- _report.rain_norm = fgGetDouble("environment/rain-norm");
- _report.snow_norm = fgGetDouble("environment/snow-norm");
-
- // NOTAMs
- _report.notam = 0;
- if (fgGetBool("/sim/atis/random-notams", true))
- {
- _report.notam = fgGetInt("/sim/atis/notam-id", 0); // fixed NOTAM for testing/debugging only
- if (!_report.notam)
- {
- // select pseudo-random NOTAM (changes every hour, differs for each airport)
- char cksum = 0;
- string name = apt->getName();
- for(string::iterator p = name.begin(); p != name.end(); p++)
- {
- cksum += *p;
- }
- cksum ^= atoi(_report.hours.c_str());
- _report.notam = cksum % 12; // 12 intentionally higher than number of available NOTAMs, so they don't appear too often
- // debugging
- //fgSetInt("/sim/atis/selected-notam", _report.notam);
- }
- }
-}
-
-void FGATIS::genPrecipitationInfo(void)
-{
- using namespace lex;
-
- double rain_norm = _report.rain_norm;
- double snow_norm = _report.snow_norm;
-
- // report rain or snow - which ever is worse
- if (rain_norm > 0.7)
- transmission += heavy + " " + rain + BRK;
- else
- if (snow_norm > 0.7)
- transmission += heavy + " " + snow + BRK;
- else
- if (rain_norm > 0.4)
- transmission += moderate + " " + rain + BRK;
- else
- if (snow_norm > 0.4)
- transmission += moderate + " " + snow + BRK;
- else
- if (rain_norm > 0.2)
- transmission += light + " " + rain + BRK;
- else
- if (snow_norm > 0.05)
- transmission += light + " " + snow + BRK;
- else
- if (rain_norm > 0.05)
- transmission += light + " " + drizzle + BRK;
-}
-
-void FGATIS::genTimeInfo(void)
-{
- using namespace lex;
-
- if (!_report.concise)
- transmission += Time + " ";
-
- // speak each digit separately:
- transmission += ConvertNumToSpokenDigits(_report.hours + _report.mins);
- transmission += " " + zulu + BRK;
-}
-
-bool FGATIS::genVisibilityInfo(string& vis_info)
-{
- using namespace lex;
-
- double visibility = fgGetDouble("/environment/config/boundary/entry[0]/visibility-m");
- bool IsMax = false;
- bool USE_KM = !_report.US_CA;
-
- vis_info += Visibility + ": ";
- if (USE_KM)
- {
- visibility /= 1000.0; // convert to statute miles
- // integer kilometers
- if (visibility >= 9.5)
- {
- visibility = 10;
- IsMax = true;
- }
- snprintf(buf, sizeof(buf), "%i", int(.5 + visibility));
- // "kelometers" instead of "kilometers" since the festival language generator doesn't get it right otherwise
- vis_info += ConvertNumToSpokenDigits(buf) + " " + kelometers;
- }
- else
- {
- visibility /= atmodel::sm; // convert to statute miles
- if (visibility < 0.25) {
- vis_info += less_than_one_quarter;
- } else if (visibility < 0.5) {
- vis_info += one_quarter;
- } else if (visibility < 0.75) {
- vis_info += one_half;
- } else if (visibility < 1.0) {
- vis_info += three_quarters;
- } else if (visibility >= 1.5 && visibility < 2.0) {
- vis_info += one_and_one_half;
- } else {
- // integer miles
- if (visibility > 9.5)
- {
- visibility = 10;
- IsMax = true;
- }
- snprintf(buf, sizeof(buf), "%i", int(.5 + visibility));
- vis_info += ConvertNumToSpokenDigits(buf);
- }
- }
- if (IsMax)
- {
- vis_info += " " + or_more;
- }
- vis_info += BRK;
- return !IsMax;
-}
-
-void FGATIS::addTemperature(int Temp)
-{
- if (Temp < 0)
- transmission += lex::minus + " ";
- else
- if (Temp > 0)
- {
- transmission += lex::plus + " ";
- }
- snprintf(buf, sizeof(buf), "%i", abs(Temp));
- transmission += ConvertNumToSpokenDigits(buf);
- if (_report.US_CA)
- transmission += " " + lex::Celsius;
-}
-
-void FGATIS::genTemperatureInfo()
-{
- // temperature
- transmission += lex::Temperature + ": ";
- addTemperature(_report.temp);
-
- // dewpoint
- transmission += BRK + lex::Dewpoint + ": ";
- addTemperature(_report.dewpoint);
-
- transmission += BRK;
-}
-
-bool FGATIS::genCloudInfo(string& cloud_info)
-{
- using namespace lex;
-
- bool did_some = false;
- bool did_ceiling = false;
-
- for (int layer = 0; layer <= 4; layer++) {
- snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/coverage", layer);
- string coverage = fgGetString(buf);
- if (coverage == clear)
- continue;
- snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/thickness-ft", layer);
- if (fgGetDouble(buf) == 0)
- continue;
- snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/elevation-ft", layer);
- double ceiling = int(fgGetDouble(buf) - _geod.getElevationFt());
- if (ceiling > 12000)
- continue;
-
- // BEWARE: At the present time, the environment system has no
- // way (so far as I know) to represent a "thin broken" or
- // "thin overcast" layer. If/when such things are implemented
- // in the environment system, code will have to be written here
- // to handle them.
-
- // First, do the prefix if any:
- if (coverage == scattered || coverage == few) {
- if (!did_some) {
- if (_report.concise)
- cloud_info += Clouds + ": ";
- else
- cloud_info += Sky_condition + ": ";
- did_some = true;
- }
- } else /* must be a ceiling */ if (!did_ceiling) {
- cloud_info += " " + Ceiling + ": ";
- did_ceiling = true;
- did_some = true;
- } else {
- cloud_info += " "; // no prefix required
- }
- int cig00 = int(SGMiscd::round(ceiling/100)); // hundreds of feet
- if (cig00) {
- int cig000 = cig00/10;
- cig00 -= cig000*10; // just the hundreds digit
- if (cig000) {
- snprintf(buf, sizeof(buf), "%i", cig000);
- cloud_info += ConvertNumToSpokenDigits(buf);
- cloud_info += " " + thousand + " ";
- }
- if (cig00) {
- snprintf(buf, sizeof(buf), "%i", cig00);
- cloud_info += ConvertNumToSpokenDigits(buf);
- cloud_info += " " + hundred + " ";
- }
- } else {
- // Should this be "sky obscured?"
- cloud_info += " " + zero + " "; // not "zero hundred"
- }
- cloud_info += coverage + BRK;
- }
- if (!did_some)
- cloud_info += " " + Sky + " " + clear + BRK;
- return did_some;
-}
-
-void FGATIS::genFacilityInfo(void)
-{
- if ((!_report.US_CA)&&(!_report.concise))
- {
- // UK CAA radiotelephony manual indicates ATIS transmissions start
- // with "This is ...", while US just starts with airport name.
- transmission += lex::This_is + " ";
- }
-
- // SG_LOG(SG_ATC, SG_ALERT, "ATIS: facility name: " << name);
-
- // Note that at this point, multi-word facility names
- // will sometimes contain hyphens, not spaces.
- std::vector<std::string> name_words;
- boost::split(name_words, name, boost::is_any_of(" -"));
-
- for( std::vector<string>::const_iterator wordp = name_words.begin();
- wordp != name_words.end(); wordp++) {
- string word(*wordp);
- // Remap some abbreviations that occur in apt.dat, to
- // make things nicer for the text-to-speech system:
- for (MSS::const_iterator replace = _remap.begin();
- replace != _remap.end(); replace++) {
- // Due to inconsistent capitalisation in the apt.dat file, we need
- // to do a case-insensitive comparison here.
- string tmp1 = word, tmp2 = replace->first;
- boost::algorithm::to_lower(tmp1);
- boost::algorithm::to_lower(tmp2);
- if (tmp1 == tmp2) {
- word = replace->second;
- break;
- }
- }
- transmission += word + " ";
- }
-}
-
-void FGATIS::genWindInfo(void)
-{
- using namespace lex;
-
- transmission += Wind + ": ";
-
- double wind_speed = fgGetDouble("/environment/config/boundary/entry[0]/wind-speed-kt");
- double wind_dir = fgGetDouble("/environment/config/boundary/entry[0]/wind-from-heading-deg");
- while (wind_dir <= 0) wind_dir += 360;
- // The following isn't as bad a kludge as it might seem.
- // It combines the magvar at the /aircraft/ location with
- // the wind direction in the environment/config array.
- // But if the aircraft is close enough to the station to
- // be receiving the ATIS signal, this should be a good-enough
- // approximation. For more-distant aircraft, the wind_dir
- // shouldn't be corrected anyway.
- // The less-kludgy approach would be to use the magvar associated
- // with the station, but that is not tabulated in the stationweather
- // structure as it stands, and computing it would be expensive.
- // Also note that as it stands, there is only one environment in
- // the entire FG universe, so the aircraft environment is the same
- // as the station environment anyway.
- wind_dir -= fgGetDouble("/environment/magnetic-variation-deg"); // wind_dir now magnetic
- if (wind_speed == 0) {
- // Force west-facing rwys to be used in no-wind situations
- // which is consistent with Flightgear's initial setup:
- wind_dir = 270;
- transmission += " " + light_and_variable;
- } else {
- // FIXME: get gust factor in somehow
- snprintf(buf, sizeof(buf), "%03.0f", 5*SGMiscd::round(wind_dir/5));
- transmission += ConvertNumToSpokenDigits(buf);
- if (!_report.concise)
- transmission += " " + degrees;
- transmission += " ";
- snprintf(buf, sizeof(buf), "%1.0f", wind_speed);
- transmission += at + " " + ConvertNumToSpokenDigits(buf);
- if (!_report.concise)
- transmission += " " + knots;
- }
- transmission += BRK;
-}
-
-void FGATIS::genTransitionLevel(const FGAirport* apt)
-{
- double hPa = _report.qnh/atmodel::mbar;
-
- /* Transition level is the flight level above which aircraft must use standard pressure and below
- * which airport pressure settings must be used.
- * Following definitions are taken from German ATIS:
- * QNH <= 977 hPa: TRL 80
- * QNH <= 1013 hPa: TRL 70
- * QNH > 1013 hPa: TRL 60
- * (maybe differs slightly for other countries...)
- */
- int tl = 60;
- if (hPa <= 977)
- tl = 80;
- else
- if (hPa <= 1013)
- tl = 70;
-
- // add an offset to the transition level for high altitude airports (just guessing here,
- // seems reasonable)
- double elevationFt = apt->getElevation();
- int e = int(elevationFt / 1000.0);
- if (e >= 3)
- {
- // TL steps in 10(00)ft
- tl += (e-2)*10;
- }
-
- snprintf(buf, sizeof(buf), "%02i", tl);
- transmission += lex::Transition_level + ": " + ConvertNumToSpokenDigits(buf) + BRK;
-}
-
-void FGATIS::genPressureInfo(void)
-{
- using namespace lex;
-
- // hectopascal for most of the world (not US, not CA)
- if(!_report.US_CA) {
- double hPa = _report.qnh/atmodel::mbar;
- transmission += QNH + ": ";
- snprintf(buf, sizeof(buf), "%03.0f", _report.qnh / atmodel::mbar);
- transmission += ConvertNumToSpokenDigits(buf);
- // "hectopascal" replaced "millibars" in new ATIS standard since 2011
- if ((!_report.concise)||(hPa < 1000))
- transmission += " " + hectopascal; // "hectopascal" must be provided for values below 1000 (to avoid confusion with inHg)
-
- // Many (European) airports (with lots of US traffic?) provide both, hPa and inHg announcements.
- // Europeans keep the "decimal" in inHg readings to make the distinction to hPa even more apparent.
- // hPa/inHg separated by "equals" or "or" with some airports
- if (_report.concise)
- transmission += " " + equals + " ";
- else
- transmission += " " + Or + " ";
- snprintf(buf, sizeof(buf), "%04.2f", _report.qnh / atmodel::inHg);
- transmission += ConvertNumToSpokenDigits(buf);
- if (!_report.concise)
- transmission += " " + inches;
- transmission += BRK;
- } else {
- // use inches of mercury for US/CA
- transmission += Altimeter + ": ";
- double asetting = _report.qnh / atmodel::inHg;
- // shift two decimal places, US/CA airports omit the "decimal" in inHg settings
- asetting *= 100.;
- snprintf(buf, sizeof(buf), "%04.0f", asetting);
- transmission += ConvertNumToSpokenDigits(buf);
- }
-
- transmission += BRK;
-}
-
-void FGATIS::genRunwayInfo(const FGAirport* apt)
-{
- using namespace lex;
-
- if (!apt)
- return;
-
- FGRunway* rwy = apt->getActiveRunwayForUsage();
- if (!rwy)
- return;
-
- string rwy_no = rwy->ident();
- if(rwy_no != "NN")
- {
- FGNavRecord* ils = rwy->ILS();
- if (ils)
- {
- _report.ils = true;
- transmission += Expect_I_L_S_approach + " "+ runway + " "+ConvertRwyNumToSpokenString(rwy_no) + BRK;
- if (fgGetBool("/sim/atis/announce-ils-frequency", false))
- {
- // this is handy - but really non-standard (so disabled by default)
- snprintf(buf, sizeof(buf), "%5.2f", ils->get_freq()/100.0);
- transmission += I_L_S + " " + ConvertNumToSpokenDigits(buf) + BRK;
- }
- }
- else
- {
- transmission += Expect_visual_approach + " "+ runway + " "+ConvertRwyNumToSpokenString(rwy_no) + BRK;
- }
-
- transmission += Landing_and_departing_runway + " ";
- transmission += ConvertRwyNumToSpokenString(rwy_no) + BRK;
- #ifdef ATIS_TEST
- if (msg_OK) {
- msg_time = cur_time;
- cout << "In atis.cxx, r.rwy_no: " << rwy_no
- << " wind_dir: " << wind_dir << endl;
- }
- #endif
- }
-}
-
-void FGATIS::genWarnings(int position)
-{
- using namespace lex;
- bool dayTime = (fgGetDouble("/sim/time/sun-angle-rad") < 1.57);
-
- if (position == -1) // warnings at beginning of ATIS
- {
- // bird related warnings at day-time only (birds are VFR-only! ;-) )
- if (dayTime)
- {
- if (_report.notam == 1)
- transmission += Attention + ": " + flock_of_birds + " " + in_the_vicinity_of_the_airport + BRK;
- else
- if (_report.notam == 2)
- transmission += Attention + ": " + bird_activity + " " + in_the_vicinity_of_the_airport + BRK;
- }
- }
- else
- if (position == 0) // warnings after runway messages
- {
- if ((_report.notam == 3)&&(_report.ils))
- {
- // "__I_LS_" necessary to trick the language generator into pronouncing it properly
- transmission += Attention + ": " + short_time__I_LS_interference_possible_by_taxiing_aircraft + BRK;
- }
- }
- else
- if (position == 1) // warnings at the end of the report
- {
- // "runway wet-wet-wet" warning in heavy rain
- if (_report.rain_norm > 0.6)
- {
- // "wet" is repeated 3 times in ATIS warnings, since the word is difficult
- // to understand over radio - but the message is important.
- transmission += runway_wet + " " + wet + " " + wet + BRK;
- }
-
- if (_report.notam == 4)
- {
- // intentional: "reed" instead of "read" since festival gets it wrong otherwise
- transmission += reed_back_all_runway_hold_instructions + BRK;
- }
- else
- if ((_report.notam == 5)&& _report.cavok && dayTime &&
- (_report.rain_norm == 0) && (_report.snow_norm == 0)) // ;-)
- {
- transmission += Attention + ": " + glider_operation_in_sector + BRK;
- }
- }
-}
-
-// Put the transmission into the property tree.
-// You can see it by pointing a web browser
-// at the property tree. The second comm radio is:
-// http://localhost:5400/instrumentation/comm[1]
-//
-// (Also, if in debug mode, dump it to the console.)
-void FGATIS::treeOut(int msg_OK)
-{
- _atis->setStringValue("<pre>\n" + transmission_readable + "</pre>\n");
- SG_LOG(SG_ATC, SG_DEBUG, "**** ATIS active on: " << _name <<
- "transmission: " << transmission_readable);
-}
-
-
-class RangeFilter : public CommStation::Filter
-{
-public:
- RangeFilter( const SGGeod & pos ) :
- CommStation::Filter(),
- _cart(SGVec3d::fromGeod(pos)),
- _pos(pos)
- {
- }
-
- virtual bool pass(FGPositioned* aPos) const
- {
- flightgear::CommStation * stn = dynamic_cast<flightgear::CommStation*>(aPos);
- if( NULL == stn )
- return false;
-
- // do the range check in cartesian space, since the distances are potentially
- // large enough that the geodetic functions become unstable
- // (eg, station on opposite side of the planet)
- double rangeM = SGMiscd::max( stn->rangeNm(), 10.0 ) * SG_NM_TO_METER;
- double d2 = distSqr( aPos->cart(), _cart);
-
- return d2 <= (rangeM * rangeM);
- }
-
- virtual CommStation::Type minType() const
- {
- return CommStation::FREQ_ATIS;
- }
-
- virtual CommStation::Type maxType() const
- {
- return CommStation::FREQ_AWOS;
- }
-
-private:
- SGVec3d _cart;
- SGGeod _pos;
-};
-
-// Search for ATC stations by frequency
-bool FGATIS::search(double dt)
-{
- double frequency = _freq->getDoubleValue();
-
- // Note: 122.375 must be rounded DOWN to 122370
- // in order to be consistent with apt.dat et cetera.
- int freqKhz = 10 * static_cast<int>(frequency * 100 + 0.25);
-
- // only search tuned frequencies when necessary
- _time_before_search_sec -= dt;
-
- // throttle frequency searches
- if ((freqKhz == _last_frequency)&&(_time_before_search_sec > 0))
- return false;
-
- _last_frequency = freqKhz;
- _time_before_search_sec = 4.0;
-
- // Position of the Users Aircraft
- SGGeod aircraftPos = SGGeod::fromDegFt(_lon_node->getDoubleValue(),
- _lat_node->getDoubleValue(),
- _elev_node->getDoubleValue());
-
- RangeFilter rangeFilter(aircraftPos );
- CommStation* sta = CommStation::findByFreq(freqKhz, aircraftPos, &rangeFilter );
- SetStation(sta);
-
- return true;
-}
+++ /dev/null
-// atis.hxx -- ATIS class
-//
-// Written by David Luff, started October 2001.
-// Based on nav.hxx by Curtis Olson, started April 2000.
-//
-// Copyright (C) 2001 David C. Luff - david.luff@nottingham.ac.uk
-//
-// 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 Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-#ifndef _FG_ATIS_HXX
-#define _FG_ATIS_HXX
-
-#include <string>
-#include <iosfwd>
-
-#include <simgear/compiler.h>
-#include <simgear/timing/sg_time.hxx>
-#include <simgear/props/props.hxx>
-
-#include "ATC.hxx"
-
-class FGAirport;
-
-typedef std::map<std::string,std::string> MSS;
-
-class FGATIS : public FGATC {
-
- std::string _name;
- int _num;
-
- SGPropertyNode_ptr _root;
- SGPropertyNode_ptr _volume;
- SGPropertyNode_ptr _serviceable;
- SGPropertyNode_ptr _operable;
- SGPropertyNode_ptr _electrical;
- SGPropertyNode_ptr _freq;
- SGPropertyNode_ptr _atis;
-
- // Pointers to current users position
- SGPropertyNode_ptr _lon_node;
- SGPropertyNode_ptr _lat_node;
- SGPropertyNode_ptr _elev_node;
-
- SGPropertyChangeCallback<FGATIS> _cb_attention;
-
- // The actual ATIS transmission
- // This is generated from the prevailing conditions when required.
- // This is the version with markup, suitable for voice synthesis:
- std::string transmission;
-
- // Same as above, but in a form more readable as text.
- std::string transmission_readable;
-
- // for failure modeling
- std::string trans_ident; // transmitted ident
- double old_volume;
- bool atis_failed; // atis failed?
- time_t msg_time; // for moderating error messages
- time_t cur_time;
- int msg_OK;
- bool _attention;
- bool _check_transmission;
-
- bool _prev_display; // Previous value of _display flag
- MSS _remap; // abbreviations to be expanded
-
- // internal periodic station search timer
- double _time_before_search_sec;
- int _last_frequency;
-
- // temporary buffer for string conversions
- char buf[100];
-
- // data for the current ATIS report
- struct
- {
- std::string phonetic_seq_string;
- bool US_CA;
- bool cavok;
- bool concise;
- bool ils;
- int temp;
- int dewpoint;
- double psl;
- double qnh;
- double rain_norm, snow_norm;
- int notam;
- std::string hours,mins;
- } _report;
-
-public:
-
- FGATIS(const std::string& name, int num);
-
- void init();
- void reinit();
-
- void attend(SGPropertyNode* node);
-
- //run the ATIS instance
- void update(double dt);
-
- //inline void set_type(const atc_type tp) {type = tp;}
- inline const std::string& get_trans_ident() { return trans_ident; }
-
-protected:
- virtual FGATCVoice* GetVoicePointer();
-
-private:
-
- void createReport (const FGAirport* apt);
-
- /** generate the ATIS transmission text */
- bool genTransmission (const int regen, bool forceUpdate);
- void genTimeInfo (void);
- void genFacilityInfo (void);
- void genPrecipitationInfo(void);
- bool genVisibilityInfo (std::string& vis_info);
- bool genCloudInfo (std::string& cloud_info);
- void genWindInfo (void);
- void genTemperatureInfo (void);
- void genTransitionLevel (const FGAirport* apt);
- void genPressureInfo (void);
- void genRunwayInfo (const FGAirport* apt);
- void genWarnings (int position);
-
- void addTemperature (int Temp);
-
- // Put the text into the property tree
- // (and in debug mode, print it on the console):
- void treeOut(int msgOK);
-
- // Search the specified radio for stations on the same frequency and in range.
- bool search(double dt);
-
- friend std::istream& operator>> ( std::istream&, FGATIS& );
-};
-
-typedef int (FGATIS::*int_getter)() const;
-
-#endif // _FG_ATIS_HXX
+++ /dev/null
-#ifndef _FG_ATIS_LEXICON_HXX
-#define _FG_ATIS_LEXICON_HXX
-
-#include <string>
-
-// NOTE: This file serves as a database.
-// It is read by some utility programs that synthesize
-// the library of spoken words.
-
-#define Q(word) const std::string word(#word);
-
-namespace lex {
-Q(Airport)
-Q(Airfield)
-Q(Airbase)
-Q(Junior)
-Q(Celsius)
-Q(Wind)
-Q(zulu)
-Q(zulu_weather)
-Q(Automated_weather_observation)
-Q(Weather)
-Q(airport_information)
-Q(International)
-Q(Regional)
-Q(County)
-Q(Municipal)
-Q(Memorial)
-Q(Field)
-Q(Air_Force_Base)
-Q(Army_Air_Field)
-Q(Marine_Corps_Air_Station)
-Q(light_and_variable)
-Q(at)
-Q(thousand)
-Q(hundred)
-Q(zero)
-Q(Temperature)
-Q(clear)
-Q(isolated)
-Q(few)
-Q(scattered)
-Q(broken)
-Q(overcast)
-Q(thin)
-Q(Sky_condition)
-Q(Sky)
-Q(Clouds)
-Q(Ceiling)
-Q(minus)
-Q(Dewpoint)
-Q(Visibility)
-Q(less_than_one_quarter)
-Q(one_quarter)
-Q(one_half)
-Q(three_quarters)
-Q(one_and_one_half)
-Q(Altimeter)
-Q(QNH)
-Q(Landing_and_departing_runway)
-Q(Advise_on_initial_contact_you_have_information)
-Q(This_is)
-Q(information)
-Q(millibars)
-Q(hectopascal)
-Q(inches)
-Q(I_L_S)
-Q(visual)
-Q(cav_ok)
-Q(clouds_and_visibility_OK)
-Q(out)
-Q(equals)
-Q(Expect_I_L_S_approach)
-Q(Expect_visual_approach)
-Q(Transition_level)
-Q(No_sig)
-Q(Time)
-Q(kelometers)
-Q(Attention)
-Q(flock_of_birds)
-Q(bird_activity)
-Q(in_the_vicinity_of_the_airport)
-Q(short_time__I_LS_interference_possible_by_taxiing_aircraft)
-Q(reed_back_all_runway_hold_instructions)
-Q(glider_operation_in_sector)
-Q(airport)
-Q(runway_wet)
-Q(runway_in_use)
-Q(arrivals)
-Q(runway)
-Q(runways)
-Q(expect)
-Q(approach)
-Q(departures)
-Q(wet)
-Q(ice)
-Q(closed)
-Q(light)
-Q(moderate)
-Q(heavy)
-Q(rain)
-Q(drizzle)
-Q(snow)
-Q(fog)
-Q(plus)
-Q(hours)
-Q(variable)
-Q(from)
-Q(Or)
-Q(And)
-Q(to)
-Q(maximum)
-Q(between)
-Q(degrees)
-Q(or_more)
-Q(left)
-Q(right)
-Q(center)
-Q(knots)
-}
-
-#undef Q
-#endif // _FG_ATIS_LEXICON_HXX
+++ /dev/null
-// NOTE: This file serves as a database.
-// It is read by some utility programs that synthesize
-// the library of spoken words.
-
-REMAP(Intl, International)
-REMAP(Rgnl, Regional)
-REMAP(Co, County)
-REMAP(Muni, Municipal)
-REMAP(Mem, Memorial)
-REMAP(Meml, Memorial)
-REMAP(Apt, Airport)
-REMAP(Arpt, Airport)
-REMAP(Fld, Field)
-REMAP(AFLD, Airfield)
-REMAP(AFB, Air_Force_Base)
-REMAP(AB, Airbase)
-REMAP(AAF, Army_Air_Field)
-REMAP(MCAS, Marine_Corps_Air_Station)
-REMAP(JR, Junior)
-REMAP(GKI, NIL)
Airports
Aircraft
ATC
- ATCDCL
Canvas
Radio
Autopilot
#include "kln89_symbols.hxx"
#include <iostream>
-#include <ATCDCL/ATCProjection.hxx>
-
#include <Main/fg_props.hxx>
#include <simgear/structure/commands.hxx>
#include <Airports/airport.hxx>
double mapScaleMeters = _mapScale * (_mapScaleUnits == 0 ? SG_NM_TO_METER : 1000);
// TODO - use an aligned projection when either DTK or TK up!
- FGATCAlignedProjection mapProj(SGGeod::fromRad(_gpsLon, _gpsLat), _mapHeading);
+ AlignedProjection mapProj(SGGeod::fromRad(_gpsLon, _gpsLat), _mapHeading);
double meter_per_pix = (_mapOrientation == 0 ? mapScaleMeters / 20.0f : mapScaleMeters / 29.0f);
// SGGeod bottomLeft = mapProj.ConvertFromLocal(SGVec3d(gps_max(-57.0 * meter_per_pix, -50000), gps_max((_mapOrientation == 0 ? -20.0 * meter_per_pix : -11.0 * meter_per_pix), -25000), 0.0));
// SGGeod topRight = mapProj.ConvertFromLocal(SGVec3d(gps_min(54.0 * meter_per_pix, 50000), gps_min((_mapOrientation == 0 ? 20.0 * meter_per_pix : 29.0 * meter_per_pix), 25000), 0.0));
* sin(GetGreatCircleCourse(wp1.lat, wp1.lon, _gpsLat, _gpsLon) - GetGreatCircleCourse(wp1.lat, wp1.lon, wp2.lat, wp2.lon)));
return(Rad2Nm(xtd));
}
+
+AlignedProjection::AlignedProjection()
+{
+ SGGeod g; // ctor initializes to zero
+ Init( g, 0.0 );
+}
+
+AlignedProjection::AlignedProjection(const SGGeod& centre, double heading)
+{
+ Init( centre, heading );
+}
+
+AlignedProjection::~AlignedProjection() {
+}
+
+void AlignedProjection::Init(const SGGeod& centre, double heading) {
+ _origin = centre;
+ _theta = heading * SG_DEGREES_TO_RADIANS;
+ _correction_factor = cos(_origin.getLatitudeRad());
+}
+
+SGVec3d AlignedProjection::ConvertToLocal(const SGGeod& pt) {
+ // convert from lat/lon to orthogonal
+ double delta_lat = pt.getLatitudeRad() - _origin.getLatitudeRad();
+ double delta_lon = pt.getLongitudeRad() - _origin.getLongitudeRad();
+ double y = sin(delta_lat) * SG_EQUATORIAL_RADIUS_M;
+ double x = sin(delta_lon) * SG_EQUATORIAL_RADIUS_M * _correction_factor;
+
+ // Align
+ if(_theta != 0.0) {
+ double xbar = x;
+ x = x*cos(_theta) - y*sin(_theta);
+ y = (xbar*sin(_theta)) + (y*cos(_theta));
+ }
+
+ return SGVec3d(x, y, pt.getElevationM());
+}
+
+SGGeod AlignedProjection::ConvertFromLocal(const SGVec3d& pt) {
+ // de-align
+ double thi = _theta * -1.0;
+ double x = pt.x()*cos(thi) - pt.y()*sin(thi);
+ double y = (pt.x()*sin(thi)) + (pt.y()*cos(thi));
+
+ // convert from orthogonal to lat/lon
+ double delta_lat = asin(y / SG_EQUATORIAL_RADIUS_M);
+ double delta_lon = asin(x / SG_EQUATORIAL_RADIUS_M) / _correction_factor;
+
+ return SGGeod::fromRadM(_origin.getLongitudeRad()+delta_lon, _origin.getLatitudeRad()+delta_lat, pt.z());
+}
+
int _min;
};
+// AlignedProjection - a class to project an area local to a runway onto an orthogonal co-ordinate system
+// with the origin at the threshold and the runway aligned with the y axis.
+class AlignedProjection {
+
+public:
+ AlignedProjection();
+ AlignedProjection(const SGGeod& centre, double heading);
+ ~AlignedProjection();
+
+ void Init(const SGGeod& centre, double heading);
+
+ // Convert a lat/lon co-ordinate (degrees) to the local projection (meters)
+ SGVec3d ConvertToLocal(const SGGeod& pt);
+
+ // Convert a local projection co-ordinate (meters) to lat/lon (degrees)
+ SGGeod ConvertFromLocal(const SGVec3d& pt);
+
+private:
+ SGGeod _origin; // lat/lon of local area origin (the threshold)
+ double _theta; // the rotation angle for alignment in radians
+ double _correction_factor; // Reduction in surface distance per degree of longitude due to latitude. Saves having to do a cos() every call.
+
+};
+
// ------------------------------------------------------------------------------
// TODO - merge generic GPS functions instead and split out KLN specific stuff.