From: Thomas Geymayer Date: Sat, 1 Mar 2014 19:18:02 +0000 (+0100) Subject: TACAN/mobile navaid cleanup and improvements. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=234e2bdf091c64587152965926fe0e789066dfdf;p=flightgear.git TACAN/mobile navaid cleanup and improvements. --- diff --git a/src/Airports/airport.cxx b/src/Airports/airport.cxx index 5a511eb99..9d07d6b99 100644 --- a/src/Airports/airport.cxx +++ b/src/Airports/airport.cxx @@ -394,7 +394,7 @@ FGRunwayRef FGAirport::getActiveRunwayForUsage() const double hdg = 270; if (envMgr) { - FGEnvironment stationWeather(envMgr->getEnvironment(mPosition)); + FGEnvironment stationWeather(envMgr->getEnvironment(geod())); double windSpeed = stationWeather.get_wind_speed_kt(); if (windSpeed > 0.0) { @@ -617,7 +617,7 @@ void FGAirport::processThreshold(SGPropertyNode* aThreshold) double lon = aThreshold->getDoubleValue("lon"), lat = aThreshold->getDoubleValue("lat"); - SGGeod newThreshold(SGGeod::fromDegM(lon, lat, mPosition.getElevationM())); + SGGeod newThreshold(SGGeod::fromDegM(lon, lat, elevationM())); double newHeading = aThreshold->getDoubleValue("hdg-deg"); double newDisplacedThreshold = aThreshold->getDoubleValue("displ-m"); @@ -685,7 +685,7 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot) // tower elevation is AGL, not AMSL. Since we don't want to depend on the // scenery for a precise terrain elevation, we use the field elevation // (this is also what the apt.dat code does) - double fieldElevationM = geod().getElevationM(); + double fieldElevationM = elevationM(); SGGeod towerLocation(SGGeod::fromDegM(lon, lat, fieldElevationM + elevM)); NavDataCache* cache = NavDataCache::instance(); diff --git a/src/Airports/gnnode.cxx b/src/Airports/gnnode.cxx index 5c61f89af..619e2e115 100644 --- a/src/Airports/gnnode.cxx +++ b/src/Airports/gnnode.cxx @@ -33,16 +33,18 @@ void FGTaxiNode::setElevation(double val) double FGTaxiNode::getElevationFt() { - if (mPosition.getElevationFt() == 0.0) { - SGGeod center2 = mPosition; + const SGGeod& pos = geod(); + if( pos.getElevationFt() == 0.0) + { + SGGeod center2 = pos; FGScenery* local_scenery = globals->get_scenery(); center2.setElevationM(SG_MAX_ELEVATION_M); double elevationEnd = -100; - if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { - - SGGeod newPos = mPosition; + if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) + { + SGGeod newPos = pos; newPos.setElevationM(elevationEnd); - // this will call modifyPosition to update mPosition + // this will call modifyPosition to update mPosition NavDataCache* cache = NavDataCache::instance(); NavDataCache::Transaction txn(cache); cache->updatePosition(guid(), newPos); @@ -50,7 +52,7 @@ double FGTaxiNode::getElevationFt() } } - return mPosition.getElevationFt(); + return pos.getElevationFt(); } double FGTaxiNode::getElevationM() diff --git a/src/Airports/runwaybase.cxx b/src/Airports/runwaybase.cxx index 0e07c1fa9..4f60682a9 100644 --- a/src/Airports/runwaybase.cxx +++ b/src/Airports/runwaybase.cxx @@ -61,8 +61,8 @@ FGRunwayBase::FGRunwayBase(PositionedID aGuid, Type aTy, const string& aIdent, SGGeod FGRunwayBase::pointOnCenterline(double aOffset) const { - SGGeod result = SGGeodesy::direct(mPosition, _heading, aOffset); - result.setElevationM(mPosition.getElevationM()); + SGGeod result = SGGeodesy::direct(geod(), _heading, aOffset); + result.setElevationM(geod().getElevationM()); return result; } @@ -73,8 +73,7 @@ SGGeod FGRunwayBase::pointOffCenterline(double aOffset, double lateralOffset) co SGGeod temp; double dummyAz2; - SGGeodesy::direct(mPosition, _heading, - aOffset, temp, dummyAz2); + SGGeodesy::direct(geod(), _heading, aOffset, temp, dummyAz2); SGGeodesy::direct(temp, SGMiscd::normalizePeriodic(0, 360,_heading+90.0), lateralOffset, diff --git a/src/Instrumentation/tacan.cxx b/src/Instrumentation/tacan.cxx index 5b1d35864..f9b463f50 100644 --- a/src/Instrumentation/tacan.cxx +++ b/src/Instrumentation/tacan.cxx @@ -17,9 +17,6 @@ #include "tacan.hxx" -using std::vector; -using std::string; - /** * Adjust the range. * @@ -31,7 +28,7 @@ static double adjust_range (double transmitter_elevation_ft, double aircraft_altitude_ft, double max_range_nm) { - max_range_nm = 150; + max_range_nm = 150; // TODO why a fixed range? double delta_elevation_ft = fabs(aircraft_altitude_ft - transmitter_elevation_ft); double range_nm = 1.23 * sqrt(delta_elevation_ft); @@ -43,32 +40,31 @@ adjust_range (double transmitter_elevation_ft, double aircraft_altitude_ft, return range_nm + (range_nm * rand * rand); } - -TACAN::TACAN ( SGPropertyNode *node ) : - _name(node->getStringValue("name", "tacan")), - _num(node->getIntValue("number", 0)), - _new_frequency(false), - _channel("0000"), - _last_distance_nm(0), - _frequency_mhz(-1), - _time_before_search_sec(0), - _mobile_valid(false), - _transmitter_valid(false), - _transmitter_pos(SGGeod::fromDeg(0, 0)), - _transmitter_range_nm(0), - _transmitter_bias(0.0), - _listener_active(0) +//------------------------------------------------------------------------------ +TACAN::TACAN( SGPropertyNode *node ): + _name(node->getStringValue("name", "tacan")), + _num(node->getIntValue("number", 0)), + _was_disabled(true), + _new_frequency(false), + _channel("0000"), + _last_distance_nm(0), + _frequency_mhz(-1), + _time_before_search_sec(0), + _listener_active(0) { + } -TACAN::~TACAN () +//------------------------------------------------------------------------------ +TACAN::~TACAN() { + } -void -TACAN::init () +//------------------------------------------------------------------------------ +void TACAN::init() { - string branch = "/instrumentation/" + _name; + std::string branch = "/instrumentation/" + _name; SGPropertyNode *node = fgGetNode(branch.c_str(), _num, true ); @@ -84,12 +80,6 @@ TACAN::init () _channel_in3_node = fnode->getChild("selected-channel", 3, true); _channel_in4_node = fnode->getChild("selected-channel", 4, true); - _channel_in0_node->addChangeListener(this); - _channel_in1_node->addChangeListener(this); - _channel_in2_node->addChangeListener(this); - _channel_in3_node->addChangeListener(this); - _channel_in4_node->addChangeListener(this, true); - _in_range_node = node->getChild("in-range", 0, true); _distance_node = node->getChild("indicated-distance-nm", 0, true); _speed_node = node->getChild("indicated-ground-speed-kt", 0, true); @@ -100,298 +90,176 @@ TACAN::init () SGPropertyNode *dnode = node->getChild("display", 0, true); _x_shift_node = dnode->getChild("x-shift", 0, true); _y_shift_node = dnode->getChild("y-shift", 0, true); - _rotation_node = dnode->getChild("rotation", 0, true); _channel_node = dnode->getChild("channel", 0, true); - SGPropertyNode *cnode = fgGetNode("/ai/models/carrier", _num, false ); - _carrier_name_node = cnode ? cnode->getChild("name", 0, false) : 0; - - SGPropertyNode *tnode = fgGetNode("/ai/models/aircraft", _num, false); - _tanker_callsign_node = tnode ? tnode->getChild("callsign", 0, false) : 0; - - SGPropertyNode *mnode = fgGetNode("/ai/models/multiplayer", _num, false); - _mp_callsign_node = mnode ? mnode->getChild("callsign", 0, false) : 0; - _heading_node = fgGetNode("/orientation/heading-deg", true); _electrical_node = fgGetNode("/systems/electrical/outputs/tacan", true); + + // Add/trigger change listener after creating all nodes + _channel_in0_node->addChangeListener(this); + _channel_in1_node->addChangeListener(this); + _channel_in2_node->addChangeListener(this); + _channel_in3_node->addChangeListener(this); + _channel_in4_node->addChangeListener(this, true); } -void -TACAN::reinit () +//------------------------------------------------------------------------------ +void TACAN::reinit() { - _time_before_search_sec = 0; + _time_before_search_sec = 0; } -void -TACAN::update (double delta_time_sec) +//------------------------------------------------------------------------------ +void TACAN::update(double delta_time_sec) { - // don't do anything when paused - if (delta_time_sec == 0) return; - - if (!_serviceable_node->getBoolValue() || !_electrical_node->getBoolValue()) { - _last_distance_nm = 0; - _in_range_node->setBoolValue(false); - _distance_node->setDoubleValue(0); - _speed_node->setDoubleValue(0); - _time_node->setDoubleValue(0); - return; - } + // don't do anything when paused + if( delta_time_sec == 0 ) + return; - SGGeod pos(globals->get_aircraft_position()); - // On timeout, scan again - _time_before_search_sec -= delta_time_sec; - if ((_time_before_search_sec < 0 || _new_frequency) && _frequency_mhz >= 0) - search(_frequency_mhz, pos); - - // Calculate the distance to the transmitter - - //calculate the bearing and range of the mobile from the aircraft - double mobile_az2 = 0; - double mobile_bearing = 0; - double mobile_distance = SGLimitsd::max(); - if (_mobile_valid) { - geo_inverse_wgs_84(pos, _mobilePos, - &mobile_bearing, &mobile_az2, &mobile_distance); - } + if( !_serviceable_node->getBoolValue() + || !_electrical_node->getBoolValue() ) + return disabled(); - //calculate the bearing and range of the station from the aircraft - double az2 = 0; - double bearing = 0; - double distance = SGLimitsd::max(); - if (_transmitter_valid) { - geo_inverse_wgs_84(pos, _transmitter_pos, - &bearing, &az2, &distance); - } - - //select the nearer - if ( mobile_distance <= distance && _mobile_valid) { - bearing = mobile_bearing; - distance = mobile_distance; - _transmitter_pos.setElevationFt(_mobilePos.getElevationFt()); - _transmitter_range_nm = _mobile_range_nm; - _transmitter_bias = _mobile_bias; - _transmitter_name = _mobile_name; - _name_node->setStringValue(_transmitter_name.c_str()); - _transmitter_ident = _mobile_ident; - _ident_node->setStringValue(_transmitter_ident.c_str()); - _channel_node->setStringValue(_channel.c_str()); - } + SGGeod pos(globals->get_aircraft_position()); - //// calculate some values for boresight display - double distance_nm = distance * SG_METER_TO_NM; + // On timeout, scan again + _time_before_search_sec -= delta_time_sec; + if ((_time_before_search_sec < 0 || _new_frequency) && _frequency_mhz >= 0) + search(_frequency_mhz, pos); - //// calculate look left/right to target, without yaw correction - // double horiz_offset = bearing - heading; - // - // if (horiz_offset > 180.0) horiz_offset -= 360.0; - // if (horiz_offset < -180.0) horiz_offset += 360.0; + if( !_active_station || !_active_station->get_serviceable() ) + return disabled(); - //// now correct look left/right for yaw - // horiz_offset += yaw; + // Calculate the bearing and range of the station from the aircraft + double az = 0; + double bearing = 0; + double distance = SGLimitsd::max(); + if( !SGGeodesy::inverse(pos, _active_station->geod(), bearing, az, distance) ) + return disabled(); - // use the bearing for a plan position indicator display - - double horiz_offset = bearing; + double range_nm = adjust_range( _active_station->get_elev_ft(), + pos.getElevationFt(), + _active_station->get_range() ); - // calculate values for radar display - double y_shift = distance_nm * cos( horiz_offset * SG_DEGREES_TO_RADIANS); - double x_shift = distance_nm * sin( horiz_offset * SG_DEGREES_TO_RADIANS); + double distance_nm = distance * SG_METER_TO_NM; - double rotation = 0; + if( distance_nm > range_nm ) + return disabled(); - double range_nm = adjust_range(_transmitter_pos.getElevationFt(), - pos.getElevationFt(), - _transmitter_range_nm); + //// calculate look left/right to target, without yaw correction + // double horiz_offset = bearing - heading; + // + // if (horiz_offset > 180.0) horiz_offset -= 360.0; + // if (horiz_offset < -180.0) horiz_offset += 360.0; - if (distance_nm <= range_nm) { - double speed_kt = (fabs(distance_nm - _last_distance_nm) * - ((1 / delta_time_sec) * 3600.0)); - _last_distance_nm = distance_nm; + //// now correct look left/right for yaw + // horiz_offset += yaw; - _in_range_node->setBoolValue(true); - double tmp_dist = distance_nm - _transmitter_bias; - if ( tmp_dist < 0.0 ) { - tmp_dist = 0.0; - } - _distance_node->setDoubleValue( tmp_dist ); - _speed_node->setDoubleValue(speed_kt); - _time_node->setDoubleValue(speed_kt > 0 ? (distance_nm/speed_kt*60.0) : 0); - _bearing_node->setDoubleValue(bearing); - _x_shift_node->setDoubleValue(x_shift); - _y_shift_node->setDoubleValue(y_shift); - _rotation_node->setDoubleValue(rotation); - } else { - _last_distance_nm = 0; - _in_range_node->setBoolValue(false); - _distance_node->setDoubleValue(0); - _speed_node->setDoubleValue(0); - _time_node->setDoubleValue(0); - _bearing_node->setDoubleValue(0); - _x_shift_node->setDoubleValue(0); - _y_shift_node->setDoubleValue(0); - _rotation_node->setDoubleValue(0); - } + // use the bearing for a plan position indicator display + double horiz_offset = bearing; - // If we can't find a valid station set everything to zero - if (!_transmitter_valid && !_mobile_valid ) { - _in_range_node->setBoolValue(false); - _distance_node->setDoubleValue(0); - _speed_node->setDoubleValue(0); - _time_node->setDoubleValue(0); - _bearing_node->setDoubleValue(0); - _x_shift_node->setDoubleValue(0); - _y_shift_node->setDoubleValue(0); - _rotation_node->setDoubleValue(0); - _transmitter_name = ""; - _name_node->setStringValue(_transmitter_name.c_str()); - _transmitter_ident = ""; - _ident_node->setStringValue(_transmitter_ident.c_str()); - _channel_node->setStringValue(_channel.c_str()); - return; - } -} // end function update + // calculate values for radar display + double y_shift = distance_nm * cos(horiz_offset * SG_DEGREES_TO_RADIANS); + double x_shift = distance_nm * sin(horiz_offset * SG_DEGREES_TO_RADIANS); -void -TACAN::search (double frequency_mhz,const SGGeod& pos) -{ - int number, i; - _mobile_valid = false; - - // reset search time - _time_before_search_sec = 1.0; - - //try any carriers first - FGNavRecord *mobile_tacan = FGNavList::findByFreq( frequency_mhz, FGNavList::carrierFilter() ); - bool freq_valid = (mobile_tacan != NULL); - - if ( freq_valid ) { - - string str1( mobile_tacan->name() ); - - SGPropertyNode * branch = fgGetNode("ai/models", true); - vector carrier = branch->getChildren("carrier"); - - number = carrier.size(); - - for ( i = 0; i < number; ++i ) { - string str2 ( carrier[i]->getStringValue("name", "")); - - string::size_type loc1= str1.find( str2, 0 ); - if ( loc1 != string::npos && str2 != "" ) { - _mobilePos = SGGeod::fromDegFt( - carrier[i]->getDoubleValue("position/longitude-deg"), - carrier[i]->getDoubleValue("position/latitude-deg"), - mobile_tacan->get_elev_ft()); - _mobile_range_nm = mobile_tacan->get_range(); - _mobile_bias = mobile_tacan->get_multiuse(); - _mobile_name = mobile_tacan->name(); - _mobile_ident = mobile_tacan->get_trans_ident(); - _mobile_valid = true; - break; - } else { - _mobile_valid = false; - } - } + // TODO probably not the best way to do this (especially with mobile stations) + double speed_kt = fabs(distance_nm - _last_distance_nm) + * (3600 / delta_time_sec); + _speed_node->setDoubleValue(speed_kt); + _last_distance_nm = distance_nm; - //try any AI tankers second - if ( !_mobile_valid) { - SGPropertyNode * branch = fgGetNode("ai/models", true); - vector tanker = branch->getChildren("tanker"); - - number = tanker.size(); - - for ( i = 0; i < number; ++i ) { - string str4 ( tanker[i]->getStringValue("callsign", "")); - string::size_type loc1= str1.find( str4, 0 ); - if ( loc1 != string::npos && str4 != "" ) { - _mobilePos = SGGeod::fromDegFt( - tanker[i]->getDoubleValue("position/longitude-deg"), - tanker[i]->getDoubleValue("position/latitude-deg"), - tanker[i]->getDoubleValue("position/altitude-ft")); - - - _mobile_range_nm = mobile_tacan->get_range(); - _mobile_bias = mobile_tacan->get_multiuse(); - _mobile_name = mobile_tacan->name(); - _mobile_ident = mobile_tacan->get_trans_ident(); - _mobile_valid = true; - break; - } else { - _mobile_valid = false; - } - } - } + double bias = _active_station->get_multiuse(); + _distance_node->setDoubleValue( SGMiscd::max(0.0, distance_nm - bias) ); - //try any mp tankers third, if we haven't found the tanker in the ai aircraft - - if ( !_mobile_valid ) { - SGPropertyNode * branch = fgGetNode("ai/models", true); - vector mp_tanker = branch->getChildren("multiplayer"); - - number = mp_tanker.size(); - - if ( number > 0 ) { // don't do this if there are no MP aircraft - for ( i = 0; i < number; ++i ) { - string str6 ( mp_tanker[i]->getStringValue("callsign", "")); - string::size_type loc1= str1.find( str6, 0 ); - if ( loc1 != string::npos && str6 != "" ) { - _mobilePos = SGGeod::fromDegFt( - mp_tanker[i]->getDoubleValue("position/longitude-deg"), - mp_tanker[i]->getDoubleValue("position/latitude-deg"), - mp_tanker[i]->getDoubleValue("position/altitude-ft")); - - - _mobile_range_nm = mobile_tacan->get_range(); - _mobile_bias = mobile_tacan->get_multiuse(); - _mobile_name = mobile_tacan->name(); - _mobile_ident = mobile_tacan->get_trans_ident(); - _mobile_valid = true; - break; - } else { - _mobile_valid = false; - } - } - } - } - } else { - _mobile_valid = false; - } + _time_node->setDoubleValue(speed_kt > 0 ? (distance_nm/speed_kt*60.0) : 0); + _bearing_node->setDoubleValue(bearing); + _x_shift_node->setDoubleValue(x_shift); + _y_shift_node->setDoubleValue(y_shift); - // try the TACAN/VORTAC list next - FGNavRecord *tacan = FGNavList::findByFreq( frequency_mhz, pos, FGNavList::tacanFilter() ); + _name_node->setStringValue(_active_station->name()); + _ident_node->setStringValue(_active_station->ident()); + _in_range_node->setBoolValue(true); - _transmitter_valid = (tacan != NULL); + _was_disabled = false; +} - if ( _transmitter_valid ) { +//------------------------------------------------------------------------------ +void TACAN::disabled() +{ + if( _was_disabled ) + return; + + _last_distance_nm = 0; + + _in_range_node->setBoolValue(false); + _distance_node->setDoubleValue(0); + _speed_node->setDoubleValue(0); + _time_node->setDoubleValue(0); + _bearing_node->setDoubleValue(0); + _x_shift_node->setDoubleValue(0); + _y_shift_node->setDoubleValue(0); + _name_node->setStringValue(""); + _ident_node->setStringValue(""); + + _was_disabled = true; +} - _transmitter_pos = tacan->geod(); - _transmitter_range_nm = tacan->get_range(); - _transmitter_bias = tacan->get_multiuse(); - _transmitter_name = tacan->name(); - _name_node->setStringValue(_transmitter_name.c_str()); - _transmitter_ident = tacan->get_trans_ident(); - _ident_node->setStringValue(_transmitter_ident.c_str()); - } +//------------------------------------------------------------------------------ +void TACAN::search (double frequency_mhz,const SGGeod& pos) +{ + // reset search time + _time_before_search_sec = 1.0; + + // Get first matching mobile station (carriers/tankers/etc.) + // TODO do we need to check for mobile stations with same frequency? Currently + // the distance is not taken into account. + FGNavRecordRef mobile_tacan = + FGNavList::findByFreq(frequency_mhz, FGNavList::mobileTacanFilter()); + + double mobile_dist = (mobile_tacan && mobile_tacan->get_serviceable()) + ? SGGeodesy::distanceM(pos, mobile_tacan->geod()) + : SGLimitsd::max(); + + // No get nearest TACAN/VORTAC + FGNavRecordRef tacan = + FGNavList::findByFreq(frequency_mhz, pos, FGNavList::tacanFilter()); + + double tacan_dist = tacan + ? SGGeodesy::distanceM(pos, tacan->geod()) + : SGLimitsd::max(); + + // Select nearer station as active + // TODO do we need to take more care of stations with same frequency? + _active_station = mobile_dist < tacan_dist + ? mobile_tacan + : tacan; } -double -TACAN::searchChannel (const string& channel) +//------------------------------------------------------------------------------ +double TACAN::searchChannel(const std::string& channel) { - double frequency_khz = 0; - - FGTACANRecord *freq - = globals->get_channellist()->findByChannel( channel ); - bool _freq_valid = (freq != NULL); - SG_LOG( SG_INSTR, SG_DEBUG, "freq valid " << _freq_valid ); - if ( _freq_valid ) { - frequency_khz = freq->get_freq(); - SG_LOG( SG_INSTR, SG_DEBUG, "freq output " << frequency_khz ); - //check sanity - if (frequency_khz >= 9620 && frequency_khz <= 121300) - return frequency_khz/100; - } - return frequency_khz = 0; -} // end TACAN::searchChannel + FGTACANRecord *freq = globals->get_channellist()->findByChannel(channel); + if( !freq ) + { + SG_LOG(SG_INSTR, SG_DEBUG, "Invalid TACAN channel: " << channel); + return 0; + } + + double frequency_khz = freq->get_freq(); + if(frequency_khz < 9620 || frequency_khz > 121300) + { + SG_LOG + ( + SG_INSTR, + SG_DEBUG, + "TACAN frequency out of range: " << channel + << ": " << frequency_khz << "kHz" + ); + return 0; + } + + return frequency_khz/100; +} /* * Listener callback. Maintains channel input properties, @@ -406,7 +274,7 @@ TACAN::valueChanged(SGPropertyNode *prop) _listener_active++; int index = prop->getIndex(); - string channel = _channel; + std::string channel = _channel; if (index) { // channel digit or X/Y input int c; @@ -451,6 +319,7 @@ TACAN::valueChanged(SGPropertyNode *prop) } _channel = channel; + _channel_node->setStringValue(_channel); _time_before_search_sec = 0; } diff --git a/src/Instrumentation/tacan.hxx b/src/Instrumentation/tacan.hxx index b48057f4b..92f57c7d3 100644 --- a/src/Instrumentation/tacan.hxx +++ b/src/Instrumentation/tacan.hxx @@ -34,19 +34,21 @@ class TACAN : public SGSubsystem, public SGPropertyChangeListener { -public: + public: - TACAN ( SGPropertyNode *node ); - virtual ~TACAN (); + TACAN(SGPropertyNode *node); + virtual ~TACAN(); virtual void init (); virtual void reinit (); virtual void update (double delta_time_sec); -private: + private: + + void disabled(); void search (double frequency, const SGGeod& pos); - double searchChannel (const std::string& channel); + double searchChannel (const std::string& channel); void valueChanged (SGPropertyNode *); std::string _name; @@ -59,7 +61,6 @@ private: SGPropertyNode_ptr _display_node; SGPropertyNode_ptr _x_shift_node; SGPropertyNode_ptr _y_shift_node; - SGPropertyNode_ptr _rotation_node; SGPropertyNode_ptr _in_range_node; SGPropertyNode_ptr _distance_node; @@ -76,31 +77,14 @@ private: SGPropertyNode_ptr _channel_in3_node; SGPropertyNode_ptr _channel_in4_node; - SGPropertyNode_ptr _carrier_name_node; // FIXME unused - SGPropertyNode_ptr _tanker_callsign_node; // FIXME - SGPropertyNode_ptr _mp_callsign_node; // FIXME - + bool _was_disabled; bool _new_frequency; std::string _channel; double _last_distance_nm; double _frequency_mhz; double _time_before_search_sec; - bool _mobile_valid; - bool _transmitter_valid; - - SGVec3d _transmitter; - SGGeod _transmitter_pos; - double _transmitter_range_nm; - double _transmitter_bias; - std::string _transmitter_name; - std::string _transmitter_ident; - - SGGeod _mobilePos; - double _mobile_range_nm; - double _mobile_bias; - std::string _mobile_name; - std::string _mobile_ident; + FGNavRecordRef _active_station; int _listener_active; }; diff --git a/src/Navaids/NavDataCache.cxx b/src/Navaids/NavDataCache.cxx index 6a8dda5d0..bbd399032 100644 --- a/src/Navaids/NavDataCache.cxx +++ b/src/Navaids/NavDataCache.cxx @@ -716,10 +716,15 @@ public: PositionedID colocated = sqlite3_column_int64(loadNavaid, 4); reset(loadNavaid); - FGNavRecord* n = new FGNavRecord(rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway); - if (colocated) { - n->setColocatedDME(colocated); - } + FGNavRecord* n = + (ty == FGPositioned::MOBILE_TACAN) + ? new FGMobileNavRecord + (rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway) + : new FGNavRecord + (rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway); + + if (colocated) + n->setColocatedDME(colocated); return n; } diff --git a/src/Navaids/navlist.cxx b/src/Navaids/navlist.cxx index 822391e04..9b0c911de 100644 --- a/src/Navaids/navlist.cxx +++ b/src/Navaids/navlist.cxx @@ -179,7 +179,7 @@ FGNavList::TypeFilter* FGNavList::tacanFilter() return &tf; } -FGNavList::TypeFilter* FGNavList::carrierFilter() +FGNavList::TypeFilter* FGNavList::mobileTacanFilter() { static TypeFilter tf(FGPositioned::MOBILE_TACAN); return &tf; diff --git a/src/Navaids/navlist.hxx b/src/Navaids/navlist.hxx index d2db76cbf..c10b7e3b7 100644 --- a/src/Navaids/navlist.hxx +++ b/src/Navaids/navlist.hxx @@ -86,7 +86,7 @@ public: static TypeFilter* tacanFilter(); - static TypeFilter* carrierFilter(); + static TypeFilter* mobileTacanFilter(); /** Query the database for the specified station. It is assumed * that there will be multiple stations with matching frequencies diff --git a/src/Navaids/navrecord.cxx b/src/Navaids/navrecord.cxx index cb83fd02d..bfc78f632 100644 --- a/src/Navaids/navrecord.cxx +++ b/src/Navaids/navrecord.cxx @@ -109,6 +109,108 @@ void FGNavRecord::updateFromXML(const SGGeod& geod, double heading) multiuse = heading; } +//------------------------------------------------------------------------------ +FGMobileNavRecord::FGMobileNavRecord( PositionedID aGuid, + Type type, + const std::string& ident, + const std::string& name, + const SGGeod& aPos, + int freq, + int range, + double multiuse, + PositionedID aRunway ): + FGNavRecord(aGuid, type, ident, name, aPos, freq, range, multiuse, aRunway) +{ + +} + +//------------------------------------------------------------------------------ +const SGGeod& FGMobileNavRecord::geod() const +{ + const_cast(this)->updatePos(); + return FGNavRecord::geod(); +} + +//------------------------------------------------------------------------------ +const SGVec3d& FGMobileNavRecord::cart() const +{ + const_cast(this)->updatePos(); + return FGNavRecord::cart(); +} + +//------------------------------------------------------------------------------ +void FGMobileNavRecord::updatePos() +{ + SGTimeStamp now = SGTimeStamp::now(); + if( (now - _last_position_update).toSecs() < 1 ) + return; + _last_position_update = now; + + SGPropertyNode* ai_branch = fgGetNode("ai/models"); + if( !ai_branch ) + { + SG_LOG( SG_NAVAID, + SG_INFO, + "Can not update mobile navaid position (no ai/models branch)" ); + return; + } + + serviceable = true; + const std::string& nav_name = name(); + + // Try any aircraft carriers first + simgear::PropertyList carrier = ai_branch->getChildren("carrier"); + for(size_t i = 0; i < carrier.size(); ++i) + { + const std::string carrier_name = carrier[i]->getStringValue("name"); + + if( carrier_name.empty() + || nav_name.find(carrier_name) == std::string::npos ) + continue; + + modifyPosition(SGGeod::fromDegFt( + carrier[i]->getDoubleValue("position/longitude-deg"), + carrier[i]->getDoubleValue("position/latitude-deg"), + get_elev_ft() + )); + return; + } + + // Now the tankers + const std::string tanker_branches[] = { + // AI tankers + "tanker", + // And finally mp tankers + "multiplayer" + }; + + for(size_t i = 0; i < sizeof(tanker_branches)/sizeof(tanker_branches[0]); ++i) + { + simgear::PropertyList tanker = ai_branch->getChildren(tanker_branches[i]); + for(size_t j = 0; j < tanker.size(); ++j) + { + const std::string callsign = tanker[j]->getStringValue("callsign"); + + if( callsign.empty() + || nav_name.find(callsign) == std::string::npos ) + continue; + + modifyPosition(SGGeod::fromDegFt( + tanker[j]->getDoubleValue("position/longitude-deg"), + tanker[j]->getDoubleValue("position/latitude-deg"), + tanker[j]->getDoubleValue("position/altitude-ft") + )); + return; + } + } + + // If no match was found set 'invalid' position (lat = lon = alt = 0) + modifyPosition(SGGeod()); + + // It's mobile but we do not know where it is... + serviceable = false; +} + FGTACANRecord::FGTACANRecord(void) : channel(""), freq(0) diff --git a/src/Navaids/navrecord.hxx b/src/Navaids/navrecord.hxx index a96e171d8..f7400ba8f 100644 --- a/src/Navaids/navrecord.hxx +++ b/src/Navaids/navrecord.hxx @@ -30,6 +30,8 @@ #include "positioned.hxx" #include +#include + const double FG_NAV_DEFAULT_RANGE = 50; // nm const double FG_LOC_DEFAULT_RANGE = 18; // nm const double FG_DME_DEFAULT_RANGE = 50; // nm @@ -44,18 +46,24 @@ class FGNavRecord : public FGPositioned // (degrees) or localizer heading // (degrees) or dme bias (nm) - std::string mName; // verbose name in nav database - PositionedID mRunway; // associated runway, if there is one - PositionedID mColocated; // Colocated DME at a navaid (ILS, VOR, TACAN, NDB) - bool serviceable; // for failure modeling + std::string mName; // verbose name in nav database + PositionedID mRunway; // associated runway, if there is one + PositionedID mColocated; // Colocated DME at a navaid (ILS, VOR, TACAN, NDB) + + protected: + mutable bool serviceable; // for failure modeling + + public: + FGNavRecord( PositionedID aGuid, + Type type, + const std::string& ident, + const std::string& name, + const SGGeod& aPos, + int freq, + int range, + double multiuse, + PositionedID aRunway ); -public: - FGNavRecord(PositionedID aGuid, Type type, const std::string& ident, - const std::string& name, - const SGGeod& aPos, - int freq, int range, double multiuse, - PositionedID aRunway); - inline double get_lon() const { return longitude(); } // degrees inline double get_lat() const { return latitude(); } // degrees inline double get_elev_ft() const { return elevation(); } @@ -93,6 +101,33 @@ public: void updateFromXML(const SGGeod& geod, double heading); }; +/** + * A mobile navaid, aka. a navaid which can change its position (eg. a mobile + * TACAN) + */ +class FGMobileNavRecord: + public FGNavRecord +{ + public: + FGMobileNavRecord( PositionedID aGuid, + Type type, + const std::string& ident, + const std::string& name, + const SGGeod& aPos, + int freq, + int range, + double multiuse, + PositionedID aRunway ); + + virtual const SGGeod& geod() const; + virtual const SGVec3d& cart() const; + + void updatePos(); + + protected: + SGTimeStamp _last_position_update; +}; + class FGTACANRecord : public SGReferenced { std::string channel; diff --git a/src/Navaids/positioned.hxx b/src/Navaids/positioned.hxx index 8b4fb6a3f..b0059c89b 100644 --- a/src/Navaids/positioned.hxx +++ b/src/Navaids/positioned.hxx @@ -111,7 +111,7 @@ public: virtual const std::string& name() const { return mIdent; } - const SGGeod& geod() const + virtual const SGGeod& geod() const { return mPosition; } PositionedID guid() const @@ -120,19 +120,19 @@ public: /** * The cartesian position associated with this object */ - const SGVec3d& cart() const; + virtual const SGVec3d& cart() const; double latitude() const - { return mPosition.getLatitudeDeg(); } + { return geod().getLatitudeDeg(); } double longitude() const - { return mPosition.getLongitudeDeg(); } + { return geod().getLongitudeDeg(); } double elevation() const - { return mPosition.getElevationFt(); } + { return geod().getElevationFt(); } double elevationM() const - { return mPosition.getElevationM(); } + { return geod().getElevationM(); } /** * Predicate class to support custom filtering of FGPositioned queries @@ -280,13 +280,14 @@ protected: static FGPositionedRef loadByIdImpl(PositionedID id); const PositionedID mGuid; - const SGGeod mPosition; - const SGVec3d mCart; const Type mType; const std::string mIdent; private: SG_DISABLE_COPY(FGPositioned); + + const SGGeod mPosition; + const SGVec3d mCart; }; #endif // of FG_POSITIONED_HXX