1
0
Fork 0

Cleanup of ATCDCL

- move FGATCAlignedProjection class as AlignedProjection
  to dclgps, the only place where it's currently used
- remove now obsolete files in ATCDCL
This commit is contained in:
Torsten Dreyer 2014-04-30 10:22:51 +02:00
parent 1c5c1b2cb1
commit d2c000699f
19 changed files with 76 additions and 3061 deletions

View file

@ -1,413 +0,0 @@
// 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

View file

@ -1,226 +0,0 @@
// 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

View file

@ -1,79 +0,0 @@
// 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());
}

View file

@ -1,50 +0,0 @@
// 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

View file

@ -1,297 +0,0 @@
// 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;
}

View file

@ -1,71 +0,0 @@
// 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

View file

@ -1,271 +0,0 @@
// 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);
}

View file

@ -1,100 +0,0 @@
// 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);

View file

@ -1,147 +0,0 @@
// 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;
}

View file

@ -1,66 +0,0 @@
// 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

View file

@ -1,11 +0,0 @@
include(FlightGearComponent)
set(SOURCES
ATCProjection.cxx
)
set(HEADERS
ATCProjection.hxx
)
flightgear_component(ATCDCL "${SOURCES}" "${HEADERS}")

File diff suppressed because it is too large Load diff

View file

@ -1,153 +0,0 @@
// 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

View file

@ -1,123 +0,0 @@
#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

View file

@ -1,20 +0,0 @@
// 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)

View file

@ -7,7 +7,6 @@ foreach( mylibfolder
Airports
Aircraft
ATC
ATCDCL
Canvas
Radio
Autopilot

View file

@ -44,8 +44,6 @@
#include "kln89_symbols.hxx"
#include <iostream>
#include <ATCDCL/ATCProjection.hxx>
#include <Main/fg_props.hxx>
#include <simgear/structure/commands.hxx>
#include <Airports/airport.hxx>
@ -763,7 +761,7 @@ void KLN89::DrawMap(bool draw_avs) {
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));

View file

@ -1564,3 +1564,54 @@ double DCLGPS::CalcCrossTrackDeviation(const GPSWaypoint& wp1, const GPSWaypoint
* 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());
}

View file

@ -166,6 +166,30 @@ private:
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.