1
0
Fork 0

Merge branch 'jmt/navradio'

The previous merge claimed to have merged this branch; it didn't because
I had reset the branch to someplace weird.
This commit is contained in:
Tim Moore 2009-10-01 23:49:57 +02:00
commit 4023bdaf26
2 changed files with 139 additions and 60 deletions

View file

@ -78,6 +78,17 @@ static SGVec3d tangentVector(const SGGeod& tail, const SGVec3d& tail_xyz,
return (head_xyz - tail_xyz) * (1.0/fudge); return (head_xyz - tail_xyz) * (1.0/fudge);
} }
// Create a "serviceable" node with a default value of "true"
SGPropertyNode_ptr createServiceableProp(SGPropertyNode* aParent, const char* aName)
{
SGPropertyNode_ptr n = (aParent->getChild(aName, 0, true)->getChild("serviceable", 0, true));
simgear::props::Type typ = n->getType();
if ((typ == simgear::props::NONE) || (typ == simgear::props::UNSPECIFIED)) {
n->setBoolValue(true);
}
return n;
}
// Constructor // Constructor
FGNavRadio::FGNavRadio(SGPropertyNode *node) : FGNavRadio::FGNavRadio(SGPropertyNode *node) :
lon_node(fgGetNode("/position/longitude-deg", true)), lon_node(fgGetNode("/position/longitude-deg", true)),
@ -96,6 +107,7 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) :
cdi_serviceable_node(NULL), cdi_serviceable_node(NULL),
gs_serviceable_node(NULL), gs_serviceable_node(NULL),
tofrom_serviceable_node(NULL), tofrom_serviceable_node(NULL),
dme_serviceable_node(NULL),
fmt_freq_node(NULL), fmt_freq_node(NULL),
fmt_alt_freq_node(NULL), fmt_alt_freq_node(NULL),
heading_node(NULL), heading_node(NULL),
@ -120,6 +132,7 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) :
gs_deflection_norm_node(NULL), gs_deflection_norm_node(NULL),
gs_rate_of_climb_node(NULL), gs_rate_of_climb_node(NULL),
gs_dist_node(NULL), gs_dist_node(NULL),
gs_inrange_node(NULL),
nav_id_node(NULL), nav_id_node(NULL),
id_c1_node(NULL), id_c1_node(NULL),
id_c2_node(NULL), id_c2_node(NULL),
@ -140,7 +153,8 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) :
_localizerWidth(5.0), _localizerWidth(5.0),
_name(node->getStringValue("name", "nav")), _name(node->getStringValue("name", "nav")),
_num(node->getIntValue("number", 0)), _num(node->getIntValue("number", 0)),
_time_before_search_sec(-1.0) _time_before_search_sec(-1.0),
_falseCoursesEnabled(true)
{ {
SGPath path( globals->get_fg_root() ); SGPath path( globals->get_fg_root() );
SGPath term = path; SGPath term = path;
@ -189,14 +203,16 @@ FGNavRadio::init ()
audio_btn_node->setBoolValue( true ); audio_btn_node->setBoolValue( true );
backcourse_node = node->getChild("back-course-btn", 0, true); backcourse_node = node->getChild("back-course-btn", 0, true);
backcourse_node->setBoolValue( false ); backcourse_node->setBoolValue( false );
nav_serviceable_node = node->getChild("serviceable", 0, true); nav_serviceable_node = node->getChild("serviceable", 0, true);
cdi_serviceable_node = (node->getChild("cdi", 0, true)) cdi_serviceable_node = createServiceableProp(node, "cdi");
->getChild("serviceable", 0, true); gs_serviceable_node = createServiceableProp(node, "gs");
gs_serviceable_node = (node->getChild("gs", 0, true)) tofrom_serviceable_node = createServiceableProp(node, "to-from");
->getChild("serviceable"); dme_serviceable_node = createServiceableProp(node, "dme");
tofrom_serviceable_node = (node->getChild("to-from", 0, true))
->getChild("serviceable", 0, true); globals->get_props()->tie("sim/realism/false-radio-courses-enabled",
SGRawValuePointer<bool>(&_falseCoursesEnabled));
// frequencies // frequencies
SGPropertyNode *subnode = node->getChild("frequencies", 0, true); SGPropertyNode *subnode = node->getChild("frequencies", 0, true);
freq_node = subnode->getChild("selected-mhz", 0, true); freq_node = subnode->getChild("selected-mhz", 0, true);
@ -232,12 +248,16 @@ FGNavRadio::init ()
gs_deflection_norm_node = node->getChild("gs-needle-deflection-norm", 0, true); gs_deflection_norm_node = node->getChild("gs-needle-deflection-norm", 0, true);
gs_rate_of_climb_node = node->getChild("gs-rate-of-climb", 0, true); gs_rate_of_climb_node = node->getChild("gs-rate-of-climb", 0, true);
gs_dist_node = node->getChild("gs-distance", 0, true); gs_dist_node = node->getChild("gs-distance", 0, true);
gs_inrange_node = node->getChild("gs-in-range", 0, true);
nav_id_node = node->getChild("nav-id", 0, true); nav_id_node = node->getChild("nav-id", 0, true);
id_c1_node = node->getChild("nav-id_asc1", 0, true); id_c1_node = node->getChild("nav-id_asc1", 0, true);
id_c2_node = node->getChild("nav-id_asc2", 0, true); id_c2_node = node->getChild("nav-id_asc2", 0, true);
id_c3_node = node->getChild("nav-id_asc3", 0, true); id_c3_node = node->getChild("nav-id_asc3", 0, true);
id_c4_node = node->getChild("nav-id_asc4", 0, true); id_c4_node = node->getChild("nav-id_asc4", 0, true);
node->tie("dme-in-range", SGRawValuePointer<bool>(&_dmeInRange));
// gps slaving support // gps slaving support
nav_slaved_to_gps_node = node->getChild("slaved-to-gps", 0, true); nav_slaved_to_gps_node = node->getChild("slaved-to-gps", 0, true);
gps_cdi_deflection_node = fgGetNode("/instrumentation/gps/cdi-deflection", true); gps_cdi_deflection_node = fgGetNode("/instrumentation/gps/cdi-deflection", true);
@ -255,20 +275,13 @@ FGNavRadio::init ()
void void
FGNavRadio::bind () FGNavRadio::bind ()
{ {
std::ostringstream temp;
string branch;
temp << _num;
branch = "/instrumentation/" + _name + "[" + temp.str() + "]";
} }
void void
FGNavRadio::unbind () FGNavRadio::unbind ()
{ {
std::ostringstream temp;
string branch;
temp << _num;
branch = "/instrumentation/" + _name + "[" + temp.str() + "]";
} }
@ -276,6 +289,10 @@ FGNavRadio::unbind ()
double FGNavRadio::adjustNavRange( double stationElev, double aircraftElev, double FGNavRadio::adjustNavRange( double stationElev, double aircraftElev,
double nominalRange ) double nominalRange )
{ {
if (nominalRange <= 0.0) {
nominalRange = FG_NAV_DEFAULT_RANGE;
}
// extend out actual usable range to be 1.3x the published safe range // extend out actual usable range to be 1.3x the published safe range
const double usability_factor = 1.3; const double usability_factor = 1.3;
@ -382,9 +399,12 @@ void FGNavRadio::clearOutputs()
gs_deflection_node->setDoubleValue( 0.0 ); gs_deflection_node->setDoubleValue( 0.0 );
gs_deflection_deg_node->setDoubleValue(0.0); gs_deflection_deg_node->setDoubleValue(0.0);
gs_deflection_norm_node->setDoubleValue(0.0); gs_deflection_norm_node->setDoubleValue(0.0);
gs_inrange_node->setBoolValue( false );
to_flag_node->setBoolValue( false ); to_flag_node->setBoolValue( false );
from_flag_node->setBoolValue( false ); from_flag_node->setBoolValue( false );
_dmeInRange = false;
} }
void FGNavRadio::updateReceiver(double dt) void FGNavRadio::updateReceiver(double dt)
@ -458,7 +478,7 @@ void FGNavRadio::updateReceiver(double dt)
effective_range effective_range
= adjustNavRange( nav_elev, pos.getElevationM(), _navaid->get_range() ); = adjustNavRange( nav_elev, pos.getElevationM(), _navaid->get_range() );
} }
double effective_range_m = effective_range * SG_NM_TO_METER; double effective_range_m = effective_range * SG_NM_TO_METER;
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
@ -502,12 +522,28 @@ void FGNavRadio::updateReceiver(double dt)
SG_NORMALIZE_RANGE(r, -180.0, 180.0); SG_NORMALIZE_RANGE(r, -180.0, 180.0);
if ( is_loc ) { if ( is_loc ) {
// The factor of 30.0 gives a period of 120 which gives us 3 cycles and six if (_falseCoursesEnabled) {
// zeros i.e. six courses: one front course, one back course, and four // The factor of 30.0 gives a period of 120 which gives us 3 cycles and six
// false courses. Three of the six are reverse sensing. // zeros i.e. six courses: one front course, one back course, and four
_cdiDeflection = 30.0 * sawtooth(r / 30.0); // false courses. Three of the six are reverse sensing.
_cdiDeflection = 30.0 * sawtooth(r / 30.0);
} else {
// no false courses, but we do need to create a back course
if (fabs(r) > 90.0) { // front course
_cdiDeflection = r - copysign(180.0, r);
} else {
_cdiDeflection = r; // back course
}
_cdiDeflection = -_cdiDeflection; // reverse for outbound radial
} // of false courses disabled
const double VOR_FULL_ARC = 20.0; // VOR is -10 .. 10 degree swing const double VOR_FULL_ARC = 20.0; // VOR is -10 .. 10 degree swing
_cdiDeflection *= VOR_FULL_ARC / _localizerWidth; // increased localiser sensitivity _cdiDeflection *= VOR_FULL_ARC / _localizerWidth; // increased localiser sensitivity
if (backcourse_node->getBoolValue()) {
_cdiDeflection = -_cdiDeflection;
}
} else { } else {
// handle the TO side of the VOR // handle the TO side of the VOR
if (fabs(r) > 90.0) { if (fabs(r) > 90.0) {
@ -523,6 +559,7 @@ void FGNavRadio::updateReceiver(double dt)
_cdiCrossTrackErrorM = loc_dist * sin(r * SGD_DEGREES_TO_RADIANS); _cdiCrossTrackErrorM = loc_dist * sin(r * SGD_DEGREES_TO_RADIANS);
updateGlideSlope(dt, aircraft, signal_quality_norm); updateGlideSlope(dt, aircraft, signal_quality_norm);
updateDME(aircraft);
last_loc_dist = loc_dist; last_loc_dist = loc_dist;
} }
@ -532,12 +569,16 @@ void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double sig
_gsNeedleDeflection = 0.0; _gsNeedleDeflection = 0.0;
if (!_gs || !inrange_node->getBoolValue()) { if (!_gs || !inrange_node->getBoolValue()) {
gs_dist_node->setDoubleValue( 0.0 ); gs_dist_node->setDoubleValue( 0.0 );
gs_inrange_node->setBoolValue(false);
return; return;
} }
double gsDist = dist(aircraft, _gsCart); double gsDist = dist(aircraft, _gsCart);
gs_dist_node->setDoubleValue(gsDist); gs_dist_node->setDoubleValue(gsDist);
if (gsDist > (_gs->get_range() * SG_NM_TO_METER)) { bool gsInRange = (gsDist < (_gs->get_range() * SG_NM_TO_METER));
gs_inrange_node->setBoolValue(gsInRange);
if (!gsInRange) {
return; return;
} }
@ -548,21 +589,23 @@ void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double sig
double dot_v = dot(pos, _gsVertical); double dot_v = dot(pos, _gsVertical);
double angle = atan2(dot_v, dot_h) * SGD_RADIANS_TO_DEGREES; double angle = atan2(dot_v, dot_h) * SGD_RADIANS_TO_DEGREES;
double deflectionAngle = target_gs - angle; double deflectionAngle = target_gs - angle;
// Construct false glideslopes. The scale factor of 1.5 if (_falseCoursesEnabled) {
// in the sawtooth gives a period of 6 degrees. // Construct false glideslopes. The scale factor of 1.5
// There will be zeros at 3, 6r, 9, 12r et cetera // in the sawtooth gives a period of 6 degrees.
// where "r" indicates reverse sensing. // There will be zeros at 3, 6r, 9, 12r et cetera
// This is is consistent with conventional pilot lore // where "r" indicates reverse sensing.
// e.g. http://www.allstar.fiu.edu/aerojava/ILS.htm // This is is consistent with conventional pilot lore
// but inconsistent with // e.g. http://www.allstar.fiu.edu/aerojava/ILS.htm
// http://www.freepatentsonline.com/3757338.html // but inconsistent with
// // http://www.freepatentsonline.com/3757338.html
// It may be that some of each exist. //
if (deflectionAngle < 0) { // It may be that some of each exist.
deflectionAngle = 1.5 * sawtooth(deflectionAngle / 1.5); if (deflectionAngle < 0) {
} else { deflectionAngle = 1.5 * sawtooth(deflectionAngle / 1.5);
// no false GS below the true GS } else {
// no false GS below the true GS
}
} }
_gsNeedleDeflection = deflectionAngle * 5.0; _gsNeedleDeflection = deflectionAngle * 5.0;
@ -590,6 +633,17 @@ void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double sig
* horiz_vel * SG_METER_TO_FEET ); * horiz_vel * SG_METER_TO_FEET );
} }
void FGNavRadio::updateDME(const SGVec3d& aircraft)
{
if (!_dme || !dme_serviceable_node->getBoolValue()) {
_dmeInRange = false;
return;
}
double dme_distance = dist(aircraft, _dme->cart());
_dmeInRange = (dme_distance < _dme->get_range() * SG_NM_TO_METER);
}
void FGNavRadio::updateGPSSlaved() void FGNavRadio::updateGPSSlaved()
{ {
has_gs_node->setBoolValue(gps_has_gs_node->getBoolValue()); has_gs_node->setBoolValue(gps_has_gs_node->getBoolValue());
@ -739,25 +793,34 @@ void FGNavRadio::updateAudio()
} else { } else {
SG_LOG( SG_COCKPIT, SG_ALERT, "Can't find nav-dme-ident sound" ); SG_LOG( SG_COCKPIT, SG_ALERT, "Can't find nav-dme-ident sound" );
} }
const int NUM_IDENT_SLOTS = 5;
const time_t SLOT_LENGTH = 5; // seconds
if ( last_time < globals->get_time_params()->get_cur_time() - 30 ) { // There are N slots numbered 0 through (NUM_IDENT_SLOTS-1) inclusive.
last_time = globals->get_time_params()->get_cur_time(); // Each slot is 5 seconds long.
play_count = 0; // Slots 0 is for DME
// the rest are for azimuth.
time_t now = globals->get_time_params()->get_cur_time();
if ((now >= last_time) && (now < last_time + SLOT_LENGTH)) {
return; // wait longer
} }
if ( play_count < 4 ) { last_time = now;
// play VOR ident play_count = ++play_count % NUM_IDENT_SLOTS;
if ( !globals->get_soundmgr()->is_playing(nav_fx_name) ) {
globals->get_soundmgr()->play_once( nav_fx_name ); // Previous ident is out of time; if still playing, cut it off:
++play_count; globals->get_soundmgr()->stop( nav_fx_name );
globals->get_soundmgr()->stop( dme_fx_name );
if (play_count == 0) { // the DME slot
if (_dmeInRange && dme_serviceable_node->getBoolValue()) {
// play DME ident
globals->get_soundmgr()->play_once( dme_fx_name );
}
} else { // NAV slot
if (inrange_node->getBoolValue() && nav_serviceable_node->getBoolValue()) {
globals->get_soundmgr()->play_once(nav_fx_name);
} }
} else if ( play_count < 5 && has_dme) {
// play DME ident
if ( !globals->get_soundmgr()->is_playing(nav_fx_name) &&
!globals->get_soundmgr()->is_playing(dme_fx_name) ) {
globals->get_soundmgr()->play_once( dme_fx_name );
++play_count;
}
} }
} }
@ -787,8 +850,7 @@ void FGNavRadio::search()
_navaid = nav; _navaid = nav;
char identBuffer[5] = " "; char identBuffer[5] = " ";
if (nav) { if (nav) {
FGNavRecord* dme = globals->get_dmelist()->findByFreq(freq, pos); _dme = globals->get_dmelist()->findByFreq(freq, pos);
has_dme = (dme != NULL);
nav_id_node->setStringValue(nav->get_ident()); nav_id_node->setStringValue(nav->get_ident());
strncpy(identBuffer, nav->ident().c_str(), 5); strncpy(identBuffer, nav->ident().c_str(), 5);
@ -800,10 +862,11 @@ void FGNavRadio::search()
if (nav->type() == FGPositioned::VOR) { if (nav->type() == FGPositioned::VOR) {
target_radial = sel_radial_node->getDoubleValue(); target_radial = sel_radial_node->getDoubleValue();
_gs = NULL; _gs = NULL;
has_gs_node->setBoolValue(false);
} else { // ILS or LOC } else { // ILS or LOC
_gs = globals->get_gslist()->findByFreq(freq, pos); _gs = globals->get_gslist()->findByFreq(freq, pos);
_localizerWidth = localizerWidth(nav);
has_gs_node->setBoolValue(_gs != NULL); has_gs_node->setBoolValue(_gs != NULL);
_localizerWidth = localizerWidth(nav);
twist = 0.0; twist = 0.0;
effective_range = nav->get_range(); effective_range = nav->get_range();
@ -814,14 +877,22 @@ void FGNavRadio::search()
int tmp = (int)(_gs->get_multiuse() / 1000.0); int tmp = (int)(_gs->get_multiuse() / 1000.0);
target_gs = (double)tmp / 100.0; target_gs = (double)tmp / 100.0;
// until penaltyForNav goes away, we cannot assume we always pick
// paired LOC/GS trasmsitters. As we pass over a runway threshold, we
// often end up picking the 'wrong' LOC, but the correct GS. To avoid
// breaking the basis computation, ensure we use the GS radial and not
// the (potentially reversed) LOC radial.
double gs_radial = fmod(_gs->get_multiuse(), 1000.0);
SG_NORMALIZE_RANGE(gs_radial, 0.0, 360.0);
// GS axis unit tangent vector // GS axis unit tangent vector
// (along the runway) // (along the runway)
_gsCart = _gs->cart(); _gsCart = _gs->cart();
_gsAxis = tangentVector(_gs->geod(), _gsCart, target_radial); _gsAxis = tangentVector(_gs->geod(), _gsCart, gs_radial);
// GS baseline unit tangent vector // GS baseline unit tangent vector
// (perpendicular to the runay along the ground) // (perpendicular to the runay along the ground)
SGVec3d baseline = tangentVector(_gs->geod(), _gsCart, target_radial + 90.0); SGVec3d baseline = tangentVector(_gs->geod(), _gsCart, gs_radial + 90.0);
_gsVertical = cross(baseline, _gsAxis); _gsVertical = cross(baseline, _gsAxis);
} // of have glideslope } // of have glideslope
} // of found LOC or ILS } // of found LOC or ILS
@ -829,8 +900,8 @@ void FGNavRadio::search()
audioNavidChanged(); audioNavidChanged();
} else { // found nothing } else { // found nothing
_gs = NULL; _gs = NULL;
_dme = NULL;
nav_id_node->setStringValue(""); nav_id_node->setStringValue("");
has_dme = false;
globals->get_soundmgr()->remove( nav_fx_name ); globals->get_soundmgr()->remove( nav_fx_name );
globals->get_soundmgr()->remove( dme_fx_name ); globals->get_soundmgr()->remove( dme_fx_name );
} }

View file

@ -66,7 +66,8 @@ class FGNavRadio : public SGSubsystem
SGPropertyNode_ptr cdi_serviceable_node; SGPropertyNode_ptr cdi_serviceable_node;
SGPropertyNode_ptr gs_serviceable_node; SGPropertyNode_ptr gs_serviceable_node;
SGPropertyNode_ptr tofrom_serviceable_node; SGPropertyNode_ptr tofrom_serviceable_node;
SGPropertyNode_ptr dme_serviceable_node;
// property outputs // property outputs
SGPropertyNode_ptr fmt_freq_node; // formated frequency SGPropertyNode_ptr fmt_freq_node; // formated frequency
SGPropertyNode_ptr fmt_alt_freq_node; // formated alternate frequency SGPropertyNode_ptr fmt_alt_freq_node; // formated alternate frequency
@ -101,6 +102,7 @@ class FGNavRadio : public SGSubsystem
SGPropertyNode_ptr gs_deflection_norm_node; SGPropertyNode_ptr gs_deflection_norm_node;
SGPropertyNode_ptr gs_rate_of_climb_node; SGPropertyNode_ptr gs_rate_of_climb_node;
SGPropertyNode_ptr gs_dist_node; SGPropertyNode_ptr gs_dist_node;
SGPropertyNode_ptr gs_inrange_node;
SGPropertyNode_ptr nav_id_node; SGPropertyNode_ptr nav_id_node;
SGPropertyNode_ptr id_c1_node; SGPropertyNode_ptr id_c1_node;
SGPropertyNode_ptr id_c2_node; SGPropertyNode_ptr id_c2_node;
@ -124,7 +126,6 @@ class FGNavRadio : public SGSubsystem
string nav_fx_name; string nav_fx_name;
string dme_fx_name; string dme_fx_name;
bool has_dme;
double target_radial; double target_radial;
SGTimeStamp prev_time; SGTimeStamp prev_time;
SGTimeStamp curr_time; SGTimeStamp curr_time;
@ -145,6 +146,9 @@ class FGNavRadio : public SGSubsystem
SGVec3d _gsCart, _gsAxis, _gsVertical; SGVec3d _gsCart, _gsAxis, _gsVertical;
FGNavRecordPtr _dme;
bool _dmeInRange;
// CDI properties // CDI properties
bool _toFlag, _fromFlag; bool _toFlag, _fromFlag;
double _cdiDeflection; double _cdiDeflection;
@ -152,6 +156,9 @@ class FGNavRadio : public SGSubsystem
double _gsNeedleDeflection; double _gsNeedleDeflection;
double _gsNeedleDeflectionNorm; double _gsNeedleDeflectionNorm;
// realism setting, are false courses and GS lobes enabled?
bool _falseCoursesEnabled;
bool updateWithPower(double aDt); bool updateWithPower(double aDt);
// model standard VOR/DME/TACAN service volumes as per AIM 1-1-8 // model standard VOR/DME/TACAN service volumes as per AIM 1-1-8
@ -166,6 +173,7 @@ class FGNavRadio : public SGSubsystem
void audioNavidChanged(); void audioNavidChanged();
void updateReceiver(double dt); void updateReceiver(double dt);
void updateDME(const SGVec3d& aircraft);
void updateGlideSlope(double dt, const SGVec3d& aircraft, double signal_quality_norm); void updateGlideSlope(double dt, const SGVec3d& aircraft, double signal_quality_norm);
void updateGPSSlaved(); void updateGPSSlaved();
void updateCDI(double dt); void updateCDI(double dt);