From dd6cccdda168318cd2a286986a7f08a6d3c5e339 Mon Sep 17 00:00:00 2001 From: Torsten Dreyer Date: Wed, 19 Feb 2014 14:24:34 +0100 Subject: [PATCH] A new comm radio and atis implementation --- src/ATC/ATISEncoder.cxx | 473 ++++++++++++++++++ src/ATC/ATISEncoder.hxx | 111 ++++ src/ATC/CMakeLists.txt | 4 + ...MetarPropertiesATISInformationProvider.cxx | 120 +++++ ...MetarPropertiesATISInformationProvider.hxx | 75 +++ src/ATCDCL/CMakeLists.txt | 12 - src/Airports/airport.cxx | 22 +- src/Airports/airport.hxx | 17 +- src/Airports/dynamics.cxx | 14 +- src/Airports/runways.cxx | 6 +- src/Airports/runways.hxx | 2 +- src/Environment/CMakeLists.txt | 4 +- src/Instrumentation/CMakeLists.txt | 2 + src/Instrumentation/commradio.cxx | 384 ++++++++++++++ src/Instrumentation/commradio.hxx | 49 ++ src/Instrumentation/frequencyformatter.hxx | 47 ++ src/Instrumentation/instrument_mgr.cxx | 4 + src/Instrumentation/newnavradio.cxx | 45 +- src/Main/fg_init.cxx | 13 - src/Main/globals.cxx | 3 - src/Main/globals.hxx | 7 - src/Main/locale.cxx | 3 + src/Main/subsystemFactory.cxx | 2 - 23 files changed, 1319 insertions(+), 100 deletions(-) create mode 100644 src/ATC/ATISEncoder.cxx create mode 100644 src/ATC/ATISEncoder.hxx create mode 100644 src/ATC/MetarPropertiesATISInformationProvider.cxx create mode 100644 src/ATC/MetarPropertiesATISInformationProvider.hxx create mode 100644 src/Instrumentation/commradio.cxx create mode 100644 src/Instrumentation/commradio.hxx create mode 100644 src/Instrumentation/frequencyformatter.hxx diff --git a/src/ATC/ATISEncoder.cxx b/src/ATC/ATISEncoder.cxx new file mode 100644 index 000000000..5edc4b232 --- /dev/null +++ b/src/ATC/ATISEncoder.cxx @@ -0,0 +1,473 @@ +/* +Encode an ATIS into spoken words +Copyright (C) 2014 Torsten Dreyer + +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 "ATISEncoder.hxx" +#include +#include
+#include
+#include +#include +#include + +#include +#include + +using std::string; +using std::vector; +using simgear::PropertyList; + +static string NO_ATIS("nil"); +static string EMPTY(""); +#define SPACE append(1,' ') + +const char * ATCSpeech::getSpokenDigit( int i ) +{ + string key = "n" + boost::lexical_cast( i ); + return globals->get_locale()->getLocalizedString(key.c_str(), "atc", "" ); +} + +string ATCSpeech::getSpokenNumber( string number ) +{ + string result; + for( string::iterator it = number.begin(); it != number.end(); ++it ) { + result.append( getSpokenDigit( (*it) - '0' )).SPACE; + } + return result; +} + +string ATCSpeech::getSpokenNumber( int number, bool leadingZero, int digits ) +{ + vector spokenDigits; + int n = 0; + while( number > 0 ) { + spokenDigits.push_back( getSpokenDigit(number%10) ); + number /= 10; + n++; + } + + if( digits > 0 ) { + while( n++ < digits ) { + spokenDigits.push_back( getSpokenDigit(0) ); + } + } + + string result; + while( false == spokenDigits.empty() ) { + if( false == spokenDigits.empty() ) + result.SPACE; + + result.append( spokenDigits.back() ); + spokenDigits.pop_back(); + } + + return result; +} + +string ATCSpeech::getSpokenAltitude( int altitude ) +{ + string result; + int thousands = altitude / 1000; + int hundrets = (altitude % 1000) / 100; + + if( thousands > 0 ) { + result.append( getSpokenNumber(thousands) ); + result.SPACE; + result.append( getSpokenDigit(1000) ); + result.SPACE; + } + if( hundrets > 0 ) + result.append( getSpokenNumber(hundrets) ) + .SPACE + .append( getSpokenDigit(100) ); + + return result; +} + +ATISEncoder::ATISEncoder() +{ + handlerMap.insert( std::make_pair( "text", &ATISEncoder::processTextToken )); + handlerMap.insert( std::make_pair( "token", &ATISEncoder::processTokenToken )); + handlerMap.insert( std::make_pair( "if", &ATISEncoder::processIfToken )); + + handlerMap.insert( std::make_pair( "id", &ATISEncoder::getAtisId )); + handlerMap.insert( std::make_pair( "airport-name", &ATISEncoder::getAirportName )); + handlerMap.insert( std::make_pair( "time", &ATISEncoder::getTime )); + handlerMap.insert( std::make_pair( "approach-type", &ATISEncoder::getApproachType )); + handlerMap.insert( std::make_pair( "rwy-land", &ATISEncoder::getLandingRunway )); + handlerMap.insert( std::make_pair( "rwy-to", &ATISEncoder::getTakeoffRunway )); + handlerMap.insert( std::make_pair( "transition-level", &ATISEncoder::getTransitionLevel )); + handlerMap.insert( std::make_pair( "wind-dir", &ATISEncoder::getWindDirection )); + handlerMap.insert( std::make_pair( "wind-speed-kn", &ATISEncoder::getWindspeedKnots )); + handlerMap.insert( std::make_pair( "gusts", &ATISEncoder::getGustsKnots )); + handlerMap.insert( std::make_pair( "visibility-metric", &ATISEncoder::getVisibilityMetric )); + handlerMap.insert( std::make_pair( "phenomena", &ATISEncoder::getPhenomena )); + handlerMap.insert( std::make_pair( "clouds", &ATISEncoder::getClouds )); + handlerMap.insert( std::make_pair( "cavok", &ATISEncoder::getCavok )); + handlerMap.insert( std::make_pair( "temperature-deg", &ATISEncoder::getTemperatureDeg )); + handlerMap.insert( std::make_pair( "dewpoint-deg", &ATISEncoder::getDewpointDeg )); + handlerMap.insert( std::make_pair( "qnh", &ATISEncoder::getQnh )); + handlerMap.insert( std::make_pair( "inhg", &ATISEncoder::getInhg )); + handlerMap.insert( std::make_pair( "trend", &ATISEncoder::getTrend )); +} + +ATISEncoder::~ATISEncoder() +{ +} + +SGPropertyNode_ptr findAtisTemplate( const std::string & stationId, SGPropertyNode_ptr atisSchemaNode ) +{ + using simgear::strutils::starts_with; + SGPropertyNode_ptr atisTemplate; + + PropertyList schemaNodes = atisSchemaNode->getChildren("atis-schema"); + for( PropertyList::iterator asit = schemaNodes.begin(); asit != schemaNodes.end(); ++asit ) { + SGPropertyNode_ptr ppp = (*asit)->getNode("station-starts-with", false ); + atisTemplate = (*asit)->getNode("atis", false ); + if( false == atisTemplate.valid() ) continue; // no node - ignore entry + + PropertyList startsWithNodes = (*asit)->getChildren("station-starts-with"); + for( PropertyList::iterator swit = startsWithNodes.begin(); swit != startsWithNodes.end(); ++swit ) { + + if( starts_with( stationId, (*swit)->getStringValue() ) ) { + return atisTemplate; + } + } + + } + + return atisTemplate; +} + +string ATISEncoder::encodeATIS( ATISInformationProvider * atisInformation ) +{ + using simgear::strutils::lowercase; + + if( false == atisInformation->isValid() ) return NO_ATIS; + + airport = FGAirport::getByIdent( atisInformation->airportId() ); + if( false == airport.valid() ) { + SG_LOG( SG_ATC, SG_WARN, "ATISEncoder: unknown airport id " << atisInformation->airportId() ); + return NO_ATIS; + } + + _atis = atisInformation; + + // lazily load the schema file on the first call + if( false == atisSchemaNode.valid() ) { + atisSchemaNode = new SGPropertyNode(); + try + { + SGPath path = globals->resolve_maybe_aircraft_path("ATC/atis.xml"); + readProperties( path.str(), atisSchemaNode ); + } + catch (const sg_exception& e) + { + SG_LOG( SG_ATC, SG_ALERT, "ATISEncoder: Failed to load atis schema definition: " << e.getMessage()); + return NO_ATIS; + } + } + + string stationId = lowercase( airport->ident() ); + + SGPropertyNode_ptr atisTemplate = findAtisTemplate( stationId, atisSchemaNode );; + if( false == atisTemplate.valid() ) { + SG_LOG(SG_ATC, SG_WARN, "no matching atis template for station " << stationId ); + return NO_ATIS; // no template for this station!? + } + + return processTokens( atisTemplate ); +} + +string ATISEncoder::processTokens( SGPropertyNode_ptr node ) +{ + string result; + if( node.valid() ) { + for( int i = 0; i < node->nChildren(); i++ ) { + result.append(processToken( node->getChild(i) )); + } + } + return result; +} + +string ATISEncoder::processToken( SGPropertyNode_ptr token ) +{ + HandlerMap::iterator it = handlerMap.find( token->getName()); + if( it == handlerMap.end() ) { + SG_LOG(SG_ATC, SG_WARN, "ATISEncoder: unknown token: " << token->getName() ); + return EMPTY; + } + handler_t h = it->second; + return (this->*h)( token ); +} + +string ATISEncoder::processTextToken( SGPropertyNode_ptr token ) +{ + return token->getStringValue(); +} + +string ATISEncoder::processTokenToken( SGPropertyNode_ptr token ) +{ + HandlerMap::iterator it = handlerMap.find( token->getStringValue()); + if( it == handlerMap.end() ) { + SG_LOG(SG_ATC, SG_WARN, "ATISEncoder: unknown token: " << token->getStringValue() ); + return EMPTY; + } + handler_t h = it->second; + return (this->*h)( token ); + + token->getStringValue(); +} + +string ATISEncoder::processIfToken( SGPropertyNode_ptr token ) +{ + SGPropertyNode_ptr n; + + if( (n = token->getChild("empty", false )).valid() ) { + return checkEmptyCondition( n, true) ? + processTokens(token->getChild("then",false)) : + processTokens(token->getChild("else",false)); + } + + if( (n = token->getChild("not-empty", false )).valid() ) { + return checkEmptyCondition( n, false) ? + processTokens(token->getChild("then",false)) : + processTokens(token->getChild("else",false)); + } + + if( (n = token->getChild("equals", false )).valid() ) { + return checkEqualsCondition( n, true) ? + processTokens(token->getChild("then",false)) : + processTokens(token->getChild("else",false)); + } + + if( (n = token->getChild("not-equals", false )).valid() ) { + return checkEqualsCondition( n, false) ? + processTokens(token->getChild("then",false)) : + processTokens(token->getChild("else",false)); + } + + SG_LOG(SG_ATC, SG_WARN, "ATISEncoder: no valid token found for element" ); + + return EMPTY; +} + +bool ATISEncoder::checkEmptyCondition( SGPropertyNode_ptr node, bool isEmpty ) +{ + SGPropertyNode_ptr n1 = node->getNode( "token", false ); + if( false == n1.valid() ) { + SG_LOG(SG_ATC, SG_WARN, "missing node for (not)-empty" ); + return false; + } + return processToken( n1 ).empty() == isEmpty; +} + +bool ATISEncoder::checkEqualsCondition( SGPropertyNode_ptr node, bool isEqual ) +{ + SGPropertyNode_ptr n1 = node->getNode( "token", 0, false ); + SGPropertyNode_ptr n2 = node->getNode( "token", 1, false ); + if( false == n1.valid() || false == n2.valid()) { + SG_LOG(SG_ATC, SG_WARN, "missing node for (not)-equals" ); + return false; + } + + bool comp = processToken( n1 ).compare( processToken( n2 ) ) == 0; + return comp == isEqual; +} + +string ATISEncoder::getAtisId( SGPropertyNode_ptr ) +{ + FGAirportDynamics * dynamics = airport->getDynamics(); + if( NULL != dynamics ) { + dynamics->updateAtisSequence( 30*60, false ); + return dynamics->getAtisSequence(); + } + return EMPTY; +} + +string ATISEncoder::getAirportName( SGPropertyNode_ptr ) +{ + return airport->getName(); +} + +string ATISEncoder::getTime( SGPropertyNode_ptr ) +{ + return getSpokenNumber( _atis->getTime() % (100*100), true, 4 ); +} + +static inline FGRunwayRef findBestRunwayForWind( FGAirportRef airport, int windDeg, int windKt ) +{ + struct FGAirport::FindBestRunwayForHeadingParams p; + //TODO: ramp down the heading weight with wind speed + p.ilsWeight = 4; + return airport->findBestRunwayForHeading( windDeg, &p ); +} + +string ATISEncoder::getApproachType( SGPropertyNode_ptr ) +{ + FGRunwayRef runway = findBestRunwayForWind( airport, _atis->getWindDeg(), _atis->getWindSpeedKt() ); + if( runway.valid() ) { + if( NULL != runway->ILS() ) return globals->get_locale()->getLocalizedString("ils", "atc", "ils" ); + //TODO: any chance to find other approach types? localizer-dme, vor-dme, vor, ndb? + } + + return globals->get_locale()->getLocalizedString("visual", "atc", "visual" ); +} + +string ATISEncoder::getLandingRunway( SGPropertyNode_ptr ) +{ + FGRunwayRef runway = findBestRunwayForWind( airport, _atis->getWindDeg(), _atis->getWindSpeedKt() ); + if( runway.valid() ) { + string runwayIdent = runway->ident(); + if(runwayIdent != "NN") { + return getSpokenNumber(runwayIdent); + } + } + return EMPTY; +} + +string ATISEncoder::getTakeoffRunway( SGPropertyNode_ptr p ) +{ + //TODO: if the airport has more than one runway, probably pick another one? + return getLandingRunway( p ); +} + +string ATISEncoder::getTransitionLevel( SGPropertyNode_ptr ) +{ + double hPa = _atis->getQnh(); + + /* Transition level is the flight level above which aircraft must use standard pressure and below + * which airport pressure settings must be used. + * Following definitions are taken from German ATIS: + * QNH <= 977 hPa: TRL 80 + * QNH <= 1013 hPa: TRL 70 + * QNH > 1013 hPa: TRL 60 + * (maybe differs slightly for other countries...) + */ + int tl; + if (hPa <= 978) { + tl = 80; + } else if( hPa > 978 && hPa <= 1013 ) { + tl = 70; + } else if( hPa > 1013 && hPa <= 1046 ) { + tl = 60; + } else { + tl = 50; + } + + // add an offset to the transition level for high altitude airports (just guessing here, + // seems reasonable) + int e = int(airport->getElevation() / 1000.0); + if (e >= 3) { + // TL steps in 10(00)ft + tl += (e-2)*10; + } + + return getSpokenNumber(tl); +} + +string ATISEncoder::getWindDirection( SGPropertyNode_ptr ) +{ + return getSpokenNumber( _atis->getWindDeg() ); +} + +string ATISEncoder::getWindspeedKnots( SGPropertyNode_ptr ) +{ + return getSpokenNumber( _atis->getWindSpeedKt() ); +} + +string ATISEncoder::getGustsKnots( SGPropertyNode_ptr ) +{ + int g = _atis->getGustsKt(); + return g > 0 ? getSpokenNumber( g ) : EMPTY; +} + +string ATISEncoder::getCavok( SGPropertyNode_ptr ) +{ + string CAVOK = globals->get_locale()->getLocalizedString("cavok", "atc", "cavok" ); + + return _atis->isCavok() ? CAVOK : EMPTY; +} + +string ATISEncoder::getVisibilityMetric( SGPropertyNode_ptr ) +{ + string m = globals->get_locale()->getLocalizedString("meters", "atc", "meters" ); + string km = globals->get_locale()->getLocalizedString("kilometersmeters", "atc", "kilometersmeters" ); + string or_more = globals->get_locale()->getLocalizedString("ormore", "atc", "or more" ); + + int v = _atis->getVisibilityMeters(); + string reply; + if( v < 5000 ) return reply.append( getSpokenAltitude( v ) ).SPACE.append( m ); + if( v >= 10000 ) return reply.append( getSpokenNumber(10) ).SPACE.append( km ).SPACE.append(or_more); + return reply.append( getSpokenNumber( v/1000 ).append( km ) ); +} + +string ATISEncoder::getPhenomena( SGPropertyNode_ptr ) +{ + return _atis->getPhenomena(); +} + +string ATISEncoder::getClouds( SGPropertyNode_ptr ) +{ + string FEET = globals->get_locale()->getLocalizedString("feet", "atc", "feet" ); + string reply; + + ATISInformationProvider::CloudEntries cloudEntries = _atis->getClouds(); + + for( ATISInformationProvider::CloudEntries::iterator it = cloudEntries.begin(); it != cloudEntries.end(); it++ ) { + if( false == reply.empty() ) reply.SPACE; + reply.append( it->second ).SPACE.append( getSpokenAltitude(it->first).SPACE.append( FEET ) ); + } + return reply; +} + +string ATISEncoder::getTemperatureDeg( SGPropertyNode_ptr ) +{ + return getSpokenNumber( _atis->getTemperatureDeg() ); +} + +string ATISEncoder::getDewpointDeg( SGPropertyNode_ptr ) +{ + return getSpokenNumber( _atis->getDewpointDeg() ); +} + +string ATISEncoder::getQnh( SGPropertyNode_ptr ) +{ + return getSpokenNumber( _atis->getQnh() ); +} + +string ATISEncoder::getInhg( SGPropertyNode_ptr ) +{ + string DECIMAL = globals->get_locale()->getLocalizedString("dp", "atc", "decimal" ); + double intpart = .0; + int fractpart = 1000 * ::modf( _atis->getQnh() * 100.0 / SG_INHG_TO_PA, &intpart ); + fractpart += 5; + fractpart /= 10; + + string reply; + reply.append( getSpokenNumber( (int)intpart ) ) + .append( DECIMAL ).SPACE + .append( getSpokenNumber( fractpart ) ); + return reply; +} + +string ATISEncoder::getTrend( SGPropertyNode_ptr ) +{ + return _atis->getTrend(); +} + diff --git a/src/ATC/ATISEncoder.hxx b/src/ATC/ATISEncoder.hxx new file mode 100644 index 000000000..269692969 --- /dev/null +++ b/src/ATC/ATISEncoder.hxx @@ -0,0 +1,111 @@ +/* +Encode an ATIS into spoken words +Copyright (C) 2014 Torsten Dreyer + +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_ENCODER_HXX +#define __ATIS_ENCODER_HXX + +#include +#include +#include +#include + +class ATCSpeech { +public: + static const char * getSpokenDigit( int i ); + static std::string getSpokenNumber( std::string number ); + static std::string getSpokenNumber( int number, bool leadingZero = false, int digits = 1 ); + static std::string getSpokenAltitude( int altitude ); +}; + +class ATISInformationProvider { +public: + virtual ~ATISInformationProvider() {} + virtual bool isValid() = 0; + virtual std::string airportId() = 0; + + static long makeAtisTime( int day, int hour, int minute ) { + return 100*100l* day + 100l * hour + minute; + } + inline int getAtisTimeDay( long atisTime ) { return atisTime / (100l*100l); } + inline int getAtisTimeHour( long atisTime ) { return (atisTime % (100l*100l)) / 100l; } + inline int getAtisTimeMinute( long atisTime ) { return atisTime % 100l; } + virtual long getTime() = 0; // see makeAtisTime + + virtual int getWindDeg() = 0; + virtual int getWindSpeedKt() = 0; + virtual int getGustsKt() = 0; + virtual int getQnh() = 0; + virtual bool isCavok() = 0; + virtual int getVisibilityMeters() = 0; + virtual std::string getPhenomena() = 0; + + typedef std::map CloudEntries; + virtual CloudEntries getClouds() = 0; + virtual int getTemperatureDeg() = 0; + virtual int getDewpointDeg() = 0; + virtual std::string getTrend() = 0; +}; + +class ATISEncoder : public ATCSpeech { +public: + ATISEncoder(); + virtual ~ATISEncoder(); + virtual std::string encodeATIS( ATISInformationProvider * atisInformationProvider ); + +protected: + virtual std::string getAtisId( SGPropertyNode_ptr ); + virtual std::string getAirportName( SGPropertyNode_ptr ); + virtual std::string getTime( SGPropertyNode_ptr ); + virtual std::string getApproachType( SGPropertyNode_ptr ); + virtual std::string getLandingRunway( SGPropertyNode_ptr ); + virtual std::string getTakeoffRunway( SGPropertyNode_ptr ); + virtual std::string getTransitionLevel( SGPropertyNode_ptr ); + virtual std::string getWindDirection( SGPropertyNode_ptr ); + virtual std::string getWindspeedKnots( SGPropertyNode_ptr ); + virtual std::string getGustsKnots( SGPropertyNode_ptr ); + virtual std::string getCavok( SGPropertyNode_ptr ); + virtual std::string getVisibilityMetric( SGPropertyNode_ptr ); + virtual std::string getPhenomena( SGPropertyNode_ptr ); + virtual std::string getClouds( SGPropertyNode_ptr ); + virtual std::string getTemperatureDeg( SGPropertyNode_ptr ); + virtual std::string getDewpointDeg( SGPropertyNode_ptr ); + virtual std::string getQnh( SGPropertyNode_ptr ); + virtual std::string getInhg( SGPropertyNode_ptr ); + virtual std::string getTrend( SGPropertyNode_ptr ); + + typedef std::string (ATISEncoder::*handler_t)( SGPropertyNode_ptr baseNode ); + typedef std::map HandlerMap; + HandlerMap handlerMap; + + SGPropertyNode_ptr atisSchemaNode; + + std::string processTokens( SGPropertyNode_ptr baseNode ); + std::string processToken( SGPropertyNode_ptr baseNode ); + + std::string processTextToken( SGPropertyNode_ptr baseNode ); + std::string processTokenToken( SGPropertyNode_ptr baseNode ); + std::string processIfToken( SGPropertyNode_ptr baseNode ); + bool checkEmptyCondition( SGPropertyNode_ptr node, bool isEmpty ); + bool checkEqualsCondition( SGPropertyNode_ptr node, bool isEmpty ); + + FGAirportRef airport; + ATISInformationProvider * _atis; +}; + +#endif diff --git a/src/ATC/CMakeLists.txt b/src/ATC/CMakeLists.txt index f7dd1f2df..35b48884a 100644 --- a/src/ATC/CMakeLists.txt +++ b/src/ATC/CMakeLists.txt @@ -5,6 +5,8 @@ set(SOURCES atcdialog.cxx trafficcontrol.cxx CommStation.cxx + ATISEncoder.cxx + MetarPropertiesATISInformationProvider.cxx ) set(HEADERS @@ -12,6 +14,8 @@ set(HEADERS atcdialog.hxx trafficcontrol.hxx CommStation.hxx + ATISEncoder.hxx + MetarPropertiesATISInformationProvider.hxx ) flightgear_component(ATC "${SOURCES}" "${HEADERS}") diff --git a/src/ATC/MetarPropertiesATISInformationProvider.cxx b/src/ATC/MetarPropertiesATISInformationProvider.cxx new file mode 100644 index 000000000..4a68792fa --- /dev/null +++ b/src/ATC/MetarPropertiesATISInformationProvider.cxx @@ -0,0 +1,120 @@ +/* +Provide Data for the ATIS Encoder from metarproperties +Copyright (C) 2014 Torsten Dreyer + +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 "MetarPropertiesATISInformationProvider.hxx" +#include
+#include + +using std::string; + +static string EMPTY(""); +static string CAVOK("cavok"); +MetarPropertiesATISInformationProvider::MetarPropertiesATISInformationProvider( SGPropertyNode_ptr metar ) : + _metar( metar ) +{ +} + +MetarPropertiesATISInformationProvider::~MetarPropertiesATISInformationProvider() +{ +} + +bool MetarPropertiesATISInformationProvider::isValid() +{ + return _metar->getBoolValue( "valid", false ); +} + +string MetarPropertiesATISInformationProvider::airportId() +{ + return _metar->getStringValue( "station-id", "xxxx" ); +} + +long MetarPropertiesATISInformationProvider::getTime() +{ + return makeAtisTime( 0, + _metar->getIntValue( "hour" ) % 24, + _metar->getIntValue( "minute" ) % 60 ); +} + +int MetarPropertiesATISInformationProvider::getWindDeg() +{ + return _metar->getIntValue( "base-wind-dir-deg" ); +} + +int MetarPropertiesATISInformationProvider::getWindSpeedKt() +{ + return _metar->getIntValue( "base-wind-speed-kt" ); +} + +int MetarPropertiesATISInformationProvider::getGustsKt() +{ + return _metar->getIntValue( "gust-wind-speed-kt" ); +} + + +int MetarPropertiesATISInformationProvider::getQnh() +{ + return _metar->getDoubleValue("pressure-sea-level-inhg") * SG_INHG_TO_PA / 100.0; +} + +bool MetarPropertiesATISInformationProvider::isCavok() +{ + return _metar->getBoolValue( "cavok" ); +} + +int MetarPropertiesATISInformationProvider::getVisibilityMeters() +{ + return _metar->getIntValue( "min-visibility-m" ); +} + +string MetarPropertiesATISInformationProvider::getPhenomena() +{ + return _metar->getStringValue( "decoded" ); +} + +ATISInformationProvider::CloudEntries MetarPropertiesATISInformationProvider::getClouds() +{ + CloudEntries reply; + + using simgear::PropertyList; + PropertyList layers = _metar->getNode("clouds", true )->getChildren("layer"); + for( PropertyList::iterator it = layers.begin(); it != layers.end(); ++it ) { + const char * coverage = (*it)->getStringValue("coverage","clear"); + double elevation = (*it)->getDoubleValue("elevation-ft", -9999 ); + if( elevation > 0 ) { + reply[elevation] = coverage; + } + } + return reply; +} + +int MetarPropertiesATISInformationProvider::getTemperatureDeg() +{ + return _metar->getIntValue( "temperature-degc" ); +} + +int MetarPropertiesATISInformationProvider::getDewpointDeg() +{ + return _metar->getIntValue( "dewpoint-degc" ); +} + +string MetarPropertiesATISInformationProvider::getTrend() +{ + return "nosig"; +} + diff --git a/src/ATC/MetarPropertiesATISInformationProvider.hxx b/src/ATC/MetarPropertiesATISInformationProvider.hxx new file mode 100644 index 000000000..95b2b4deb --- /dev/null +++ b/src/ATC/MetarPropertiesATISInformationProvider.hxx @@ -0,0 +1,75 @@ +/* +Provide Data for the ATIS Encoder from metarproperties +Copyright (C) 2014 Torsten Dreyer + +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 __METARPROPERTIES_ATIS_ENCODER_HXX +#define __METARPROPERTIES_ATIS_ENCODER_HXX + +/* ATIS encoder from metarproperties */ + +#include + +#include +#include "ATISEncoder.hxx" + +class MetarPropertiesATISInformationProvider : public ATISInformationProvider +{ +public: + MetarPropertiesATISInformationProvider( SGPropertyNode_ptr metar ); + virtual ~MetarPropertiesATISInformationProvider(); + +protected: + virtual bool isValid(); + virtual std::string airportId(); + virtual long getTime(); + virtual int getWindDeg(); + virtual int getWindSpeedKt(); + virtual int getGustsKt(); + virtual int getQnh(); + virtual bool isCavok(); + virtual int getVisibilityMeters(); + virtual std::string getPhenomena(); + virtual CloudEntries getClouds(); + virtual int getTemperatureDeg(); + virtual int getDewpointDeg(); + virtual std::string getTrend(); +#if 0 + virtual std::string getStationId(); + virtual std::string getAtisId(); + virtual std::string getTime(); + virtual std::string getApproachType(); + virtual std::string getLandingRunway(); + virtual std::string getTakeoffRunway(); + virtual std::string getTransitionLevel(); + virtual std::string getWindDirection(); + virtual std::string getWindspeedKnots(); + virtual std::string getGustsKnots(); + virtual std::string getVisibilityMetric(); + virtual std::string getPhenomena(); + virtual std::string getClouds(); + virtual std::string getCavok(); + virtual std::string getTemperatureDeg(); + virtual std::string getDewpointDeg(); + virtual std::string getQnh(); + virtual std::string getTrend(); +#endif +private: + SGPropertyNode_ptr _metar; +}; + +#endif diff --git a/src/ATCDCL/CMakeLists.txt b/src/ATCDCL/CMakeLists.txt index fd1a3736b..98d253a97 100644 --- a/src/ATCDCL/CMakeLists.txt +++ b/src/ATCDCL/CMakeLists.txt @@ -1,23 +1,11 @@ include(FlightGearComponent) set(SOURCES - ATC.cxx - atis.cxx - ATCVoice.cxx - ATISmgr.cxx - ATCutils.cxx ATCProjection.cxx ) set(HEADERS - ATC.hxx - atis.hxx - ATCVoice.hxx - ATISmgr.hxx - ATCutils.hxx ATCProjection.hxx - atis_lexicon.hxx - atis_remap.hxx ) flightgear_component(ATCDCL "${SOURCES}" "${HEADERS}") diff --git a/src/Airports/airport.cxx b/src/Airports/airport.cxx index 1a1fa7b1a..5a511eb99 100644 --- a/src/Airports/airport.cxx +++ b/src/Airports/airport.cxx @@ -236,18 +236,24 @@ FGHelipadRef FGAirport::getHelipadByIdent(const std::string& aIdent) const } //------------------------------------------------------------------------------ -FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading) const +FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading, struct FindBestRunwayForHeadingParams * parms ) const { loadRunways(); FGRunway* result = NULL; double currentBestQuality = 0.0; - SGPropertyNode *param = fgGetNode("/sim/airport/runways/search", true); - double lengthWeight = param->getDoubleValue("length-weight", 0.01); - double widthWeight = param->getDoubleValue("width-weight", 0.01); - double surfaceWeight = param->getDoubleValue("surface-weight", 10); - double deviationWeight = param->getDoubleValue("deviation-weight", 1); + struct FindBestRunwayForHeadingParams fbrfhp; + if( NULL != parms ) fbrfhp = *parms; + + SGPropertyNode_ptr searchNode = fgGetNode("/sim/airport/runways/search"); + if( searchNode.valid() ) { + fbrfhp.lengthWeight = searchNode->getDoubleValue("length-weight", fbrfhp.lengthWeight ); + fbrfhp.widthWeight = searchNode->getDoubleValue("width-weight", fbrfhp.widthWeight ); + fbrfhp.surfaceWeight = searchNode->getDoubleValue("surface-weight", fbrfhp.surfaceWeight ); + fbrfhp.deviationWeight = searchNode->getDoubleValue("deviation-weight", fbrfhp.deviationWeight ); + fbrfhp.ilsWeight = searchNode->getDoubleValue("ils-weight", fbrfhp.ilsWeight ); + } BOOST_FOREACH(PositionedID id, mRunways) { FGRunway* rwy = loadById(id); @@ -257,10 +263,10 @@ FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading) const continue; } - double good = rwy->score(lengthWeight, widthWeight, surfaceWeight); + double good = rwy->score( fbrfhp.lengthWeight, fbrfhp.widthWeight, fbrfhp.surfaceWeight, fbrfhp.ilsWeight ); double dev = aHeading - rwy->headingDeg(); SG_NORMALIZE_RANGE(dev, -180.0, 180.0); - double bad = fabs(deviationWeight * dev) + 1e-20; + double bad = fabs( fbrfhp.deviationWeight * dev) + 1e-20; double quality = good / bad; if (quality > currentBestQuality) { diff --git a/src/Airports/airport.hxx b/src/Airports/airport.hxx index a35354075..830106980 100644 --- a/src/Airports/airport.hxx +++ b/src/Airports/airport.hxx @@ -94,7 +94,22 @@ class FGAirport : public FGPositioned bool hasHelipadWithIdent(const std::string& aIdent) const; FGRunwayRef getRunwayByIdent(const std::string& aIdent) const; FGHelipadRef getHelipadByIdent(const std::string& aIdent) const; - FGRunwayRef findBestRunwayForHeading(double aHeading) const; + + struct FindBestRunwayForHeadingParams { + FindBestRunwayForHeadingParams() { + lengthWeight = 0.01; + widthWeight = 0.01; + surfaceWeight = 10; + deviationWeight = 1; + ilsWeight = 0; + } + double lengthWeight; + double widthWeight; + double surfaceWeight; + double deviationWeight; + double ilsWeight; + }; + FGRunwayRef findBestRunwayForHeading(double aHeading, struct FindBestRunwayForHeadingParams * parms = NULL ) const; /** * return the most likely target runway based on a position. diff --git a/src/Airports/dynamics.cxx b/src/Airports/dynamics.cxx index a88a44149..5916c68b0 100644 --- a/src/Airports/dynamics.cxx +++ b/src/Airports/dynamics.cxx @@ -38,8 +38,8 @@ #include #include
#include
+#include
#include -#include #include #include "airport.hxx" @@ -530,8 +530,12 @@ const std::string FGAirportDynamics::getAtisSequence() if (atisSequenceIndex == -1) { updateAtisSequence(1, false); } + + char atisSequenceString[2]; + atisSequenceString[0] = 'a' + atisSequenceIndex; + atisSequenceString[1] = 0; - return GetPhoneticLetter(atisSequenceIndex); + return globals->get_locale()->getLocalizedString(atisSequenceString, "atc", "unknown"); } int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate) @@ -540,7 +544,7 @@ int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate) if (atisSequenceIndex == -1) { // first computation atisSequenceTimeStamp = now; - atisSequenceIndex = rand() % LTRS; // random initial sequence letters + atisSequenceIndex = rand() % 26; // random initial sequence letters return atisSequenceIndex; } @@ -550,7 +554,7 @@ int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate) ++steps; // a "special" ATIS update is required } - atisSequenceIndex = (atisSequenceIndex + steps) % LTRS; + atisSequenceIndex = (atisSequenceIndex + steps) % 26; // return a huge value if no update occurred - return (atisSequenceIndex + (steps ? 0 : LTRS*1000)); + return (atisSequenceIndex + (steps ? 0 : 26*1000)); } diff --git a/src/Airports/runways.cxx b/src/Airports/runways.cxx index 4dc866076..9f2291f7b 100644 --- a/src/Airports/runways.cxx +++ b/src/Airports/runways.cxx @@ -96,15 +96,17 @@ string FGRunway::reverseIdent(const string& aRunwayIdent) return buf; } -double FGRunway::score(double aLengthWt, double aWidthWt, double aSurfaceWt) const +double FGRunway::score(double aLengthWt, double aWidthWt, double aSurfaceWt, double aIlsWt) const { int surface = 1; if (_surface_code == 12 || _surface_code == 5) // dry lakebed & gravel surface = 2; else if (_surface_code == 1 || _surface_code == 2) // asphalt & concrete surface = 3; + + int ils = (_ils != 0); - return _length * aLengthWt + _width * aWidthWt + surface * aSurfaceWt + 1e-20; + return _length * aLengthWt + _width * aWidthWt + surface * aSurfaceWt + ils * aIlsWt + 1e-20; } SGGeod FGRunway::begin() const diff --git a/src/Airports/runways.hxx b/src/Airports/runways.hxx index ae4404753..e795d9ee5 100644 --- a/src/Airports/runways.hxx +++ b/src/Airports/runways.hxx @@ -58,7 +58,7 @@ public: * score this runway according to the specified weights. Used by * FGAirport::findBestRunwayForHeading */ - double score(double aLengthWt, double aWidthWt, double aSurfaceWt) const; + double score(double aLengthWt, double aWidthWt, double aSurfaceWt, double aIlsWt) const; /** * Get the runway beginning point - this is syntatic sugar, equivalent to diff --git a/src/Environment/CMakeLists.txt b/src/Environment/CMakeLists.txt index e75dde258..ccff9f7e0 100644 --- a/src/Environment/CMakeLists.txt +++ b/src/Environment/CMakeLists.txt @@ -17,7 +17,7 @@ set(SOURCES terrainsampler.cxx presets.cxx gravity.cxx - magvarmanager.cxx + magvarmanager.cxx ) set(HEADERS @@ -37,7 +37,7 @@ set(HEADERS terrainsampler.hxx presets.hxx gravity.hxx - magvarmanager.hxx + magvarmanager.hxx ) flightgear_component(Environment "${SOURCES}" "${HEADERS}") diff --git a/src/Instrumentation/CMakeLists.txt b/src/Instrumentation/CMakeLists.txt index a955a3cd3..ee3cd55fc 100644 --- a/src/Instrumentation/CMakeLists.txt +++ b/src/Instrumentation/CMakeLists.txt @@ -23,6 +23,7 @@ set(SOURCES mrg.cxx navradio.cxx newnavradio.cxx + commradio.cxx rad_alt.cxx rnav_waypt_controller.cxx slip_skid_ball.cxx @@ -83,6 +84,7 @@ set(HEADERS mrg.hxx navradio.hxx newnavradio.hxx + commradio.hxx rad_alt.hxx rnav_waypt_controller.hxx slip_skid_ball.hxx diff --git a/src/Instrumentation/commradio.cxx b/src/Instrumentation/commradio.cxx new file mode 100644 index 000000000..a16fe60b2 --- /dev/null +++ b/src/Instrumentation/commradio.cxx @@ -0,0 +1,384 @@ +// commradio.cxx -- class to manage a nav radio instance +// +// Written by Torsten Dreyer, February 2014 +// +// Copyright (C) 2000 - 2011 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 "commradio.hxx" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include
+#include + +#include "frequencyformatter.hxx" + +namespace Instrumentation { + +using simgear::PropertyObject; +using std::string; + + +SignalQualityComputer::~SignalQualityComputer() +{ +} + +class SimpleDistanceSquareSignalQualityComputer : public SignalQualityComputer +{ +public: + SimpleDistanceSquareSignalQualityComputer( double range ) : _rangeM(range), _rangeM2(range*range) {} + virtual double computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const; + virtual double computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const; + virtual double computeSignalQuality( double slantDistanceM ) const; +private: + double _rangeM; + double _rangeM2; +}; + +double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const +{ + return computeSignalQuality( dist( sender, receiver ) ); +} + +double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const +{ + return computeSignalQuality( SGGeodesy::distanceM( sender, receiver ) ); +} + +double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( double distanceM ) const +{ + return distanceM < _rangeM ? 1.0 : ( _rangeM2 / (distanceM*distanceM) ); +} + +class OnExitHandler { + public: + virtual void onExit() = 0; +}; + +class OnExit { + public: + OnExit( OnExitHandler * onExitHandler ) : _onExitHandler( onExitHandler ) {} + ~OnExit() { _onExitHandler->onExit(); } + private: + OnExitHandler * _onExitHandler; +}; + + +class OutputProperties : public OnExitHandler { + public: + OutputProperties( SGPropertyNode_ptr rootNode ) : + _rootNode(rootNode), + _signalQuality_norm(0.0), + _slantDistance_m(0.0), + _trueBearingTo_deg(0.0), + _trueBearingFrom_deg(0.0), + _trackDistance_m(0.0), + _heightAboveStation_ft(0.0), + + _PO_stationType( rootNode->getNode("station-type", true ) ), + _PO_stationName( rootNode->getNode("station-name", true ) ), + _PO_airportId( rootNode->getNode("airport-id", true ) ), + _PO_signalQuality_norm( rootNode->getNode("signal-quality-norm",true) ), + _PO_slantDistance_m( rootNode->getNode("slant-distance-m",true) ), + _PO_trueBearingTo_deg( rootNode->getNode("true-bearing-to-deg",true) ), + _PO_trueBearingFrom_deg( rootNode->getNode("true-bearing-from-deg",true) ), + _PO_trackDistance_m( rootNode->getNode("track-distance-m",true) ), + _PO_heightAboveStation_ft( rootNode->getNode("height-above-station-ft",true) ) + {} + +protected: + SGPropertyNode_ptr _rootNode; + + std::string _stationType; + std::string _stationName; + std::string _airportId; + double _signalQuality_norm; + double _slantDistance_m; + double _trueBearingTo_deg; + double _trueBearingFrom_deg; + double _trackDistance_m; + double _heightAboveStation_ft; + +private: + PropertyObject _PO_stationType; + PropertyObject _PO_stationName; + PropertyObject _PO_airportId; + PropertyObject _PO_signalQuality_norm; + PropertyObject _PO_slantDistance_m; + PropertyObject _PO_trueBearingTo_deg; + PropertyObject _PO_trueBearingFrom_deg; + PropertyObject _PO_trackDistance_m; + PropertyObject _PO_heightAboveStation_ft; + + virtual void onExit() { + _PO_stationType = _stationType; + _PO_stationName = _stationName; + _PO_airportId = _airportId; + _PO_signalQuality_norm = _signalQuality_norm; + _PO_slantDistance_m = _slantDistance_m; + _PO_trueBearingTo_deg = _trueBearingTo_deg; + _PO_trueBearingFrom_deg = _trueBearingFrom_deg; + _PO_trackDistance_m = _trackDistance_m; + _PO_heightAboveStation_ft = _heightAboveStation_ft; + } +}; + +/* ------------- The CommRadio implementation ---------------------- */ + +class MetarBridge : public SGReferenced, public SGPropertyChangeListener { +public: + MetarBridge(); + ~MetarBridge(); + + void bind(); + void unbind(); + void requestMetarForId( std::string & id ); + void clearMetar(); + void setMetarPropertiesRoot( SGPropertyNode_ptr n ) { _metarPropertiesNode = n; } + void setAtisNode( SGPropertyNode * n ) { _atisNode = n; } + +protected: + virtual void valueChanged(SGPropertyNode * ); + +private: + std::string _requestedId; + SGPropertyNode_ptr _metarPropertiesNode; + SGPropertyNode * _atisNode; + ATISEncoder _atisEncoder; +}; +typedef SGSharedPtr MetarBridgeRef; + +MetarBridge::MetarBridge() : + _atisNode(0) +{ +} + +MetarBridge::~MetarBridge() +{ +} + +void MetarBridge::bind() +{ + _metarPropertiesNode->getNode( "valid", true )->addChangeListener( this ); +} + +void MetarBridge::unbind() +{ + _metarPropertiesNode->getNode( "valid", true )->removeChangeListener( this ); +} + +void MetarBridge::requestMetarForId( std::string & id ) +{ + std::string uppercaseId = simgear::strutils::uppercase( id ); + if( _requestedId == uppercaseId ) return; + _requestedId = uppercaseId; + _metarPropertiesNode->getNode( "station-id", true )->setStringValue( uppercaseId ); + _metarPropertiesNode->getNode( "valid", true )->setBoolValue( false ); + _metarPropertiesNode->getNode( "time-to-live", true )->setDoubleValue( 0.0 ); +} + +void MetarBridge::clearMetar() +{ + string empty; + requestMetarForId( empty ); +} + +void MetarBridge::valueChanged(SGPropertyNode * node ) +{ + // check for raising edge of valid flag + if( NULL == node || false == node->getBoolValue() ) + return; + + std::string responseId = simgear::strutils::uppercase( + _metarPropertiesNode->getNode( "station-id", true )->getStringValue() ); + + // unrequested metar!? + if( responseId != _requestedId ) + return; + + if( NULL != _atisNode ) { + MetarPropertiesATISInformationProvider provider( _metarPropertiesNode ); + _atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) ); + } +} + +/* ------------- The CommRadio implementation ---------------------- */ + +class CommRadioImpl : public CommRadio, OutputProperties { + +public: + CommRadioImpl( SGPropertyNode_ptr node ); + virtual ~CommRadioImpl(); + + virtual void update( double dt ); + virtual void init(); + void bind(); + void unbind(); + +private: + MetarBridgeRef _metarBridge; + FrequencyFormatter _useFrequencyFormatter; + FrequencyFormatter _stbyFrequencyFormatter; + const SignalQualityComputerRef _signalQualityComputer; + + double _stationTTL; + double _frequency; + flightgear::CommStationRef _commStationForFrequency; + + PropertyObject _serviceable; + PropertyObject _power_btn; + PropertyObject _power_good; + PropertyObject _volume_norm; + PropertyObject _atis; + + +}; + +CommRadioImpl::CommRadioImpl( SGPropertyNode_ptr node ) : + OutputProperties( fgGetNode("/instrumentation",true)->getNode( + node->getStringValue("name", "nav"), + node->getIntValue("number", 0), true)), + _metarBridge( new MetarBridge() ), + _useFrequencyFormatter( _rootNode->getNode("frequencies/selected-mhz",true), + _rootNode->getNode("frequencies/selected-mhz-fmt",true), 0.025 ), + _stbyFrequencyFormatter( _rootNode->getNode("frequencies/standby-mhz",true), + _rootNode->getNode("frequencies/standby-mhz-fmt",true), 0.025 ), + _signalQualityComputer( new SimpleDistanceSquareSignalQualityComputer(10*SG_NM_TO_METER) ), + + _stationTTL(0.0), + _frequency(-1.0), + _commStationForFrequency(NULL), + + _serviceable( _rootNode->getNode("serviceable",true) ), + _power_btn( _rootNode->getNode("power-btn",true) ), + _power_good( _rootNode->getNode("power-good",true) ), + _volume_norm( _rootNode->getNode("volume",true) ), + _atis( _rootNode->getNode("atis",true) ) +{ +} + +CommRadioImpl::~CommRadioImpl() +{ +} + +void CommRadioImpl::bind() +{ + _metarBridge->setAtisNode( _atis.node() ); + _metarBridge->setMetarPropertiesRoot( fgGetNode( "/environment/metar[3]", true ) ); + _metarBridge->bind(); +} + +void CommRadioImpl::unbind() +{ + _metarBridge->unbind(); +} + +void CommRadioImpl::init() +{ +} + +void CommRadioImpl::update( double dt ) +{ + if( dt < SGLimitsd::min() ) return; + _stationTTL -= dt; + + // Ensure all output properties get written on exit of this method + OnExit onExit(this); + + SGGeod position; + try { position = globals->get_aircraft_position(); } + catch( std::exception & ) { return; } + + if( false == (_power_btn )) { + _stationTTL = 0.0; + return; + } + + + if( _frequency != _useFrequencyFormatter.getFrequency() ) { + _frequency = _useFrequencyFormatter.getFrequency(); + _stationTTL = 0.0; + } + + if( _stationTTL <= 0.0 ) { + _stationTTL = 30.0; + + // Note: 122.375 must be rounded DOWN to 122370 + // in order to be consistent with apt.dat et cetera. + int freqKhz = 10 * static_cast(_frequency * 100 + 0.25); + _commStationForFrequency = flightgear::CommStation::findByFreq( freqKhz, position, NULL ); + + } + + if( false == _commStationForFrequency.valid() ) return; + + _slantDistance_m = dist(_commStationForFrequency->cart(), SGVec3d::fromGeod(position)); + + SGGeodesy::inverse(position, _commStationForFrequency->geod(), + _trueBearingTo_deg, + _trueBearingFrom_deg, + _trackDistance_m ); + + _heightAboveStation_ft = + SGMiscd::max(0.0, position.getElevationFt() + - _commStationForFrequency->airport()->elevation()); + + _signalQuality_norm = _signalQualityComputer->computeSignalQuality( _slantDistance_m ); + _stationType = _commStationForFrequency->nameForType( _commStationForFrequency->type() ); + _stationName = _commStationForFrequency->ident(); + _airportId = _commStationForFrequency->airport()->getId(); + + switch( _commStationForFrequency->type() ) { + case FGPositioned::FREQ_ATIS: + case FGPositioned::FREQ_AWOS: { + if( _signalQuality_norm > 0.01 ) { + _metarBridge->requestMetarForId( _airportId ); + } else { + _metarBridge->clearMetar(); + _atis = ""; + } + } + break; + + default: + _metarBridge->clearMetar(); + _atis = ""; + break; + } + +} + +SGSubsystem * CommRadio::createInstance( SGPropertyNode_ptr rootNode ) +{ + return new CommRadioImpl( rootNode ); +} + +} // namespace Instrumentation + diff --git a/src/Instrumentation/commradio.hxx b/src/Instrumentation/commradio.hxx new file mode 100644 index 000000000..746bd1284 --- /dev/null +++ b/src/Instrumentation/commradio.hxx @@ -0,0 +1,49 @@ +// commradio.hxx -- class to manage a nav radio instance +// +// Written by Torsten Dreyer, started February 2014 +// +// Copyright (C) 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. +// + + +#ifndef _FG_INSTRUMENTATION_COMMRADIO_HXX +#define _FG_INSTRUMENTATION_COMMRADIO_HXX + +#include +#include + +namespace Instrumentation { + +class SignalQualityComputer : public SGReferenced { +public: + virtual ~SignalQualityComputer(); + virtual double computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const = 0; + virtual double computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const = 0; + virtual double computeSignalQuality( double slantDistanceM ) const = 0; +}; + +typedef SGSharedPtr SignalQualityComputerRef; + +class CommRadio : public SGSubsystem +{ +public: + static SGSubsystem * createInstance( SGPropertyNode_ptr rootNode ); +}; + +} + +#endif // _FG_INSTRUMENTATION_COMMRADIO_HXX diff --git a/src/Instrumentation/frequencyformatter.hxx b/src/Instrumentation/frequencyformatter.hxx new file mode 100644 index 000000000..d231eb0a1 --- /dev/null +++ b/src/Instrumentation/frequencyformatter.hxx @@ -0,0 +1,47 @@ +#ifndef __FREQUENCY_FORMATTER_HXX +#define __FREQUENCY_FORMATTER_HXX + +/* ------------- A NAV/COMM Frequency formatter ---------------------- */ + +class FrequencyFormatter : public SGPropertyChangeListener { +public: + FrequencyFormatter( SGPropertyNode_ptr freqNode, SGPropertyNode_ptr fmtFreqNode, double channelSpacing ) : + _freqNode( freqNode ), + _fmtFreqNode( fmtFreqNode ), + _channelSpacing(channelSpacing) + { + _freqNode->addChangeListener( this ); + valueChanged(_freqNode); + } + ~FrequencyFormatter() + { + _freqNode->removeChangeListener( this ); + } + + void valueChanged (SGPropertyNode * prop) + { + // format as fixed decimal "nnn.nn" + std::ostringstream buf; + buf << std::fixed + << std::setw(5) + << std::setfill('0') + << std::setprecision(2) + << getFrequency(); + _fmtFreqNode->setStringValue( buf.str() ); + } + + double getFrequency() const + { + double d = SGMiscd::roundToInt(_freqNode->getDoubleValue() / _channelSpacing) * _channelSpacing; + // strip last digit, do not round + return ((int)(d*100))/100.0; + } + +private: + SGPropertyNode_ptr _freqNode; + SGPropertyNode_ptr _fmtFreqNode; + double _channelSpacing; +}; + + +#endif //__FREQUENCY_FORMATTER_HXX diff --git a/src/Instrumentation/instrument_mgr.cxx b/src/Instrumentation/instrument_mgr.cxx index c21bf7cba..44d636683 100644 --- a/src/Instrumentation/instrument_mgr.cxx +++ b/src/Instrumentation/instrument_mgr.cxx @@ -36,6 +36,7 @@ #include "mag_compass.hxx" #include "marker_beacon.hxx" #include "newnavradio.hxx" +#include "commradio.hxx" #include "slip_skid_ball.hxx" #include "transponder.hxx" #include "turn_indicator.hxx" @@ -169,6 +170,9 @@ bool FGInstrumentMgr::build (SGPropertyNode* config_props) } else if ( name == "marker-beacon" ) { set_subsystem( id, new FGMarkerBeacon( node ), 0.2 ); + } else if ( name == "comm-radio" ) { + set_subsystem( id, Instrumentation::CommRadio::createInstance( node ) ); + } else if ( name == "nav-radio" ) { set_subsystem( id, Instrumentation::NavRadio::createInstance( node ) ); diff --git a/src/Instrumentation/newnavradio.cxx b/src/Instrumentation/newnavradio.cxx index 41b606c15..0c97a7c6d 100644 --- a/src/Instrumentation/newnavradio.cxx +++ b/src/Instrumentation/newnavradio.cxx @@ -40,7 +40,7 @@ #include #include "navradio.hxx" - +#include "frequencyformatter.hxx" namespace Instrumentation { @@ -793,49 +793,6 @@ void GS::display( NavIndicator & navIndicator ) navIndicator.setGS( _glideslopeOffset_norm ); } -/* ------------- A NAV/COMM Frequency formatter ---------------------- */ - -class FrequencyFormatter : public SGPropertyChangeListener { -public: - FrequencyFormatter( SGPropertyNode_ptr freqNode, SGPropertyNode_ptr fmtFreqNode, double channelSpacing ) : - _freqNode( freqNode ), - _fmtFreqNode( fmtFreqNode ), - _channelSpacing(channelSpacing) - { - _freqNode->addChangeListener( this ); - valueChanged(_freqNode); - } - ~FrequencyFormatter() - { - _freqNode->removeChangeListener( this ); - } - - void valueChanged (SGPropertyNode * prop) - { - // format as fixed decimal "nnn.nn" - std::ostringstream buf; - buf << std::fixed - << std::setw(5) - << std::setfill('0') - << std::setprecision(2) - << getFrequency(); - _fmtFreqNode->setStringValue( buf.str() ); - } - - double getFrequency() const - { - double d = SGMiscd::roundToInt(_freqNode->getDoubleValue() / _channelSpacing) * _channelSpacing; - // strip last digit, do not round - return ((int)(d*100))/100.0; - } - -private: - SGPropertyNode_ptr _freqNode; - SGPropertyNode_ptr _fmtFreqNode; - double _channelSpacing; -}; - - /* ------------- The NavRadio implementation ---------------------- */ class NavRadioImpl : public NavRadio { diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 86494da84..4712eb4c5 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -78,7 +78,6 @@ #include -#include #include #include @@ -702,16 +701,6 @@ void fgCreateSubsystems(bool duringReset) { globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY); globals->add_subsystem("CanvasGUI", new GUIMgr, SGSubsystemMgr::DISPLAY); - //////////////////////////////////////////////////////////////////// - // Initialise the ATIS Manager - // Note that this is old stuff, but is necessary for the - // current ATIS implementation. Therefore, leave it in here - // until the ATIS system is ported over to make use of the ATIS - // sub system infrastructure. - //////////////////////////////////////////////////////////////////// - - globals->add_subsystem("ATIS", new FGATISMgr, SGSubsystemMgr::INIT, 0.4); - //////////////////////////////////////////////////////////////////// // Initialize the ATC subsystem //////////////////////////////////////////////////////////////////// @@ -927,8 +916,6 @@ void fgReInitSubsystems() globals->get_subsystem("systems")->reinit(); globals->get_subsystem("instrumentation")->reinit(); - globals->get_subsystem("ATIS")->reinit(); - // setup state to end re-init fgSetBool("/sim/signals/reinit", false); if ( !freeze ) { diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index d57defc0f..29377bb36 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -46,7 +46,6 @@ #include #include -#include #include #include #include @@ -146,7 +145,6 @@ FGGlobals::FGGlobals() : time_params( NULL ), ephem( NULL ), route_mgr( NULL ), - ATIS_mgr( NULL ), controls( NULL ), viewmgr( NULL ), commands( SGCommandMgr::instance() ), @@ -241,7 +239,6 @@ FGGlobals::~FGGlobals() delete time_params; set_matlib(NULL); delete route_mgr; - delete ATIS_mgr; delete channel_options_list; delete initial_waypoints; delete fontcache; diff --git a/src/Main/globals.hxx b/src/Main/globals.hxx index d36090846..61ee6ebfc 100644 --- a/src/Main/globals.hxx +++ b/src/Main/globals.hxx @@ -55,7 +55,6 @@ class SGSubsystemMgr; class SGSubsystem; class SGSoundMgr; -class FGATISMgr; class FGControls; class FGTACANList; class FGLocale; @@ -119,9 +118,6 @@ private: // Global autopilot "route" FGRouteMgr *route_mgr; - // ATC manager - FGATISMgr *ATIS_mgr; - // control input state FGControls *controls; @@ -271,9 +267,6 @@ public: inline SGMaterialLib *get_matlib() const { return matlib; } void set_matlib( SGMaterialLib *m ); - inline FGATISMgr *get_ATIS_mgr() const { return ATIS_mgr; } - inline void set_ATIS_mgr( FGATISMgr *a ) {ATIS_mgr = a; } - inline FGControls *get_controls() const { return controls; } inline void set_controls( FGControls *c ) { controls = c; } diff --git a/src/Main/locale.cxx b/src/Main/locale.cxx index 783b3f247..7572f42e9 100644 --- a/src/Main/locale.cxx +++ b/src/Main/locale.cxx @@ -191,6 +191,9 @@ FGLocale::selectLanguage(const char *language) // load resource for system messages (translations for fgfs internal messages) loadResource("sys"); + // load resource for atc messages + loadResource("atc"); + return true; } diff --git a/src/Main/subsystemFactory.cxx b/src/Main/subsystemFactory.cxx index 0c92aed91..2a8d7e56a 100644 --- a/src/Main/subsystemFactory.cxx +++ b/src/Main/subsystemFactory.cxx @@ -49,7 +49,6 @@ #include #include #include
-#include #include #include #include @@ -90,7 +89,6 @@ SGSubsystem* createSubsystemByName(const std::string& name) MAKE_SUB(FGRouteMgr, "route-manager"); MAKE_SUB(FGLogger, "logger"); MAKE_SUB(NewGUI, "gui"); - MAKE_SUB(FGATISMgr, "atis"); MAKE_SUB(FGATCManager, "atc"); MAKE_SUB(FGMultiplayMgr, "mp"); MAKE_SUB(FGTrafficManager, "traffic-manager"); -- 2.39.5