diff --git a/projects/VC90/FlightGear/FlightGear.vcproj b/projects/VC90/FlightGear/FlightGear.vcproj index 98fde43d6..084a5531f 100644 --- a/projects/VC90/FlightGear/FlightGear.vcproj +++ b/projects/VC90/FlightGear/FlightGear.vcproj @@ -569,14 +569,6 @@ RelativePath="..\..\..\src\ATCDCL\atis.hxx" > - - - - - - - - @@ -2489,6 +2473,14 @@ RelativePath="..\..\..\src\FDM\NullFDM.hxx" > + + + + + + + + @@ -3220,7 +3220,7 @@ - + @@ -3381,6 +3381,14 @@ RelativePath="..\..\..\src\Environment\fgwind.hxx" > + + + + @@ -3405,6 +3413,14 @@ RelativePath="..\..\..\src\Environment\precipitation_mgr.hxx" > + + + + @@ -3429,22 +3445,6 @@ RelativePath="..\..\..\src\Environment\terrainsampler.hxx" > - - - - - - - - + + + + @@ -3800,14 +3808,6 @@ - - - - + +#include + +namespace { + +typedef std::multimap FrequencyMap; +static FrequencyMap static_frequencies; + +typedef std::pair FrequencyMapRange; + +} // of anonymous namespace + +namespace flightgear { + +CommStation::CommStation(const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq) : + FGPositioned(t, name, pos), + mRangeNM(range), + mFreqKhz(freq), + mAirport(NULL) +{ + static_frequencies.insert(std::make_pair(freq, this)); + + init(true); +} + +void CommStation::setAirport(FGAirport* apt) +{ + mAirport = apt; +} + +double CommStation::freqMHz() const +{ + return mFreqKhz / 100.0; +} + +PositionedBinding* +CommStation::createBinding(SGPropertyNode* nd) const +{ + return new CommStationBinding(this, nd); +} + +CommStation* +CommStation::findByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt) +{ + FrequencyMapRange range = static_frequencies.equal_range(freqKhz); + FGPositioned::List results; + for (; range.first != range.second; ++range.first) { + CommStation* sta = range.first->second; + if (filt && !filt->pass(sta)) { + continue; // filtered out + } + + results.push_back(sta); + } + + if (results.empty()) { + return NULL; + } + + FGPositioned::sortByRange(results, pos); + return (CommStation*) results.front().ptr(); +} + +} // of namespace flightgear diff --git a/src/ATC/CommStation.hxx b/src/ATC/CommStation.hxx new file mode 100644 index 000000000..89c0d1f43 --- /dev/null +++ b/src/ATC/CommStation.hxx @@ -0,0 +1,39 @@ +#ifndef FG_ATC_COMM_STATION_HXX +#define FG_ATC_COMM_STATION_HXX + +#include + +class FGAirport; + +namespace flightgear +{ + +class CommStation : public FGPositioned +{ +public: + CommStation(const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq); + + void setAirport(FGAirport* apt); + FGAirport* airport() const { return mAirport; } + + virtual flightgear::PositionedBinding* createBinding(SGPropertyNode* nd) const; + + int rangeNm() const + { return mRangeNM; } + + int freqKHz() const + { return mFreqKhz; } + + double freqMHz() const; + + static CommStation* findByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt = NULL); +private: + int mRangeNM; + int mFreqKhz; + FGAirport* mAirport; +}; + +} // of namespace flightgear + +#endif // of FG_ATC_COMM_STATION_HXX + diff --git a/src/ATC/Makefile.am b/src/ATC/Makefile.am index fd5302aac..522a82033 100644 --- a/src/ATC/Makefile.am +++ b/src/ATC/Makefile.am @@ -1,9 +1,9 @@ noinst_LIBRARIES = libATC.a libATC_a_SOURCES = \ - atis_mgr.cxx atis_mgr.hxx \ atcdialog.cxx atcdialog.hxx \ atc_mgr.cxx atc_mgr.hxx \ + CommStation.cxx CommStation.hxx \ trafficcontrol.cxx trafficcontrol.hxx INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/ATC/atis_mgr.cxx b/src/ATC/atis_mgr.cxx deleted file mode 100644 index 034a8358a..000000000 --- a/src/ATC/atis_mgr.cxx +++ /dev/null @@ -1,46 +0,0 @@ -/****************************************************************************** - * atis.cxx - * Written by Durk Talsma, started August 1, 2010. - * - * 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 - -#include -#include "atis_mgr.hxx" - -FGAtisManager::FGAtisManager() { - -} - -FGAtisManager::~FGAtisManager() { - -} - -void FGAtisManager::init() { - SGSubsystem::init(); -} - -void FGAtisManager::update ( double time ) { - //cerr << "ATIS code is running at time: " << time << endl; -} diff --git a/src/ATC/atis_mgr.hxx b/src/ATC/atis_mgr.hxx deleted file mode 100644 index 12fc4fd07..000000000 --- a/src/ATC/atis_mgr.hxx +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- Mode: C++ -*- ***************************************************** - * atic.hxx - * Written by Durk Talsma. Started August 1, 2010; based on earlier work - * by David C. Luff - * - * 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 _ATIS_HXX_ -#define _ATIS_HXX_ - -#include - -class FGAtisManager : public SGSubsystem -{ -private: - -public: - FGAtisManager(); - ~FGAtisManager(); - void init(); - void update(double time); -}; - -#endif // _ATIS_HXX_ \ No newline at end of file diff --git a/src/ATCDCL/ATC.cxx b/src/ATCDCL/ATC.cxx index 30d3d23e8..4701a67ee 100644 --- a/src/ATCDCL/ATC.cxx +++ b/src/ATCDCL/ATC.cxx @@ -31,8 +31,8 @@ #include
#include
- - +#include +#include FGATC::FGATC() : _playing(false), @@ -181,14 +181,20 @@ void FGATC::NotifyTransmissionFinished(const string& rid) { } } -void FGATC::SetData(ATCData* d) { - _type = d->type; - _geod = d->geod; - _cart = d->cart; - range = d->range; - ident = d->ident; - name = d->name; - freq = d->freq; +void FGATC::SetStation(flightgear::CommStation* sta) { + switch (sta->type()) { + case FGPositioned::FREQ_ATIS: _type = ATIS; break; + case FGPositioned::FREQ_AWOS: _type = AWOS; break; + default: + throw sg_exception("unsupported comm station type"); + } + + _geod = sta->geod(); + _cart = sta->cart(); + range = sta->rangeNm(); + ident = sta->airport()->ident(); + name = sta->airport()->name(); + freq = sta->freqKHz(); } // Render a transmission diff --git a/src/ATCDCL/ATC.hxx b/src/ATCDCL/ATC.hxx index 2109b697b..17f743cd7 100644 --- a/src/ATCDCL/ATC.hxx +++ b/src/ATCDCL/ATC.hxx @@ -37,6 +37,11 @@ class SGSampleGroup; +namespace flightgear +{ + class CommStation; +} + // Convert a frequency in MHz to tens of kHz // so we can use it e.g. as an index into commlist_freq // @@ -140,7 +145,7 @@ public: inline atc_type GetType() { return _type; } // Set the core ATC data - void SetData(ATCData* d); + void SetStation(flightgear::CommStation* sta); inline int get_freq() const { return freq; } inline void set_freq(const int fq) {freq = fq;} diff --git a/src/ATCDCL/ATCDialog.cxx b/src/ATCDCL/ATCDialog.cxx index a1c3c5e84..026a65449 100644 --- a/src/ATCDCL/ATCDialog.cxx +++ b/src/ATCDCL/ATCDialog.cxx @@ -34,9 +34,9 @@ #include "ATCDialog.hxx" #include "ATC.hxx" #include "ATCmgr.hxx" -#include "commlist.hxx" #include "ATCutils.hxx" #include +#include #include @@ -287,21 +287,17 @@ void FGATCDialog::PopupCallback(int num) { } } -// map() key data type (removes duplicates and sorts by distance) -struct atcdata { - atcdata() {} - atcdata(const string i, const string n, const double d) { - id = i, name = n, distance = d; - } - bool operator<(const atcdata& a) const { - return id != a.id && distance < a.distance; - } - bool operator==(const atcdata& a) const { - return id == a.id && distance == a.distance; - } - string id; - string name; - double distance; +class AirportsWithATC : public FGAirport::AirportFilter +{ +public: + virtual FGPositioned::Type maxType() const { + return FGPositioned::SEAPORT; + } + + virtual bool passAirport(FGAirport* aApt) const + { + return (!aApt->commStations().empty()); + } }; void FGATCDialog::FreqDialog() { @@ -316,38 +312,25 @@ void FGATCDialog::FreqDialog() { // remove all dynamic airport/ATC buttons button_group->removeChildren("button", false); - // Find the ATC stations within a reasonable range - comm_list_type atc_stations; - comm_list_iterator atc_stat_itr; SGGeod geod(SGGeod::fromDegFt(fgGetDouble("/position/longitude-deg"), fgGetDouble("/position/latitude-deg"), fgGetDouble("/position/altitude-ft"))); - SGVec3d aircraft = SGVec3d::fromGeod(geod); - - // search stations in range - int num_stat = current_commlist->FindByPos(geod, 50.0, &atc_stations); - if (num_stat != 0) { - map uniq; - // fill map (sorts by distance and removes duplicates) - comm_list_iterator itr = atc_stations.begin(); - for (; itr != atc_stations.end(); ++itr) { - double distance = distSqr(aircraft, itr->cart); - uniq[atcdata(itr->ident, itr->name, distance)] = true; - } - // create button per map entry (modified copy of ) - map::iterator uit = uniq.begin(); - for (int n = 0; uit != uniq.end() && n < 6; ++uit, ++n) { // max 6 buttons - SGPropertyNode *entry = button_group->getNode("button", n, true); - copyProperties(button_group->getNode("button-template", true), entry); - entry->removeChildren("enabled", true); - entry->setStringValue("legend", uit->first.id.c_str()); - entry->setStringValue("binding[0]/value", uit->first.id.c_str()); - } - } + AirportsWithATC filt; + FGPositioned::List results = FGPositioned::findWithinRange(geod, 50.0, &filt); + FGPositioned::sortByRange(results, geod); + for (unsigned int r=0; (rgetNode("button", r, true); + copyProperties(button_group->getNode("button-template", true), entry); + entry->removeChildren("enabled", true); + entry->setStringValue("legend", results[r]->ident()); + entry->setStringValue("binding[0]/value", results[r]->ident()); + } + // (un)hide message saying no things in range SGPropertyNode_ptr range_error = getNamedNode(dlg, "no-atc-in-range"); - range_error->setBoolValue("enabled", !num_stat); + range_error->setBoolValue("enabled", !results.empty()); _gui->showDialog(dialog_name); } @@ -378,43 +361,33 @@ void FGATCDialog::FreqDisplay(string& ident) { label = ident + " Frequencies"; dlg->setStringValue("text/label", label.c_str()); - int n = 0; // Number of ATC frequencies at this airport - - comm_list_type stations; - int found = current_commlist->FindByPos(a->geod(), 20.0, &stations); - if(found) { - comm_list_iterator itr = stations.begin(); - for (n = 0; itr != stations.end(); ++itr) { - if(itr->ident != ident) - continue; - - if(itr->type == INVALID) - continue; - - // add frequency line (modified copy of ) - SGPropertyNode *entry = freq_group->getNode("group", n, true); - copyProperties(freq_group->getNode("group-template", true), entry); - entry->removeChildren("enabled", true); - - ostringstream ostr; - ostr << itr->type; - entry->setStringValue("text[0]/label", ostr.str()); - - char buf[8]; - snprintf(buf, 8, "%.2f", (itr->freq / 100.0)); // Convert from KHz to MHz - if(buf[5] == '3') buf[5] = '2'; - if(buf[5] == '8') buf[5] = '7'; - buf[7] = '\0'; - - entry->setStringValue("text[1]/label", buf); - n++; - } - } - if(n == 0) { - label = "No frequencies found for airport " + ident; + const flightgear::CommStationList& comms(a->commStations()); + if (comms.empty()) { + label = "No frequencies found for airport " + ident; mkDialog(label.c_str()); return; - } + } + + int n = 0; + for (unsigned int c=0; c < comms.size(); ++c) { + flightgear::CommStation* comm = comms[c]; + + // add frequency line (modified copy of ) + SGPropertyNode *entry = freq_group->getNode("group", n, true); + copyProperties(freq_group->getNode("group-template", true), entry); + entry->removeChildren("enabled", true); + + entry->setStringValue("text[0]/label", comm->ident()); + + char buf[8]; + snprintf(buf, 8, "%.2f", comm->freqMHz()); + if(buf[5] == '3') buf[5] = '2'; + if(buf[5] == '8') buf[5] = '7'; + buf[7] = '\0'; + + entry->setStringValue("text[1]/label", buf); + ++n; + } _gui->showDialog(dialog_name); } diff --git a/src/ATCDCL/ATCmgr.cxx b/src/ATCDCL/ATCmgr.cxx index 537bfddef..8d8a6fc58 100644 --- a/src/ATCDCL/ATCmgr.cxx +++ b/src/ATCDCL/ATCmgr.cxx @@ -24,29 +24,18 @@ #include #include +#include #include +#include +#include
#include "ATCmgr.hxx" -#include "commlist.hxx" #include "ATCDialog.hxx" #include "ATCutils.hxx" +#include "atis.hxx" - -/* -// periodic radio station search wrapper -static void fgATCSearch( void ) { - globals->get_ATC_mgr()->Search(); -} -*/ //This wouldn't compile - including Time/event.hxx breaks it :-( - // Is this still true?? -EMH- - -AirportATC::AirportATC() : - atis_freq(0.0), - atis_active(false) - //airport_atc_map.clear(); -{ -} +using flightgear::CommStation; FGATCMgr::FGATCMgr() : initDone(false), @@ -148,44 +137,6 @@ void FGATCMgr::update(double dt) { //cout << "Leaving update..." << endl; } - -// Returns frequency in KHz - should I alter this to return in MHz? -unsigned short int FGATCMgr::GetFrequency(const string& ident, const atc_type& tp) { - ATCData test; - bool ok = current_commlist->FindByCode(ident, test, tp); - return(ok ? test.freq : 0); -} - -// Register the fact that the comm radio is tuned to an airport -// Channel is zero based -bool FGATCMgr::CommRegisterAirport(const string& ident, int chan, const atc_type& tp) { - SG_LOG(SG_ATC, SG_BULK, "Comm channel " << chan << " registered airport " << ident); - //cout << "Comm channel " << chan << " registered airport " << ident << ' ' << tp << '\n'; - if(airport_atc_map.find(ident) != airport_atc_map.end()) { - if(tp == ATIS || tp == AWOS) { - airport_atc_map[ident]->atis_active = true; - } - return(true); - } else { - //cout << "NOT IN MAP - creating new..." << endl; - const FGAirport *ap = fgFindAirportID(ident); - if (ap) { - AirportATC *a = new AirportATC; - // I'm not entirely sure that this AirportATC structure business is actually needed - it just duplicates what we can find out anyway! - a->geod = ap->geod(); - a->atis_freq = GetFrequency(ident, ATIS) - || GetFrequency(ident, AWOS); - a->atis_active = false; - if(tp == ATIS || tp == AWOS) { - a->atis_active = true; - } - airport_atc_map[ident] = a; - return(true); - } - } - return(false); -} - typedef map MSI; void FGATCMgr::ZapOtherService(const string ncunit, const string svc_name){ @@ -224,16 +175,6 @@ FGATC* FGATCMgr::FindInList(const string& id, const atc_type& tp) { return (*atc_list)[ndx]; } -// Returns true if the airport is found in the map -bool FGATCMgr::GetAirportATCDetails(const string& icao, AirportATC* a) { - if(airport_atc_map.find(icao) != airport_atc_map.end()) { - *a = *airport_atc_map[icao]; - return(true); - } else { - return(false); - } -} - // Return a pointer to an appropriate voice for a given type of ATC // creating the voice if necessary - ie. make sure exactly one copy // of every voice in use exists in memory. @@ -303,49 +244,44 @@ void FGATCMgr::FreqSearch(const string navcomm, const int unit) { if (!comm_node) return; // no such radio unit ATCData data; - double freq = comm_node->getDoubleValue(); + // Note: 122.375 must be rounded DOWN to 12237 + // in order to be consistent with apt.dat et cetera. + int freqKhz = static_cast(comm_node->getDoubleValue() * 100.0 + 0.25); + _aircraftPos = SGGeod::fromDegFt(lon_node->getDoubleValue(), lat_node->getDoubleValue(), elev_node->getDoubleValue()); - // Query the data store and get the closest match if any - //cout << "Will FindByFreq: " << lat << " " << lon << " " << elev - // << " freq: " << freq << endl; - if(current_commlist->FindByFreq(_aircraftPos, freq, &data)) { - //cout << "FoundByFreq: " << freq - // << " ident: " << data.ident - // << " type: " << data.type << " ***" << endl; - // We are in range of something. + CommStation* sta = CommStation::findByFreq(freqKhz, _aircraftPos); + if (!sta) { + ZapOtherService(ncunit, "x x x"); + return; + } + + // Get rid of any *other* service that was on this radio unit: + FGPositioned::Type ty = sta->type(); + string svc_name = sta->ident() + FGPositioned::nameForType(ty); + ZapOtherService(ncunit, svc_name); + // See if the service already exists, possibly connected to + // some other radio unit: + if (atc_list->count(svc_name)) { + // make sure the service knows it's tuned on this radio: + FGATC* svc = (*atc_list)[svc_name]; + svc->active_on[ncunit] = 1; + svc->SetDisplay(); + return; + } - - // Get rid of any *other* service that was on this radio unit: - string svc_name = data.ident+decimalNumeral(data.type); - ZapOtherService(ncunit, svc_name); - // See if the service already exists, possibly connected to - // some other radio unit: - if (atc_list->count(svc_name)) { - // make sure the service knows it's tuned on this radio: - FGATC* svc = (*atc_list)[svc_name]; + // This was a switch-case statement but the compiler didn't like + // the new variable creation with it. + if(ty == FGPositioned::FREQ_ATIS || ty == FGPositioned::FREQ_AWOS) { + (*atc_list)[svc_name] = new FGATIS; + FGATC* svc = (*atc_list)[svc_name]; + if(svc != NULL) { + svc->SetStation(sta); svc->active_on[ncunit] = 1; svc->SetDisplay(); - return; + svc->Init(); } + } - CommRegisterAirport(data.ident, unit, data.type); - - // This was a switch-case statement but the compiler didn't like - // the new variable creation with it. - if(data.type == ATIS || data.type == AWOS) { - (*atc_list)[svc_name] = new FGATIS; - FGATC* svc = (*atc_list)[svc_name]; - if(svc != NULL) { - svc->SetData(&data); - svc->active_on[ncunit] = 1; - svc->SetDisplay(); - svc->Init(); - } - } - } else { - // No services in range. Zap any service on this unit. - ZapOtherService(ncunit, "x x x"); - } } diff --git a/src/ATCDCL/ATCmgr.hxx b/src/ATCDCL/ATCmgr.hxx index 850fcc0a9..179f65fc3 100644 --- a/src/ATCDCL/ATCmgr.hxx +++ b/src/ATCDCL/ATCmgr.hxx @@ -24,31 +24,12 @@ #include -#include
-#include - #include #include #include #include "ATC.hxx" -using std::string; -using std::list; -using std::map; - -// Structure for holding details of the ATC frequencies at a given airport, and whether they are in the active list or not. -// These can then be cross referenced with the commlists which are stored by frequency or bucket. -// Non-available services are denoted by a frequency of zero. -// These structures are only intended to be created for in-use airports, and removed when no longer needed. -struct AirportATC { - AirportATC(); - - SGGeod geod; - float atis_freq; - bool atis_active; -}; - class FGATCMgr : public SGSubsystem { @@ -56,16 +37,8 @@ private: bool initDone; // Hack - guard against update getting called before init - // A map of airport ID vs frequencies and ATC provision - typedef map < string, AirportATC* > airport_atc_map_type; - typedef airport_atc_map_type::iterator airport_atc_map_iterator; - typedef airport_atc_map_type::const_iterator airport_atc_map_const_iterator; - - airport_atc_map_type airport_atc_map; - airport_atc_map_iterator airport_atc_map_itr; - // A list of pointers to all currently active ATC classes - typedef map atc_list_type; + typedef std::map atc_list_type; typedef atc_list_type::iterator atc_list_iterator; typedef atc_list_type::const_iterator atc_list_const_iterator; @@ -107,9 +80,7 @@ public: void update(double dt); - // Returns true if the airport is found in the map - bool GetAirportATCDetails(const string& icao, AirportATC* a); - + // Return a pointer to an appropriate voice for a given type of ATC // creating the voice if necessary - ie. make sure exactly one copy // of every voice in use exists in memory. @@ -124,26 +95,20 @@ public: atc_type GetComm2ATCType() { return(INVALID); } FGATC* GetComm2ATCPointer() { return(0/* kludge */); } - // Get the frequency of a given service at a given airport - // Returns zero if not found - unsigned short int GetFrequency(const string& ident, const atc_type& tp); - - // Register the fact that the comm radio is tuned to an airport - bool CommRegisterAirport(const string& ident, int chan, const atc_type& tp); - + private: // Remove a class from the atc_list and delete it from memory // *if* no other comm channel or AI plane is using it. - void ZapOtherService(const string ncunit, const string svc_name); + void ZapOtherService(const std::string ncunit, const std::string svc_name); // Return a pointer to a class in the list given ICAO code and type // Return NULL if the given service is not in the list // - *** THE CALLING FUNCTION MUST CHECK FOR THIS *** - FGATC* FindInList(const string& id, const atc_type& tp); + FGATC* FindInList(const std::string& id, const atc_type& tp); // Search the specified radio for stations on the same frequency and in range. - void FreqSearch(const string navcomm, const int unit); + void FreqSearch(const std::string navcomm, const int unit); }; #endif // _FG_ATCMGR_HXX diff --git a/src/ATCDCL/CMakeLists.txt b/src/ATCDCL/CMakeLists.txt index 72c1da6a9..d9e8db69b 100644 --- a/src/ATCDCL/CMakeLists.txt +++ b/src/ATCDCL/CMakeLists.txt @@ -3,7 +3,6 @@ include(FlightGearComponent) set(SOURCES ATC.cxx atis.cxx - commlist.cxx ATCDialog.cxx ATCVoice.cxx ATCmgr.cxx diff --git a/src/ATCDCL/Makefile.am b/src/ATCDCL/Makefile.am index d268da1e5..8c2a2b74b 100644 --- a/src/ATCDCL/Makefile.am +++ b/src/ATCDCL/Makefile.am @@ -3,7 +3,6 @@ noinst_LIBRARIES = libATCDCL.a libATCDCL_a_SOURCES = \ ATC.hxx ATC.cxx \ atis.hxx atis.cxx \ - commlist.hxx commlist.cxx \ ATCDialog.hxx ATCDialog.cxx \ ATCVoice.hxx ATCVoice.cxx \ ATCmgr.hxx ATCmgr.cxx \ diff --git a/src/ATCDCL/atis.cxx b/src/ATCDCL/atis.cxx index 53973a7a7..b2f358620 100644 --- a/src/ATCDCL/atis.cxx +++ b/src/ATCDCL/atis.cxx @@ -34,6 +34,8 @@ #include "atis_lexicon.hxx" #include +#include +#include #include // atoi() #include // sprintf @@ -44,9 +46,6 @@ #include #include - -#include - #include #include #include @@ -56,7 +55,6 @@ #include -#include "commlist.hxx" #include "ATCutils.hxx" #include "ATCmgr.hxx" @@ -230,6 +228,47 @@ int Apt_US_CA(const string id) { return 0; } +// Add structure and map for storing a log of atis transmissions +// made in this session of FlightGear. This allows the callsign +// to be allocated correctly wrt time. +typedef struct { + double tstamp; + int sequence; +} atis_transmission_type; + +typedef std::map < std::string, atis_transmission_type > atis_log_type; +typedef atis_log_type::iterator atis_log_iterator; +typedef atis_log_type::const_iterator atis_log_const_iterator; + +static atis_log_type atislog; + +int FGATIS::GetAtisSequence( const string& apt_id, + const double tstamp, const int interval, const int special) +{ + atis_transmission_type tran; + + if(atislog.find(apt_id) == atislog.end()) { // New station + tran.tstamp = tstamp - interval; +// Random number between 0 and 25 inclusive, i.e. 26 equiprobable outcomes: + tran.sequence = int(sg_random() * LTRS); + atislog[apt_id] = tran; + //cout << "New ATIS station: " << apt_id << " seq-1: " + // << tran.sequence << endl; + } + +// calculate the appropriate identifier and update the log + tran = atislog[apt_id]; + + int delta = int((tstamp - tran.tstamp) / interval); + tran.tstamp += delta * interval; + if (special && !delta) delta++; // a "special" ATIS update is required + tran.sequence = (tran.sequence + delta) % LTRS; + atislog[apt_id] = tran; + //if (delta) cout << "New ATIS sequence: " << tran.sequence + // << " Delta: " << delta << endl; + return(tran.sequence + (delta ? 0 : LTRS*1000)); +} + // Generate the actual broadcast ATIS transmission. // Regen means regenerate the /current/ transmission. // Special means generate a new transmission, with a new sequence. @@ -245,8 +284,8 @@ int FGATIS::GenTransmission(const int regen, const int special) { int interval = _type == ATIS ? ATIS_interval // ATIS updated hourly : 2*minute; // AWOS updated more frequently - int sequence = current_commlist->GetAtisSequence(ident, - tstamp, interval, special); + + int sequence = GetAtisSequence(ident, tstamp, interval, special); if (!regen && sequence > LTRS) { //xx if (msg_OK) cout << "ATIS: no change: " << sequence << endl; //xx msg_time = cur_time; diff --git a/src/ATCDCL/atis.hxx b/src/ATCDCL/atis.hxx index 76809eb09..189b11106 100644 --- a/src/ATCDCL/atis.hxx +++ b/src/ATCDCL/atis.hxx @@ -95,6 +95,9 @@ class FGATIS : public FGATC { void TreeOut(int msgOK); friend std::istream& operator>> ( std::istream&, FGATIS& ); + + int GetAtisSequence( const std::string& apt_id, + const double tstamp, const int interval, const int special); }; typedef int (FGATIS::*int_getter)() const; diff --git a/src/ATCDCL/commlist.cxx b/src/ATCDCL/commlist.cxx deleted file mode 100644 index ef5c8d7b8..000000000 --- a/src/ATCDCL/commlist.cxx +++ /dev/null @@ -1,302 +0,0 @@ -// commlist.cxx -- comm frequency lookup class -// -// Written by David Luff and Alexander Kappes, started Jan 2003. -// Based on navlist.cxx by Curtis Olson, started April 2000. -// -// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt -// -// 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 -#endif - -#include "commlist.hxx" - -#include -#include -#include -#include -#include -#include -#include - -#include "ATCutils.hxx" - - -FGCommList *current_commlist; - - -// Constructor -FGCommList::FGCommList( void ) { - sg_srandom_time(); -} - - -// Destructor -FGCommList::~FGCommList( void ) { -} - - -// load the navaids and build the map -bool FGCommList::init( const SGPath& path ) { - - SGPath temp = path; - commlist_freq.erase(commlist_freq.begin(), commlist_freq.end()); - commlist_bck.erase(commlist_bck.begin(), commlist_bck.end()); - temp.append( "ATC/default.atis" ); - LoadComms(temp); - temp = path; - temp.append( "ATC/default.tower" ); - LoadComms(temp); - temp = path; - temp.append( "ATC/default.ground" ); - LoadComms(temp); - temp = path; - temp.append( "ATC/default.approach" ); - LoadComms(temp); - return true; -} - - -bool FGCommList::LoadComms(const SGPath& path) { - - sg_gzifstream fin( path.str() ); - if ( !fin.is_open() ) { - SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() ); - exit(-1); - } - - // read in each line of the file - fin >> skipcomment; - - while ( !fin.eof() ) { - ATCData a; - fin >> a; - if(a.type == INVALID) { - SG_LOG(SG_GENERAL, SG_DEBUG, "WARNING - INVALID type found in " << path.str() << '\n'); - } else { - // Push all stations onto frequency map - commlist_freq[a.freq].push_back(a); - - // Push non-atis stations onto bucket map as well - // In fact, push all stations onto bucket map for now so FGATCMgr::GetFrequency() works. - //if(a.type != ATIS and/or AWOS?) { - // get bucket number - SGBucket bucket(a.geod); - int bucknum = bucket.gen_index(); - commlist_bck[bucknum].push_back(a); - //} - } - - fin >> skipcomment; - } - - fin.close(); - return true; -} - - -// query the database for the specified frequency, lon and lat are in -// degrees, elev is in meters -// If no atc_type is specified, it returns true if any non-invalid type is found -// If atc_type is specifed, returns true only if the specified type is found -bool FGCommList::FindByFreq(const SGGeod& aPos, double freq, - ATCData* ad, atc_type tp ) -{ - comm_list_type stations; - stations = commlist_freq[kHz10(freq)]; - comm_list_iterator current = stations.begin(); - comm_list_iterator last = stations.end(); - - // double az1, az2, s; - SGVec3d aircraft = SGVec3d::fromGeod(aPos); - const double orig_max_d = 1e100; - double max_d = orig_max_d; - double d; - // TODO - at the moment this loop returns the first match found in range - // We want to return the closest match in the event of a frequency conflict - for ( ; current != last ; ++current ) { - d = distSqr(aircraft, current->cart); - - //cout << " dist = " << sqrt(d) - // << " range = " << current->range * SG_NM_TO_METER << endl; - - // TODO - match up to twice the published range so we can model - // reduced signal strength - // NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt. - if ( d < (current->range * SG_NM_TO_METER - * current->range * SG_NM_TO_METER ) ) { - //cout << "matched = " << current->ident << endl; - if((tp == INVALID) || (tp == (*current).type)) { - if(d < max_d) { - max_d = d; - *ad = *current; - } - } - } - } - - if(max_d < orig_max_d) { - return true; - } else { - return false; - } -} - -int FGCommList::FindByPos(const SGGeod& aPos, double range, comm_list_type* stations, atc_type tp) -{ - // number of relevant stations found within range - int found = 0; - stations->erase(stations->begin(), stations->end()); - - // get bucket number for plane position - SGBucket buck(aPos); - - // get neigboring buckets - int bx = (int)( range*SG_NM_TO_METER / buck.get_width_m() / 2) + 1; - int by = (int)( range*SG_NM_TO_METER / buck.get_height_m() / 2 ) + 1; - - // loop over bucket range - for ( int i=-bx; i<=bx; i++) { - for ( int j=-by; j<=by; j++) { - buck = sgBucketOffset(aPos.getLongitudeDeg(), aPos.getLatitudeDeg(), i, j); - long int bucket = buck.gen_index(); - comm_map_const_iterator Fstations = commlist_bck.find(bucket); - if (Fstations == commlist_bck.end()) continue; - comm_list_const_iterator current = Fstations->second.begin(); - comm_list_const_iterator last = Fstations->second.end(); - - - // double az1, az2, s; - SGVec3d aircraft = SGVec3d::fromGeod(aPos); - double d; - for(; current != last; ++current) { - if((current->type == tp) || (tp == INVALID)) { - d = distSqr(aircraft, current->cart); - // NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt. - if ( d < (current->range * SG_NM_TO_METER - * current->range * SG_NM_TO_METER ) ) { - stations->push_back(*current); - ++found; - } - } - } - } - } - return found; -} - - -// Returns the distance in meters to the closest station of a given type, -// with the details written into ATCData& ad. If no type is specifed simply -// returns the distance to the closest station of any type. -// Returns -9999 if no stations found within max_range in nautical miles (default 100 miles). -// Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if -// say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result -// and giving up after 1000. -double FGCommList::FindClosest(const SGGeod& aPos, ATCData& ad, atc_type tp, double max_range) { - int num_stations = 0; - int range = 10; - comm_list_type stations; - comm_list_iterator itr; - double distance = -9999.0; - - while(num_stations == 0) { - num_stations = FindByPos(aPos, range, &stations, tp); - if(num_stations) { - double closest = max_range * SG_NM_TO_METER; - double tmp; - for(itr = stations.begin(); itr != stations.end(); ++itr) { - ATCData ad2 = *itr; - const FGAirport *a = fgFindAirportID(ad2.ident); - if (a) { - tmp = dclGetHorizontalSeparation(ad2.geod, aPos); - if(tmp <= closest) { - closest = tmp; - distance = tmp; - ad = *itr; - } - } - } - //cout << "Closest station is " << ad.ident << " at a range of " << distance << " meters\n"; - return(distance); - } - if(range > max_range) { - break; - } - range *= 10; - } - return(-9999.0); -} - - -// Find by Airport code. -// This is basically a wrapper for a call to the airport database to get the airport -// position followed by a call to FindByPos(...) -bool FGCommList::FindByCode( const string& ICAO, ATCData& ad, atc_type tp ) { - const FGAirport *a = fgFindAirportID( ICAO); - if ( a) { - comm_list_type stations; - int found = FindByPos(a->geod(), 10.0, &stations, tp); - if(found) { - comm_list_iterator itr = stations.begin(); - while(itr != stations.end()) { - if(((*itr).ident == ICAO) && ((*itr).type == tp)) { - ad = *itr; - //cout << "FindByCode returns " << ICAO - // << " type: " << tp - // << " freq: " << itr->freq - // << endl; - return true; - } - ++itr; - } - } - } - return false; -} - -// TODO - this function should move somewhere else eventually! -// Return an appropriate sequence number for an ATIS transmission. -// Return sequence number + 2600 if sequence is unchanged since -// last time. -int FGCommList::GetAtisSequence( const string& apt_id, - const double tstamp, const int interval, const int special) -{ - atis_transmission_type tran; - - if(atislog.find(apt_id) == atislog.end()) { // New station - tran.tstamp = tstamp - interval; -// Random number between 0 and 25 inclusive, i.e. 26 equiprobable outcomes: - tran.sequence = int(sg_random() * LTRS); - atislog[apt_id] = tran; - //cout << "New ATIS station: " << apt_id << " seq-1: " - // << tran.sequence << endl; - } - -// calculate the appropriate identifier and update the log - tran = atislog[apt_id]; - - int delta = int((tstamp - tran.tstamp) / interval); - tran.tstamp += delta * interval; - if (special && !delta) delta++; // a "special" ATIS update is required - tran.sequence = (tran.sequence + delta) % LTRS; - atislog[apt_id] = tran; - //if (delta) cout << "New ATIS sequence: " << tran.sequence - // << " Delta: " << delta << endl; - return(tran.sequence + (delta ? 0 : LTRS*1000)); -} diff --git a/src/ATCDCL/commlist.hxx b/src/ATCDCL/commlist.hxx deleted file mode 100644 index c5066f3e9..000000000 --- a/src/ATCDCL/commlist.hxx +++ /dev/null @@ -1,136 +0,0 @@ -// commlist.hxx -- comm frequency lookup class -// -// Written by David Luff and Alexander Kappes, started Jan 2003. -// Based on navlist.hxx by Curtis Olson, started April 2000. -// -// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt -// -// 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. - -/***************************************************************** -* -* FGCommList is used to store communication frequency information -* for the ATC and AI subsystems. Two maps are maintained - one -* searchable by location and one searchable by frequency. The -* data structure returned from the search is the ATCData struct -* defined in ATC.hxx, containing location, frequency, name, range -* and type of the returned station. -* -******************************************************************/ - -#ifndef _FG_COMMLIST_HXX -#define _FG_COMMLIST_HXX - - -#include - -#include -#include -#include - -#include "ATC.hxx" -#include "atis.hxx" - -class SGPath; - -// A list of ATC stations -typedef std::list < ATCData > comm_list_type; -typedef comm_list_type::iterator comm_list_iterator; -typedef comm_list_type::const_iterator comm_list_const_iterator; - -// A map of ATC station lists -typedef std::map < int, comm_list_type > comm_map_type; -typedef comm_map_type::iterator comm_map_iterator; -typedef comm_map_type::const_iterator comm_map_const_iterator; - - -class FGCommList { - -public: - - FGCommList(); - ~FGCommList(); - - // load all comm frequencies and build the map - bool init( const SGPath& path ); - - // query the database for the specified frequency, lon and lat are - // If no atc_type is specified, it returns true if any non-invalid type is found. - // If atc_type is specifed, returns true only if the specified type is found. - // Returns the station closest to the supplied position. - // The data found is written into the passed-in ATCData structure. - bool FindByFreq(const SGGeod& aPos, double freq, ATCData* ad, atc_type tp = INVALID ); - - // query the database by location, lon and lat are in degrees, elev is in meters, range is in nautical miles. - // Returns the number of stations of the specified atc_type tp that are in range of the position defined by - // lon, lat and elev, and pushes them into stations. - // If no atc_type is specifed, returns the number of all stations in range, and pushes them into stations - // ** stations is erased before use ** - int FindByPos(const SGGeod& aPos, double range, comm_list_type* stations, atc_type tp = INVALID ); - - // Returns the distance in meters to the closest station of a given type, - // with the details written into ATCData& ad. If no type is specifed simply - // returns the distance to the closest station of any type. - // Returns -9999 if no stations found within max_range in nautical miles (default 100 miles). - // Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if - // say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result - // and giving up after 1000. - // !!!Be warned that searching anything over 100 miles will pause the sim unacceptably!!! - // (The ability to search longer ranges should be used during init only). - double FindClosest(const SGGeod& aPos, ATCData& ad, atc_type tp = INVALID, double max_range = 100.0 ); - - // Find by Airport code. - bool FindByCode( const std::string& ICAO, ATCData& ad, atc_type tp = INVALID ); - - // Return the sequence letter for an ATIS transmission given transmission time and airport id - // This maybe should get moved somewhere else!! - int GetAtisSequence( const std::string& apt_id, const double tstamp, - const int interval, const int flush=0); - - // Comm stations mapped by frequency - comm_map_type commlist_freq; - - // Comm stations mapped by bucket - comm_map_type commlist_bck; - - // Load comms from a specified path (which must include the filename) -private: - - bool LoadComms(const SGPath& path); - -//----------- This stuff is left over from atislist.[ch]xx and maybe should move somewhere else - // Add structure and map for storing a log of atis transmissions - // made in this session of FlightGear. This allows the callsign - // to be allocated correctly wrt time. - typedef struct { - double tstamp; - int sequence; - } atis_transmission_type; - - typedef std::map < std::string, atis_transmission_type > atis_log_type; - typedef atis_log_type::iterator atis_log_iterator; - typedef atis_log_type::const_iterator atis_log_const_iterator; - - atis_log_type atislog; -//----------------------------------------------------------------------------------------------- - -}; - - -extern FGCommList *current_commlist; - -#endif // _FG_COMMLIST_HXX - - diff --git a/src/Airports/apt_loader.cxx b/src/Airports/apt_loader.cxx index 8bb9924e6..5698eb16b 100644 --- a/src/Airports/apt_loader.cxx +++ b/src/Airports/apt_loader.cxx @@ -46,7 +46,8 @@ #include "simple.hxx" #include "runways.hxx" #include "pavement.hxx" -#include + +#include #include @@ -78,7 +79,7 @@ public: - void parseAPT(const string &aptdb_file, FGCommList *comm_list) + void parseAPT(const string &aptdb_file) { sg_gzifstream in( aptdb_file ); @@ -157,12 +158,8 @@ public: // custom startup locations (ignore) } else if ( line_id == 0 ) { // ?? - } else if ( line_id == 50 ) { - - parseATISLine(comm_list, simgear::strutils::split(line)); - - } else if ( line_id >= 51 && line_id <= 56 ) { - // other frequency entries (ignore) + } else if ( line_id >= 50 && line_id <= 56) { + parseCommLine(line_id, simgear::strutils::split(line)); } else if ( line_id == 110 ) { pavement = true; parsePavementLine850(simgear::strutils::split(line, 0, 4)); @@ -206,6 +203,7 @@ private: vector runways; vector taxiways; vector pavements; + vector commStations; void addAirport() { @@ -235,6 +233,7 @@ private: FGAirport* apt = new FGAirport(last_apt_id, pos, tower, last_apt_name, false, fptypeFromRobinType(atoi(last_apt_type.c_str()))); apt->setRunwaysAndTaxiways(runways, taxiways, pavements); + apt->setCommStations(commStations); } void parseAirportLine(const vector& token) @@ -466,48 +465,38 @@ private: } } - void parseATISLine(FGCommList *comm_list, const vector& token) + void parseCommLine(int lineId, const vector& token) { if ( rwy_count <= 0 ) { - SG_LOG( SG_GENERAL, SG_ALERT, - "No runways; skipping AWOS for " + last_apt_id); + SG_LOG( SG_GENERAL, SG_ALERT, "No runways; skipping comm for " + last_apt_id); } - -// This assumes/requires that any code-50 line (ATIS or AWOS) - // applies to the preceding code-1 line (airport ID and name) - // and that a full set of code-10 lines (runway descriptors) - // has come between the code-1 and code-50 lines. - // typical code-50 lines: - // 50 11770 ATIS - // 50 11770 AWOS 3 - // This code parallels code found in "operator>>" in ATC.hxx; - // FIXME: unify the code. - ATCData a; - a.geod = SGGeod::fromDegFt(rwy_lon_accum / (double)rwy_count, - rwy_lat_accum / (double)rwy_count, last_apt_elev); - a.range = 50; // give all ATISs small range - a.ident = last_apt_id; - a.name = last_apt_name; + + SGGeod pos = SGGeod::fromDegFt(rwy_lon_accum / (double)rwy_count, + rwy_lat_accum / (double)rwy_count, last_apt_elev); + // short int representing tens of kHz: - a.freq = atoi(token[1].c_str()); - if (token[2] == "ATIS") a.type = ATIS; - else a.type = AWOS; // ASOS same as AWOS + int freqKhz = atoi(token[1].c_str()); + int rangeNm = 50; + FGPositioned::Type ty; + switch (lineId) { + case 50: + ty = FGPositioned::FREQ_AWOS; + if (token[2] == "ATIS") { + ty = FGPositioned::FREQ_ATIS; + } + break; + + case 51: ty = FGPositioned::FREQ_UNICOM; break; + case 52: ty = FGPositioned::FREQ_CLEARANCE; break; + case 53: ty = FGPositioned::FREQ_GROUND; break; + case 54: ty = FGPositioned::FREQ_TOWER; break; + case 55: + case 56: ty = FGPositioned::FREQ_APP_DEP; break; + default: + throw sg_range_exception("unupported apt.dat comm station type"); + } - // generate cartesian coordinates - a.cart = SGVec3d::fromGeod(a.geod); - comm_list->commlist_freq[a.freq].push_back(a); - - SGBucket bucket(a.geod); - int bucknum = bucket.gen_index(); - comm_list->commlist_bck[bucknum].push_back(a); -#if 0 - SG_LOG( SG_GENERAL, SG_ALERT, - "Loaded ATIS/AWOS for airport: " << a.ident - << " lat: " << a.geod.getLatitudeDeg() - << " lon: " << a.geod.getLongitudeDeg() - << " freq: " << a.freq - << " type: " << a.type ); -#endif + commStations.push_back(new flightgear::CommStation(token[2], ty, pos, rangeNm, freqKhz)); } }; @@ -516,12 +505,11 @@ private: // Load the airport data base from the specified aptdb file. The // metar file is used to mark the airports as having metar available // or not. -bool fgAirportDBLoad( const string &aptdb_file, - FGCommList *comm_list, const std::string &metar_file ) +bool fgAirportDBLoad( const string &aptdb_file, const std::string &metar_file ) { APTLoader ld; - ld.parseAPT(aptdb_file, comm_list); + ld.parseAPT(aptdb_file); // // Load the metar.dat file and update apt db with stations that // have metar data. diff --git a/src/Airports/apt_loader.hxx b/src/Airports/apt_loader.hxx index 7e2405638..106268f54 100644 --- a/src/Airports/apt_loader.hxx +++ b/src/Airports/apt_loader.hxx @@ -29,14 +29,11 @@ #include -// forward decls -class FGCommList; - // Load the airport data base from the specified aptdb file. The // metar file is used to mark the airports as having metar available // or not. bool fgAirportDBLoad( const std::string &aptdb_file, - FGCommList *comm_list, const std::string &metar_file ); + const std::string &metar_file ); #endif // _FG_APT_LOADER_HXX diff --git a/src/Airports/runwaybase.cxx b/src/Airports/runwaybase.cxx index 01855347c..951f860e1 100644 --- a/src/Airports/runwaybase.cxx +++ b/src/Airports/runwaybase.cxx @@ -26,6 +26,7 @@ #endif #include +#include #include "runwaybase.hxx" diff --git a/src/Airports/runwaybase.hxx b/src/Airports/runwaybase.hxx index 745d4cb86..ca0a9fb6a 100644 --- a/src/Airports/runwaybase.hxx +++ b/src/Airports/runwaybase.hxx @@ -85,6 +85,7 @@ public: { return _surface_code; } protected: + double _heading; double _length; double _width; diff --git a/src/Airports/runways.cxx b/src/Airports/runways.cxx index c3e48ecbb..bd2df3038 100644 --- a/src/Airports/runways.cxx +++ b/src/Airports/runways.cxx @@ -39,6 +39,8 @@ #include #include +#include +#include using std::string; @@ -197,4 +199,9 @@ std::vector FGRunway::getSTARs() return result; } +flightgear::PositionedBinding* +FGRunway::createBinding(SGPropertyNode* nd) const +{ + return new flightgear::RunwayBinding(this, nd); +} diff --git a/src/Airports/runways.hxx b/src/Airports/runways.hxx index 1c5ab85b1..2cdc2e495 100644 --- a/src/Airports/runways.hxx +++ b/src/Airports/runways.hxx @@ -116,6 +116,8 @@ public: { return _reciprocal; } void setReciprocalRunway(FGRunway* other); + virtual flightgear::PositionedBinding* createBinding(SGPropertyNode* nd) const; + /** * Helper to process property data loaded from an ICAO.threshold.xml file */ @@ -130,6 +132,7 @@ public: * Get STARs associared with this runway */ std::vector getSTARs(); + }; #endif // _FG_RUNWAYS_HXX diff --git a/src/Airports/simple.cxx b/src/Airports/simple.cxx index a085fab57..f4f3d1adb 100644 --- a/src/Airports/simple.cxx +++ b/src/Airports/simple.cxx @@ -47,6 +47,8 @@ #include #include #include +#include +#include using std::vector; using namespace flightgear; @@ -662,6 +664,61 @@ Approach* FGAirport::getApproachByIndex(unsigned int aIndex) const return mApproaches[aIndex]; } +class AirportNodeListener : public SGPropertyChangeListener +{ +public: + AirportNodeListener() + { + SGPropertyNode* airports = fgGetNode("/sim/airport"); + airports->addChangeListener(this, false); + } + + virtual void valueChanged(SGPropertyNode*) + { + } + + virtual void childAdded(SGPropertyNode* pr, SGPropertyNode* child) + { + FGAirport* apt = FGAirport::findByIdent(child->getName()); + if (!apt) { + return; + } + + flightgear::PositionedBinding::bind(apt, child); + } +}; + +void FGAirport::installPropertyListener() +{ + new AirportNodeListener; +} + +flightgear::PositionedBinding* +FGAirport::createBinding(SGPropertyNode* nd) const +{ + return new flightgear::AirportBinding(this, nd); +} + +void FGAirport::setCommStations(CommStationList& comms) +{ + mCommStations.swap(comms); + for (unsigned int c=0; csetAirport(this); + } +} + +CommStationList +FGAirport::commStationsOfType(FGPositioned::Type aTy) const +{ + CommStationList result; + for (unsigned int c=0; ctype() == aTy) { + result.push_back(mCommStations[c]); + } + } + return result; +} + // get airport elevation double fgGetAirportElev( const string& id ) { diff --git a/src/Airports/simple.hxx b/src/Airports/simple.hxx index db5c738a4..03d66a190 100644 --- a/src/Airports/simple.hxx +++ b/src/Airports/simple.hxx @@ -50,10 +50,12 @@ namespace flightgear { class STAR; class Approach; class Waypt; - + class CommStation; typedef SGSharedPtr WayptRef; typedef std::vector WayptVec; + + typedef std::vector CommStationList; } @@ -185,6 +187,8 @@ public: unsigned int numApproaches() const; flightgear::Approach* getApproachByIndex(unsigned int aIndex) const; + static void installPropertyListener(); + /** * Syntactic wrapper around FGPositioned::findClosest - find the closest * match for filter, and return it cast to FGAirport. The default filter @@ -228,6 +232,14 @@ public: */ std::pair selectSTAR(const SGGeod& aOrigin, FGRunway* aRwy); + virtual flightgear::PositionedBinding* createBinding(SGPropertyNode* nd) const; + + void setCommStations(flightgear::CommStationList& comms); + + flightgear::CommStationList commStationsOfType(FGPositioned::Type aTy) const; + + const flightgear::CommStationList& commStations() const + { return mCommStations; } private: typedef std::vector::const_iterator Runway_iterator; /** @@ -275,6 +287,8 @@ private: std::vector mSIDs; std::vector mSTARs; std::vector mApproaches; + + flightgear::CommStationList mCommStations; }; // find basic airport location info from airport database diff --git a/src/Autopilot/route_mgr.cxx b/src/Autopilot/route_mgr.cxx index 1b37fa256..044c624c8 100644 --- a/src/Autopilot/route_mgr.cxx +++ b/src/Autopilot/route_mgr.cxx @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -109,6 +110,151 @@ PropertyWatcher* createWatcher(T* obj, void (T::*m)()) return new MethodPropertyWatcher(obj, m); } +static bool commandLoadFlightPlan(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + SGPath path(arg->getStringValue("path")); + return self->loadRoute(path); +} + +static bool commandSaveFlightPlan(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + SGPath path(arg->getStringValue("path")); + return self->saveRoute(path); +} + +static bool commandActivateFlightPlan(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + bool activate = arg->getBoolValue("activate", true); + if (activate) { + self->activate(); + } else { + + } + + return true; +} + +static bool commandClearFlightPlan(const SGPropertyNode*) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + self->clearRoute(); + return true; +} + +static bool commandSetActiveWaypt(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + int index = arg->getIntValue("index"); + if ((index < 0) || (index >= self->numWaypts())) { + return false; + } + + self->jumpToIndex(index); + return true; +} + +static bool commandInsertWaypt(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + int index = arg->getIntValue("index"); + std::string ident(arg->getStringValue("id")); + int alt = arg->getIntValue("altitude-ft", -999); + int ias = arg->getIntValue("speed-knots", -999); + + WayptRef wp; +// lat/lon may be supplied to narrow down navaid search, or to specify +// a raw waypoint + SGGeod pos; + if (arg->hasChild("longitude-deg")) { + pos = SGGeod::fromDeg(arg->getDoubleValue("longitude-deg"), + arg->getDoubleValue("latitude-deg")); + } + + if (arg->hasChild("navaid")) { + FGPositionedRef p = FGPositioned::findClosestWithIdent(arg->getStringValue("navaid"), pos); + + if (arg->hasChild("navaid", 1)) { + // intersection of two radials + FGPositionedRef p2 = FGPositioned::findClosestWithIdent(arg->getStringValue("navaid[1]"), pos); + if (!p2) { + SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << arg->getStringValue("navaid[1]")); + return false; + } + + double r1 = arg->getDoubleValue("radial"), + r2 = arg->getDoubleValue("radial[1]"); + + SGGeod intersection; + bool ok = SGGeodesy::radialIntersection(p->geod(), r1, p2->geod(), r2, intersection); + if (!ok) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "no valid intersection for:" << p->ident() + << "," << p2->ident()); + return false; + } + + std::string name = p->ident() + "-" + p2->ident(); + wp = new BasicWaypt(intersection, name, NULL); + } else if (arg->hasChild("offset-nm") && arg->hasChild("radial")) { + // offset radial from navaid + double radial = arg->getDoubleValue("radial"); + double distanceNm = arg->getDoubleValue("offset-nm"); + //radial += magvar->getDoubleValue(); // convert to true bearing + wp = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm); + } else { + wp = new NavaidWaypoint(p, NULL); + } + } else if (arg->hasChild("airport")) { + const FGAirport* apt = fgFindAirportID(arg->getStringValue("airport")); + if (!apt) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "no such airport" << arg->getStringValue("airport")); + return false; + } + + if (arg->hasChild("runway")) { + if (!apt->hasRunwayWithIdent(arg->getStringValue("runway"))) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "No runway: " << arg->getStringValue("runway") << " at " << apt->ident()); + return false; + } + + FGRunway* runway = apt->getRunwayByIdent(arg->getStringValue("runway")); + wp = new RunwayWaypt(runway, NULL); + } else { + wp = new NavaidWaypoint((FGAirport*) apt, NULL); + } + } else if (arg->hasChild("text")) { + wp = self->waypointFromString(arg->getStringValue("text")); + } else if (!(pos == SGGeod())) { + // just a raw lat/lon + wp = new BasicWaypt(pos, ident, NULL); + } else { + return false; // failed to build waypoint + } + + if (alt >= 0) { + wp->setAltitude(alt, flightgear::RESTRICT_AT); + } + + if (ias > 0) { + wp->setSpeed(ias, flightgear::RESTRICT_AT); + } + + self->insertWayptAtIndex(wp, index); + return true; +} + +static bool commandDeleteWaypt(const SGPropertyNode* arg) +{ + FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); + int index = arg->getIntValue("index"); + self->removeWayptAtIndex(index); + return true; +} + +///////////////////////////////////////////////////////////////////////////// + FGRouteMgr::FGRouteMgr() : _currentIndex(0), input(fgGetNode( RM "input", true )), @@ -117,6 +263,14 @@ FGRouteMgr::FGRouteMgr() : listener = new InputListener(this); input->setStringValue(""); input->addChangeListener(listener); + + SGCommandMgr::instance()->addCommand("load-flightplan", commandLoadFlightPlan); + SGCommandMgr::instance()->addCommand("save-flightplan", commandSaveFlightPlan); + SGCommandMgr::instance()->addCommand("activate-flightplan", commandActivateFlightPlan); + SGCommandMgr::instance()->addCommand("clear-flightplan", commandClearFlightPlan); + SGCommandMgr::instance()->addCommand("set-active-waypt", commandSetActiveWaypt); + SGCommandMgr::instance()->addCommand("insert-waypt", commandInsertWaypt); + SGCommandMgr::instance()->addCommand("delete-waypt", commandDeleteWaypt); } @@ -227,7 +381,7 @@ void FGRouteMgr::postinit() SGPath path(_pathNode->getStringValue()); if (path.exists()) { SG_LOG(SG_AUTOPILOT, SG_INFO, "loading flight-plan from:" << path.str()); - loadRoute(); + loadRoute(path); } // this code only matters for the --wp option now - perhaps the option @@ -897,11 +1051,11 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop) else if (!strcmp(s, "@ACTIVATE")) mgr->activate(); else if (!strcmp(s, "@LOAD")) { - mgr->loadRoute(); + SGPath path(mgr->_pathNode->getStringValue()); + mgr->loadRoute(path); } else if (!strcmp(s, "@SAVE")) { - mgr->saveRoute(); - } else if (!strcmp(s, "@POP")) { - SG_LOG(SG_AUTOPILOT, SG_WARN, "route-manager @POP command is deprecated"); + SGPath path(mgr->_pathNode->getStringValue()); + mgr->saveRoute(path); } else if (!strcmp(s, "@NEXT")) { mgr->jumpToIndex(mgr->_currentIndex + 1); } else if (!strcmp(s, "@PREVIOUS")) { @@ -1119,9 +1273,8 @@ Waypt* FGRouteMgr::wayptAtIndex(int index) const return _route[index]; } -void FGRouteMgr::saveRoute() +bool FGRouteMgr::saveRoute(const SGPath& path) { - SGPath path(_pathNode->getStringValue()); SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str()); try { SGPropertyNode_ptr d(new SGPropertyNode); @@ -1148,18 +1301,19 @@ void FGRouteMgr::saveRoute() wpt->saveAsNode(routeNode->getChild("wp", i, true)); } // of waypoint iteration writeProperties(path.str(), d, true /* write-all */); + return true; } catch (sg_exception& e) { SG_LOG(SG_IO, SG_WARN, "failed to save flight-plan:" << e.getMessage()); + return false; } } -void FGRouteMgr::loadRoute() +bool FGRouteMgr::loadRoute(const SGPath& path) { // deactivate route first active->setBoolValue(false); SGPropertyNode_ptr routeData(new SGPropertyNode); - SGPath path(_pathNode->getStringValue()); SG_LOG(SG_IO, SG_INFO, "going to read flight-plan from:" << path.str()); @@ -1167,8 +1321,7 @@ void FGRouteMgr::loadRoute() readProperties(path.str(), routeData); } catch (sg_exception& ) { // if XML parsing fails, the file might be simple textual list of waypoints - loadPlainTextRoute(path); - return; + return loadPlainTextRoute(path); } try { @@ -1180,9 +1333,11 @@ void FGRouteMgr::loadRoute() } else { throw sg_io_exception("unsupported XML route version"); } + return true; } catch (sg_exception& e) { SG_LOG(SG_IO, SG_WARN, "failed to load flight-plan (from '" << e.getOrigin() << "'):" << e.getMessage()); + return false; } } @@ -1299,11 +1454,11 @@ WayptRef FGRouteMgr::parseVersion1XMLWaypt(SGPropertyNode* aWP) return w; } -void FGRouteMgr::loadPlainTextRoute(const SGPath& path) +bool FGRouteMgr::loadPlainTextRoute(const SGPath& path) { sg_gzifstream in(path.str().c_str()); if (!in.is_open()) { - return; + return false; } try { @@ -1330,8 +1485,10 @@ void FGRouteMgr::loadPlainTextRoute(const SGPath& path) } // of line iteration _route = wpts; + return true; } catch (sg_exception& e) { SG_LOG(SG_IO, SG_WARN, "failed to load route from:" << path.str() << ":" << e.getMessage()); + return false; } } diff --git a/src/Autopilot/route_mgr.hxx b/src/Autopilot/route_mgr.hxx index 16b4adc4b..31336f96a 100644 --- a/src/Autopilot/route_mgr.hxx +++ b/src/Autopilot/route_mgr.hxx @@ -118,13 +118,22 @@ public: */ void jumpToIndex(int index); - void saveRoute(); - void loadRoute(); + bool saveRoute(const SGPath& p); + bool loadRoute(const SGPath& p); /** * Helper command to setup current airport/runway if necessary */ void initAtPosition(); + + /** + * Create a WayPoint from a string in the following format: + * - simple identifier + * - decimal-lon,decimal-lat + * - airport-id/runway-id + * - navaid/radial-deg/offset-nm + */ + flightgear::WayptRef waypointFromString(const std::string& target); private: flightgear::WayptVec _route; int _currentIndex; @@ -188,17 +197,7 @@ private: SGPropertyNode_ptr weightOnWheels; InputListener *listener; - SGPropertyNode_ptr mirror; - - /** - * Create a SGWayPoint from a string in the following format: - * - simple identifier - * - decimal-lon,decimal-lat - * - airport-id/runway-id - * - navaid/radial-deg/offset-nm - */ - flightgear::WayptRef waypointFromString(const std::string& target); - + SGPropertyNode_ptr mirror; void departureChanged(); void buildDeparture(flightgear::WayptRef enroute, flightgear::WayptVec& wps); @@ -229,7 +228,7 @@ private: bool checkFinished(); - void loadPlainTextRoute(const SGPath& path); + bool loadPlainTextRoute(const SGPath& path); void loadVersion1XMLRoute(SGPropertyNode_ptr routeData); void loadVersion2XMLRoute(SGPropertyNode_ptr routeData); diff --git a/src/Environment/fgclouds.cxx b/src/Environment/fgclouds.cxx index 9b689d443..6cc4d49ef 100644 --- a/src/Environment/fgclouds.cxx +++ b/src/Environment/fgclouds.cxx @@ -161,7 +161,7 @@ double FGClouds::buildCloud(SGPropertyNode *cloud_def_root, SGPropertyNode *box_ y = w * (y - 0.5) + pos[1]; // E/W z = h * z + pos[2]; // Up/Down. pos[2] is the cloudbase - SGVec3f newpos = SGVec3f(x, y, z); + //SGVec3f newpos = SGVec3f(x, y, z); SGNewCloud cld = SGNewCloud(texture_root, cld_def); //layer->addCloud(newpos, cld.genCloud()); diff --git a/src/Environment/gravity.cxx b/src/Environment/gravity.cxx index cfeec7b04..cd32feeb4 100644 --- a/src/Environment/gravity.cxx +++ b/src/Environment/gravity.cxx @@ -42,7 +42,6 @@ Somigliana::Somigliana() Somigliana::~Somigliana() { } -#include double Somigliana::getGravity( const SGGeod & position ) const { diff --git a/src/Environment/metarproperties.cxx b/src/Environment/metarproperties.cxx index 171d716af..11971f08c 100644 --- a/src/Environment/metarproperties.cxx +++ b/src/Environment/metarproperties.cxx @@ -301,9 +301,6 @@ void MetarProperties::set_metar( const char * metar ) } } - vector cv = m->getClouds(); - vector::const_iterator cloud, cloud_end = cv.end(); - { static const char * LAYER = "layer"; SGPropertyNode_ptr cloudsNode = _rootNode->getNode("clouds", true ); diff --git a/src/Environment/terrainsampler.cxx b/src/Environment/terrainsampler.cxx index 0d08af84e..293eb2c86 100644 --- a/src/Environment/terrainsampler.cxx +++ b/src/Environment/terrainsampler.cxx @@ -285,6 +285,7 @@ void AreaSampler::analyse() } } +/* double alt_low_min = 0.0; double n_max = 0.0; sum = 0.0; @@ -296,7 +297,7 @@ void AreaSampler::analyse() break; } } - +*/ _altLayered = 0.5 * (_altMin + _altOffset); #if 0 diff --git a/src/FDM/YASim/Turbulence.cpp b/src/FDM/YASim/Turbulence.cpp index 4a6183680..1b4048d1e 100644 --- a/src/FDM/YASim/Turbulence.cpp +++ b/src/FDM/YASim/Turbulence.cpp @@ -270,12 +270,10 @@ float Turbulence::iturb(unsigned int x, unsigned int y) xfrac = xfrac*xfrac*(3 - 2*xfrac); // ... as cubics yfrac = yfrac*yfrac*(3 - 2*yfrac); -#define WRAP(a) (a) >= wrapmax ? 0 : (a) - float p00 = lattice(WRAP(xl), WRAP(yl)); // lattice values - float p01 = lattice(WRAP(xl), WRAP(yl+1)); - float p10 = lattice(WRAP(xl+1), WRAP(yl)); - float p11 = lattice(WRAP(xl+1), WRAP(yl+1)); -#undef WRAP + float p00 = lattice(xl, yl); // lattice values + float p01 = lattice(xl, yl+1); + float p10 = lattice(xl+1, yl); + float p11 = lattice(xl+1, yl+1); float p0 = p00 * (1-yfrac) + p01 * yfrac; float p1 = p10 * (1-yfrac) + p11 * yfrac; diff --git a/src/Instrumentation/KLN89/kln89_page_apt.cxx b/src/Instrumentation/KLN89/kln89_page_apt.cxx index be9a4d721..95b34cfe6 100644 --- a/src/Instrumentation/KLN89/kln89_page_apt.cxx +++ b/src/Instrumentation/KLN89/kln89_page_apt.cxx @@ -27,58 +27,14 @@ #include "kln89_page_apt.hxx" +#include #include -#include +#include #include
#include #include -// This function is copied from Airports/runways.cxx -// TODO - Make the original properly available and remove this instance!!!! -// Return reverse rwy number -// eg 01 -> 19 -// 03L -> 21R -static string GetReverseRunwayNo(string rwyno) { - // cout << "Original rwyno = " << rwyNo << '\n'; - - // standardize input number - string tmp = rwyno.substr(1, 1); - if (( tmp == "L" || tmp == "R" || tmp == "C" ) || (rwyno.size() == 1)) { - tmp = rwyno; - rwyno = "0" + tmp; - SG_LOG( SG_GENERAL, SG_INFO, - "Standardising rwy number from " << tmp << " to " << rwyno ); - } - - char buf[4]; - int rn = atoi(rwyno.substr(0,2).c_str()); - rn += 18; - while(rn > 36) { - rn -= 36; - } - sprintf(buf, "%02i", rn); - if(rwyno.size() == 3) { - if(rwyno.substr(2,1) == "L") { - buf[2] = 'R'; - buf[3] = '\0'; - } else if (rwyno.substr(2,1) == "R") { - buf[2] = 'L'; - buf[3] = '\0'; - } else if (rwyno.substr(2,1) == "C") { - buf[2] = 'C'; - buf[3] = '\0'; - } else if (rwyno.substr(2,1) == "T") { - buf[2] = 'T'; - buf[3] = '\0'; - } else { - SG_LOG(SG_GENERAL, SG_ALERT, "Unknown runway code " - << rwyno << " passed to GetReverseRunwayNo(...)"); - } - } - return(buf); -} - KLN89AptPage::KLN89AptPage(KLN89* parent) : KLN89Page(parent) { _nSubPages = 8; @@ -230,7 +186,8 @@ void KLN89AptPage::Update(double dt) { string s = _aptRwys[i]->ident(); _kln89->DrawText(s, 2, 9, 3); _kln89->DrawText("/", 2, 12, 3); - _kln89->DrawText(GetReverseRunwayNo(s), 2, 13, 3); + string recipIdent = _aptRwys[i]->reciprocalRunway()->ident(); + _kln89->DrawText(recipIdent, 2, 13, 3); // Length s = GPSitoa(int(float(_aptRwys[i]->lengthFt()) * (_kln89->_altUnits == GPS_ALT_UNITS_FT ? 1.0 : SG_FEET_TO_METER) + 0.5)); _kln89->DrawText(s, 2, 5 - s.size(), 2); @@ -278,7 +235,8 @@ void KLN89AptPage::Update(double dt) { string s = _aptRwys[i]->ident(); _kln89->DrawText(s, 2, 9, 1); _kln89->DrawText("/", 2, 12, 1); - _kln89->DrawText(GetReverseRunwayNo(s), 2, 13, 1); + string recip = _aptRwys[i]->reciprocalRunway()->ident(); + _kln89->DrawText(recip, 2, 13, 1); // Length s = GPSitoa(int(float(_aptRwys[i]->lengthFt()) * (_kln89->_altUnits == GPS_ALT_UNITS_FT ? 1.0 : SG_FEET_TO_METER) + 0.5)); _kln89->DrawText(s, 2, 5 - s.size(), 0); @@ -499,38 +457,35 @@ void KLN89AptPage::SetId(const string& s) { // Update the cached airport details void KLN89AptPage::UpdateAirport(const string& id) { // Frequencies - _aptFreqs.clear(); - ATCData ad; - AptFreq aq; - //cout << "UpdateAirport called, id = " << id << '\n'; - // TODO - the logic below only returns one service per type per airport - they can be on more than one freq though. - if(current_commlist->FindByCode(id, ad, ATIS)) { - //cout << "Found ATIS\n"; - aq.service = "ATIS*"; - aq.freq = ad.freq; - _aptFreqs.push_back(aq); - } - if(current_commlist->FindByCode(id, ad, GROUND)) { - aq.service = "GRND*"; - aq.freq = ad.freq; - _aptFreqs.push_back(aq); - } - if(current_commlist->FindByCode(id, ad, TOWER)) { - aq.service = "TWR *"; - aq.freq = ad.freq; - _aptFreqs.push_back(aq); - } - if(current_commlist->FindByCode(id, ad, APPROACH)) { - aq.service = "APR"; - aq.freq = ad.freq; - _aptFreqs.push_back(aq); - } + _aptFreqs.clear(); + + const FGAirport* apt = fgFindAirportID(id); + if (!apt) { + throw sg_exception("UpdateAirport: unknown airport id " + id); + } + + for (unsigned int c=0; ccommStations().size(); ++c) { + flightgear::CommStation* comm = apt->commStations()[c]; + AptFreq aq; + aq.freq = comm->freqKHz(); + switch (comm->type()) { + case FGPositioned::FREQ_ATIS: + aq.service = "ATIS*"; break; + case FGPositioned::FREQ_GROUND: + aq.service = "GRND*"; break; + case FGPositioned::FREQ_TOWER: + aq.service = "TWR *"; break; + case FGPositioned::FREQ_APP_DEP: + aq.service = "APR *"; break; + default: + continue; + } + } + _nFreqPages = (unsigned int)ceil((float(_aptFreqs.size())) / 3.0f); // Runways _aptRwys.clear(); - const FGAirport* apt = fgFindAirportID(id); - assert(apt); // build local array, longest runway first for (unsigned int r=0; rnumRunways(); ++r) { diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index bccabc3c9..d57338318 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -75,8 +75,6 @@ #include #include -#include -#include #include #include @@ -1085,15 +1083,10 @@ fgInitNav () SGPath p_metar( globals->get_fg_root() ); p_metar.append( "Airports/metar.dat" ); -// Initialise the frequency search map BEFORE reading -// the airport database: - - - - current_commlist = new FGCommList; - current_commlist->init( globals->get_fg_root() ); - fgAirportDBLoad( aptdb.str(), current_commlist, p_metar.str() ); - + fgAirportDBLoad( aptdb.str(), p_metar.str() ); + FGAirport::installPropertyListener(); + FGPositioned::installCommands(); + FGNavList *navlist = new FGNavList; FGNavList *loclist = new FGNavList; FGNavList *gslist = new FGNavList; @@ -1435,7 +1428,7 @@ bool fgInitSubsystems() { //////////////////////////////////////////////////////////////////// // Initialise the ATIS Subsystem //////////////////////////////////////////////////////////////////// - globals->add_subsystem("atis", new FGAtisManager, SGSubsystemMgr::POST_FDM); + //globals->add_subsystem("atis", new FGAtisManager, SGSubsystemMgr::POST_FDM); //////////////////////////////////////////////////////////////////// diff --git a/src/Navaids/CMakeLists.txt b/src/Navaids/CMakeLists.txt index cf8ce4ad7..8f2747270 100644 --- a/src/Navaids/CMakeLists.txt +++ b/src/Navaids/CMakeLists.txt @@ -13,6 +13,7 @@ set(SOURCES route.cxx routePath.cxx waypoint.cxx + PositionedBinding.cxx ) flightgear_component(Navaids "${SOURCES}") \ No newline at end of file diff --git a/src/Navaids/PositionedBinding.cxx b/src/Navaids/PositionedBinding.cxx new file mode 100644 index 000000000..c1ecdf0aa --- /dev/null +++ b/src/Navaids/PositionedBinding.cxx @@ -0,0 +1,173 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "PositionedBinding.hxx" + +#include + +#include +#include +#include + +#include
+#include +#include +#include +#include + +typedef std::map BindingMap; +static BindingMap static_bindings; + +namespace { + +class PropertyDeleteObserver : public SGPropertyChangeListener +{ +public: + virtual void childRemoved(SGPropertyNode*, SGPropertyNode* node) + { + BindingMap::iterator it = static_bindings.find(node); + if (it != static_bindings.end()) { + SG_LOG(SG_GENERAL, SG_INFO, "saw remove of:" << node->getPath() << ", deleting binding"); + delete it->second; + static_bindings.erase(it); + } + } +}; + +static PropertyDeleteObserver* static_deleteObserver = NULL; + +} // of anonymous namespace + +namespace flightgear +{ + +PositionedBinding::PositionedBinding(const FGPositioned* pos, SGPropertyNode* nd) : + p(const_cast(pos)), + tied(nd) +{ + if (!static_deleteObserver) { + static_deleteObserver = new PropertyDeleteObserver; + globals->get_props()->addChangeListener(static_deleteObserver, false); + } + + nd->setDoubleValue("latitude-deg", p->latitude()); + nd->setDoubleValue("longitude-deg", p->longitude()); + + if (p->elevation() > -1000) { + nd->setDoubleValue("elevation-ft", p->elevation()); + } + + nd->setStringValue("ident", p->ident()); + if (!p->name().empty()) { + nd->setStringValue("name", p->name()); + } + + nd->setStringValue("type", FGPositioned::nameForType(p->type())); +} + +PositionedBinding::~PositionedBinding() +{ + tied.Untie(); +} + +void PositionedBinding::bind(FGPositioned* pos, SGPropertyNode* node) +{ + BindingMap::iterator it = static_bindings.find(node); + if (it != static_bindings.end()) { + throw sg_exception("duplicate positioned binding", node->getPath()); + } + + PositionedBinding* binding = pos->createBinding(node); + static_bindings.insert(it, std::make_pair(node, binding)); +} + +NavaidBinding::NavaidBinding(const FGNavRecord* nav, SGPropertyNode* nd) : + PositionedBinding(nav, nd) +{ + FGPositioned::Type ty = nav->type(); + if (ty == FGPositioned::NDB) { + nd->setDoubleValue("frequency-khz", nav->get_freq() / 100.0); + } else { + nd->setDoubleValue("frequency-mhz", nav->get_freq() / 100.0); + } + + if ((ty == FGPositioned::LOC) || (ty == FGPositioned::ILS)) { + nd->setDoubleValue("loc-course-deg", nav->get_multiuse()); + } + + if (ty == FGPositioned::GS) { + nd->setDoubleValue("gs-angle-deg", nav->get_multiuse()); + } + + nd->setDoubleValue("range-nm", nav->get_range()); + + if (nav->runway()) { + // don't want to create a cycle in the graph, so we don't re-bind + // the airport/runway node here - just expose the IDs + nd->setStringValue("airport", nav->runway()->airport()->ident()); + nd->setStringValue("runway", nav->runway()->ident()); + } +}; + +RunwayBinding::RunwayBinding(const FGRunway* rwy, SGPropertyNode* nd) : + PositionedBinding(rwy, nd) +{ + nd->setDoubleValue("length-ft", rwy->lengthFt()); + nd->setDoubleValue("length-m", rwy->lengthM()); + nd->setDoubleValue("width-ft", rwy->widthFt()); + nd->setDoubleValue("width-m", rwy->widthM()); + nd->setDoubleValue("heading-deg", rwy->headingDeg()); + nd->setBoolValue("hard-surface", rwy->isHardSurface()); + + nd->setDoubleValue("threshold-displacement-m", rwy->displacedThresholdM()); + nd->setDoubleValue("stopway-m", rwy->stopwayM()); + + if (rwy->ILS()) { + SGPropertyNode* ilsNode = nd->getChild("ils", 0, true); + PositionedBinding::bind(rwy->ILS(), ilsNode); + } +} + +AirportBinding::AirportBinding(const FGAirport* apt, SGPropertyNode* nd) : + PositionedBinding(apt, nd) +{ + nd->setIntValue("num-runways", apt->numRunways()); + + SGGeod tower = apt->getTowerLocation(); + nd->setDoubleValue("tower/latitude-deg", tower.getLatitudeDeg()); + nd->setDoubleValue("tower/longitude-deg", tower.getLongitudeDeg()); + nd->setDoubleValue("tower/elevation-ft", tower.getElevationFt()); + + for (unsigned int r=0; rnumRunways(); ++r) { + SGPropertyNode* rn = nd->getChild("runway", r, true); + FGRunway* rwy = apt->getRunwayByIndex(r); + PositionedBinding::bind(rwy, rn); + } + + for (unsigned int c=0; ccommStations().size(); ++c) { + flightgear::CommStation* comm = apt->commStations()[c]; + std::string tynm = FGPositioned::nameForType(comm->type()); + int count = nd->getChildren(tynm).size(); + + SGPropertyNode* commNode = nd->getChild(tynm, count, true); + commNode->setStringValue("ident", comm->ident()); + commNode->setDoubleValue("frequency-mhz", comm->freqMHz()); + } +} + +CommStationBinding::CommStationBinding(const CommStation* sta, SGPropertyNode* node) : + PositionedBinding(sta, node) +{ + node->setIntValue("range-nm", sta->rangeNm()); + node->setDoubleValue("frequency-mhz", sta->freqMHz()); + + if (sta->airport()) { + // don't want to create a cycle in the graph, so we don't re-bind + // the airport/runway node here - just expose the IDs + node->setStringValue("airport", sta->airport()->ident()); + } +} + +} // of namespace flightgear + diff --git a/src/Navaids/PositionedBinding.hxx b/src/Navaids/PositionedBinding.hxx new file mode 100644 index 000000000..ee4df7d19 --- /dev/null +++ b/src/Navaids/PositionedBinding.hxx @@ -0,0 +1,63 @@ +#ifndef FG_POSITIONED_BINDING_HXX +#define FG_POSITIONED_BINDING_HXX + +#include + +#include "positioned.hxx" + +// forward decls +class FGNavRecord; +class FGRunway; +class FGAirport; + +namespace flightgear +{ + +// forward decls +class CommStation; + +class PositionedBinding +{ +public: + virtual ~PositionedBinding(); + + static void bind(FGPositioned* pos, SGPropertyNode* node); + + + PositionedBinding(const FGPositioned* pos, SGPropertyNode* node); + +protected: + FGPositionedRef p; // bindings own a reference to their positioned + simgear::TiedPropertyList tied; + +private: + +}; + +class NavaidBinding : public PositionedBinding +{ +public: + NavaidBinding(const FGNavRecord* nav, SGPropertyNode* node); +}; + +class RunwayBinding : public PositionedBinding +{ +public: + RunwayBinding(const FGRunway* rwy, SGPropertyNode* node); +}; + +class AirportBinding : public PositionedBinding +{ +public: + AirportBinding(const FGAirport* apt, SGPropertyNode* node); +}; + +class CommStationBinding : public PositionedBinding +{ +public: + CommStationBinding(const CommStation* sta, SGPropertyNode* node); +}; + +} // of namespace flightgear + +#endif // of FG_POSITIONED_BINDING_HXX diff --git a/src/Navaids/navrecord.cxx b/src/Navaids/navrecord.cxx index 03317a57d..5edc64ef1 100644 --- a/src/Navaids/navrecord.cxx +++ b/src/Navaids/navrecord.cxx @@ -41,6 +41,7 @@ #include #include
+#include FGNavRecord::FGNavRecord(Type aTy, const std::string& aIdent, const std::string& aName, const SGGeod& aPos, @@ -187,6 +188,13 @@ double FGNavRecord::localizerWidth() const } +flightgear::PositionedBinding* +FGNavRecord::createBinding(SGPropertyNode* nd) const +{ + return new flightgear::NavaidBinding(this, nd); +} + + FGTACANRecord::FGTACANRecord(void) : channel(""), freq(0) diff --git a/src/Navaids/navrecord.hxx b/src/Navaids/navrecord.hxx index 09abb6e97..f58d5a167 100644 --- a/src/Navaids/navrecord.hxx +++ b/src/Navaids/navrecord.hxx @@ -89,12 +89,17 @@ public: */ FGRunway* runway() const { return mRunway; } + virtual flightgear::PositionedBinding* createBinding(SGPropertyNode* nd) const; + /** * return the localizer width, in degrees * computation is based up ICAO stdandard width at the runway threshold * see implementation for further details. */ double localizerWidth() const; + + void bindToNode(SGPropertyNode* nd) const; + void unbindFromNode(SGPropertyNode* nd) const; }; class FGTACANRecord : public SGReferenced { diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index b74e4e877..045839da3 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -35,11 +35,16 @@ #include // for osg::isNaN #include +#include #include #include #include +#include +#include - +#include "PositionedBinding.hxx" +#include "Airports/simple.hxx" +#include "Main/fg_props.hxx" typedef std::multimap NamedPositionedIndex; typedef std::pair NamedIndexRange; @@ -628,7 +633,15 @@ FGPositioned::Type FGPositioned::typeFromName(const std::string& aName) {"fix", FIX}, {"tacan", TACAN}, {"dme", DME}, + {"atis", FREQ_ATIS}, + {"awos", FREQ_AWOS}, + {"tower", FREQ_TOWER}, + {"ground", FREQ_GROUND}, + {"approach", FREQ_APP_DEP}, + {"departure", FREQ_APP_DEP}, // aliases + {"gnd", FREQ_GROUND}, + {"twr", FREQ_TOWER}, {"waypoint", WAYPOINT}, {"apt", AIRPORT}, {"arpt", AIRPORT}, @@ -672,11 +685,24 @@ const char* FGPositioned::nameForType(Type aTy) case WAYPOINT: return "waypoint"; case DME: return "dme"; case TACAN: return "tacan"; + case FREQ_TOWER: return "tower"; + case FREQ_ATIS: return "atis"; + case FREQ_AWOS: return "awos"; + case FREQ_GROUND: return "ground"; + case FREQ_CLEARANCE: return "clearance"; + case FREQ_UNICOM: return "unicom"; + case FREQ_APP_DEP: return "approach-departure"; default: return "unknown"; } } +flightgear::PositionedBinding* +FGPositioned::createBinding(SGPropertyNode* node) const +{ + return new flightgear::PositionedBinding(this, node); +} + /////////////////////////////////////////////////////////////////////////////// // search / query functions @@ -811,3 +837,145 @@ FGPositioned::sortByRange(List& aResult, const SGGeod& aPos) aResult[i] = r[i].get(); } } + +FGPositioned::Filter* createSearchFilter(const SGPropertyNode* arg) +{ + string sty(arg->getStringValue("type", 0)); + FGPositioned::Type ty = FGPositioned::typeFromName(sty); + double minRunwayLenFt = arg->getDoubleValue("min-runway-length-ft", -1.0); + + if ((ty == FGPositioned::AIRPORT) && (minRunwayLenFt > 0.0)) { + return new FGAirport::HardSurfaceFilter(minRunwayLenFt); + } else if (ty != FGPositioned::INVALID) { + FGPositioned::TypeFilter* tf = new FGPositioned::TypeFilter(ty); + + for (int t=1; arg->hasChild("type", t); ++t) { + sty = arg->getChild("type", t)->getStringValue(); + tf->addType(FGPositioned::typeFromName(sty)); + } + + return tf; + } + + return NULL; +} + +SGGeod commandSearchPos(const SGPropertyNode* arg) +{ + if (arg->hasChild("longitude-deg") && arg->hasChild("latitude-deg")) { + return SGGeod::fromDeg(arg->getDoubleValue("longitude-deg"), + arg->getDoubleValue("latitude-deg")); + } + + // use current viewer/aircraft position + return SGGeod::fromDeg(fgGetDouble("/position/longitude-deg"), + fgGetDouble("/position/latitude-deg")); +} + +void commandClearExisting(const SGPropertyNode* arg) +{ + if (arg->getBoolValue("clear", true)) { + // delete all existing result children from their parent + string resultPath = arg->getStringValue("results"); + SGPropertyNode* n = fgGetNode(resultPath.c_str(), 0, true); + SGPropertyNode* pr = n->getParent(); + pr->removeChildren(n->getName(), false /* keep=false, i.e delete nodes */); + } +} + +bool commandFindClosest(const SGPropertyNode* arg) +{ + int n = arg->getIntValue("max-results", 1); + if ((n < 1) || (n > 100)) { + SG_LOG(SG_GENERAL, SG_WARN, "commandFindClosest: max-results invalid:" << n); + return false; + } + + string resultPath = arg->getStringValue("results"); + if (resultPath.empty()) { + SG_LOG(SG_GENERAL, SG_WARN, "commandFindClosest: no results path defined"); + return false; + } + + std::auto_ptr filt(createSearchFilter(arg)); +// cap search range, since huge ranges will overload everything + double cutoff = arg->getDoubleValue("cutoff-nm", 400.0); + SG_CLAMP_RANGE(cutoff, 0.0, 1000.0); + + SGGeod pos = commandSearchPos(arg); + commandClearExisting(arg); + + FGPositioned::List results = FGPositioned::findClosestN(pos, n, cutoff, filt.get()); + for (unsigned int i=0; igetStringValue("results"); + if (resultPath.empty()) { + SG_LOG(SG_GENERAL, SG_WARN, "commandFindByIdent: no results path defined"); + return false; + } + + std::auto_ptr filt(createSearchFilter(arg)); + SGGeod pos = commandSearchPos(arg); + commandClearExisting(arg); + + FGPositioned::List results; + bool exact = arg->getBoolValue("exact", true); + if (arg->hasChild("name")) { + results = FGPositioned::findAllWithName(arg->getStringValue("name"), filt.get(), exact); + } else if (arg->hasChild("ident")) { + results = FGPositioned::findAllWithName(arg->getStringValue("ident"), filt.get(), exact); + } else { + SG_LOG(SG_GENERAL, SG_WARN, "commandFindByIdent: no search term defined"); + return false; + } + + bool orderByRange = arg->getBoolValue("order-by-distance", true); + if (orderByRange) { + FGPositioned::sortByRange(results, pos); + } + + for (unsigned int i=0; iaddCommand("find-nearest", commandFindClosest); + SGCommandMgr::instance()->addCommand("find-by-ident", commandFindByIdent); +} + +FGPositioned::TypeFilter::TypeFilter(Type aTy) +{ + types.push_back(aTy); +} + +void FGPositioned::TypeFilter::addType(Type aTy) +{ + types.push_back(aTy); +} + +bool +FGPositioned::TypeFilter::pass(FGPositioned* aPos) const +{ + std::vector::const_iterator it = types.begin(), + end = types.end(); + for (; it != end; ++it) { + return aPos->type() == *it; + } + + return false; +} + diff --git a/src/Navaids/positioned.hxx b/src/Navaids/positioned.hxx index f6f61229a..de5a31322 100644 --- a/src/Navaids/positioned.hxx +++ b/src/Navaids/positioned.hxx @@ -27,9 +27,15 @@ #include class FGPositioned; +class SGPropertyNode; typedef SGSharedPtr FGPositionedRef; +namespace flightgear +{ + class PositionedBinding; +} + class FGPositioned : public SGReferenced { public: @@ -56,9 +62,14 @@ public: DME, TACAN, OBSTACLE, - FREQ_GND, - FREQ_TWR, + FREQ_GROUND, + FREQ_TOWER, FREQ_ATIS, + FREQ_AWOS, + FREQ_APP_DEP, + FREQ_ENROUTE, + FREQ_CLEARANCE, + FREQ_UNICOM, LAST_TYPE } Type; @@ -97,6 +108,9 @@ public: double elevation() const { return mPosition.getElevationFt(); } + + virtual flightgear::PositionedBinding* createBinding(SGPropertyNode* nd) const; + /** * Predicate class to support custom filtering of FGPositioned queries * Default implementation of this passes any FGPositioned instance. @@ -135,13 +149,15 @@ public: class TypeFilter : public Filter { public: - TypeFilter(Type aTy) : mType(aTy) { ; } - virtual bool pass(FGPositioned* aPos) const - { return (mType == aPos->type()); } + TypeFilter(Type aTy); + virtual bool pass(FGPositioned* aPos) const; + void addType(Type aTy); private: - const Type mType; + std::vector types; }; + static void installCommands(); + static List findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilter = NULL); static FGPositionedRef findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter = NULL);