Further re-factoring of nav-radio code, again no functionality changed should occur. Radio reception / GPS slaving / Glideslope handling and CDI updating are all separate functions now.
This commit is contained in:
parent
e8db3a514a
commit
e2f0d1960e
2 changed files with 211 additions and 228 deletions
|
@ -299,16 +299,10 @@ double FGNavRadio::adjustILSRange( double stationElev, double aircraftElev,
|
||||||
void
|
void
|
||||||
FGNavRadio::update(double dt)
|
FGNavRadio::update(double dt)
|
||||||
{
|
{
|
||||||
// Do a nav station search only once a second to reduce
|
if (dt <= 0.0) {
|
||||||
// unnecessary work. (Also, make sure to do this before caching
|
return; // paused
|
||||||
// any values!)
|
|
||||||
_time_before_search_sec -= dt;
|
|
||||||
if ( _time_before_search_sec < 0 ) {
|
|
||||||
search();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inrange = false;
|
|
||||||
|
|
||||||
// Create "formatted" versions of the nav frequencies for
|
// Create "formatted" versions of the nav frequencies for
|
||||||
// instrument displays.
|
// instrument displays.
|
||||||
char tmp[16];
|
char tmp[16];
|
||||||
|
@ -317,87 +311,79 @@ FGNavRadio::update(double dt)
|
||||||
sprintf( tmp, "%.2f", alt_freq_node->getDoubleValue() );
|
sprintf( tmp, "%.2f", alt_freq_node->getDoubleValue() );
|
||||||
fmt_alt_freq_node->setStringValue(tmp);
|
fmt_alt_freq_node->setStringValue(tmp);
|
||||||
|
|
||||||
if (_navaid
|
if (power_btn_node->getBoolValue()
|
||||||
&& power_btn_node->getBoolValue()
|
|
||||||
&& (bus_power_node->getDoubleValue() > 1.0)
|
&& (bus_power_node->getDoubleValue() > 1.0)
|
||||||
&& nav_serviceable_node->getBoolValue() )
|
&& nav_serviceable_node->getBoolValue() )
|
||||||
{
|
{
|
||||||
inrange = updateWithPower(dt);
|
if (nav_slaved_to_gps_node->getBoolValue()) {
|
||||||
|
updateGPSSlaved();
|
||||||
|
} else {
|
||||||
|
updateReceiver(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCDI(dt);
|
||||||
} else {
|
} else {
|
||||||
inrange_node->setBoolValue( false );
|
clearOutputs();
|
||||||
cdi_deflection_node->setDoubleValue( 0.0 );
|
|
||||||
cdi_xtrack_error_node->setDoubleValue( 0.0 );
|
|
||||||
cdi_xtrack_hdg_err_node->setDoubleValue( 0.0 );
|
|
||||||
time_to_intercept->setDoubleValue( 0.0 );
|
|
||||||
gs_deflection_node->setDoubleValue( 0.0 );
|
|
||||||
to_flag_node->setBoolValue( false );
|
|
||||||
from_flag_node->setBoolValue( false );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAudio(inrange);
|
updateAudio();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FGNavRadio::updateWithPower(double dt)
|
void FGNavRadio::clearOutputs()
|
||||||
{
|
{
|
||||||
bool nav_serviceable = nav_serviceable_node->getBoolValue();
|
inrange_node->setBoolValue( false );
|
||||||
bool cdi_serviceable = cdi_serviceable_node->getBoolValue();
|
cdi_deflection_node->setDoubleValue( 0.0 );
|
||||||
bool tofrom_serviceable = tofrom_serviceable_node->getBoolValue();
|
cdi_xtrack_error_node->setDoubleValue( 0.0 );
|
||||||
|
cdi_xtrack_hdg_err_node->setDoubleValue( 0.0 );
|
||||||
double az1, az2, s;
|
time_to_intercept->setDoubleValue( 0.0 );
|
||||||
|
gs_deflection_node->setDoubleValue( 0.0 );
|
||||||
|
to_flag_node->setBoolValue( false );
|
||||||
|
from_flag_node->setBoolValue( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGNavRadio::updateReceiver(double dt)
|
||||||
|
{
|
||||||
|
// Do a nav station search only once a second to reduce
|
||||||
|
// unnecessary work. (Also, make sure to do this before caching
|
||||||
|
// any values!)
|
||||||
|
_time_before_search_sec -= dt;
|
||||||
|
if ( _time_before_search_sec < 0 ) {
|
||||||
|
search();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_navaid) {
|
||||||
|
_cdiDeflection = 0.0;
|
||||||
|
_cdiCrossTrackErrorM = 0.0;
|
||||||
|
_toFlag = _fromFlag = false;
|
||||||
|
_gsNeedleDeflection = 0.0;
|
||||||
|
inrange_node->setBoolValue(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SGGeod pos = SGGeod::fromDegFt(lon_node->getDoubleValue(),
|
SGGeod pos = SGGeod::fromDegFt(lon_node->getDoubleValue(),
|
||||||
lat_node->getDoubleValue(),
|
lat_node->getDoubleValue(),
|
||||||
alt_node->getDoubleValue());
|
alt_node->getDoubleValue());
|
||||||
|
|
||||||
bool inrange = false;
|
|
||||||
if ( nav_slaved_to_gps_node->getBoolValue() ) {
|
|
||||||
// FIXME - GPS-slaved GS support in unfinished
|
|
||||||
has_gs_node->setBoolValue(gps_has_gs_node->getBoolValue());
|
|
||||||
inrange = gps_to_flag_node->getBoolValue() || gps_from_flag_node->getBoolValue();
|
|
||||||
} else {
|
|
||||||
inrange = inrange_node->getBoolValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
double nav_elev = _navaid->get_elev_ft();
|
double nav_elev = _navaid->get_elev_ft();
|
||||||
SGVec3d aircraft = SGVec3d::fromGeod(pos);
|
SGVec3d aircraft = SGVec3d::fromGeod(pos);
|
||||||
double loc_dist = dist(aircraft, _navaid->cart());
|
double loc_dist = dist(aircraft, _navaid->cart());
|
||||||
loc_dist_node->setDoubleValue( loc_dist );
|
loc_dist_node->setDoubleValue( loc_dist );
|
||||||
bool is_loc = loc_node->getBoolValue();
|
bool is_loc = loc_node->getBoolValue();
|
||||||
double signal_quality_norm = signal_quality_norm_node->getDoubleValue();
|
double signal_quality_norm = signal_quality_norm_node->getDoubleValue();
|
||||||
|
|
||||||
if (_gs) {
|
double az2, s;
|
||||||
// find closest distance to the gs base line
|
|
||||||
double dist = sgdClosestPointToLineDistSquared(aircraft.data(), _gs->cart().data(),
|
|
||||||
gs_base_vec.data());
|
|
||||||
gs_dist_node->setDoubleValue( sqrt( dist ) );
|
|
||||||
// cout << "gs_dist = " << gs_dist_node->getDoubleValue()
|
|
||||||
// << endl;
|
|
||||||
|
|
||||||
// wgs84 heading to glide slope (to determine sign of distance)
|
|
||||||
SGGeodesy::inverse(pos, _gs->geod(), az1, az2, s);
|
|
||||||
double r = az1 - target_radial;
|
|
||||||
SG_NORMALIZE_RANGE(r, -180.0, 180.0);
|
|
||||||
if (fabs(r) <= 90.0) {
|
|
||||||
gs_dist_signed = gs_dist_node->getDoubleValue();
|
|
||||||
} else {
|
|
||||||
gs_dist_signed = -gs_dist_node->getDoubleValue();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gs_dist_node->setDoubleValue( 0.0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
// compute forward and reverse wgs84 headings to localizer
|
// compute forward and reverse wgs84 headings to localizer
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
double hdg;
|
double hdg;
|
||||||
SGGeodesy::inverse(pos, _navaid->geod(), hdg, az2, s);
|
SGGeodesy::inverse(pos, _navaid->geod(), hdg, az2, s);
|
||||||
heading_node->setDoubleValue( hdg );
|
heading_node->setDoubleValue(hdg);
|
||||||
double radial = az2 - twist;
|
double radial = az2 - twist;
|
||||||
double recip = radial + 180.0;
|
double recip = radial + 180.0;
|
||||||
SG_NORMALIZE_RANGE(recip, 0.0, 360.0);
|
SG_NORMALIZE_RANGE(recip, 0.0, 360.0);
|
||||||
radial_node->setDoubleValue( radial );
|
radial_node->setDoubleValue( radial );
|
||||||
recip_radial_node->setDoubleValue( recip );
|
recip_radial_node->setDoubleValue( recip );
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
// compute the target/selected radial in "true" heading
|
// compute the target/selected radial in "true" heading
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
|
@ -446,133 +432,66 @@ bool FGNavRadio::updateWithPower(double dt)
|
||||||
signal_quality_norm, dt );
|
signal_quality_norm, dt );
|
||||||
|
|
||||||
signal_quality_norm_node->setDoubleValue( signal_quality_norm );
|
signal_quality_norm_node->setDoubleValue( signal_quality_norm );
|
||||||
if ( ! nav_slaved_to_gps_node->getBoolValue() ) {
|
bool inrange = signal_quality_norm > 0.2;
|
||||||
/* not slaved to gps */
|
|
||||||
inrange = signal_quality_norm > 0.2;
|
|
||||||
}
|
|
||||||
inrange_node->setBoolValue( inrange );
|
inrange_node->setBoolValue( inrange );
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
// compute to/from flag status
|
// compute to/from flag status
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
if (tofrom_serviceable) {
|
if (inrange) {
|
||||||
if (nav_slaved_to_gps_node->getBoolValue()) {
|
if (is_loc) {
|
||||||
to_flag_node->setBoolValue(gps_to_flag_node->getBoolValue());
|
_toFlag = true;
|
||||||
from_flag_node->setBoolValue(gps_from_flag_node->getBoolValue());
|
} else {
|
||||||
} else if (inrange) {
|
double offset = fabs(radial - target_radial);
|
||||||
bool toFlag = false;
|
_toFlag = (offset > 90.0 && offset < 270.0);
|
||||||
if (is_loc) {
|
|
||||||
toFlag = true;
|
|
||||||
} else {
|
|
||||||
double offset = fabs(radial - target_radial);
|
|
||||||
toFlag = (offset > 90.0 && offset < 270.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
to_flag_node->setBoolValue(toFlag);
|
|
||||||
from_flag_node->setBoolValue(!toFlag);
|
|
||||||
} else { // out-of-range
|
|
||||||
to_flag_node->setBoolValue(false);
|
|
||||||
from_flag_node->setBoolValue(false);
|
|
||||||
}
|
}
|
||||||
|
_fromFlag = !_toFlag;
|
||||||
} else {
|
} else {
|
||||||
to_flag_node->setBoolValue(false);
|
_toFlag = _fromFlag = false;
|
||||||
from_flag_node->setBoolValue(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
// CDI deflection
|
||||||
// compute the deflection of the CDI needle, clamped to the range
|
double r = radial - target_radial;
|
||||||
// of ( -10 , 10 )
|
SG_NORMALIZE_RANGE(r, -180.0, 180.0);
|
||||||
//////////////////////////////////////////////////////////
|
if ( fabs(r) > 90.0 ) {
|
||||||
double r = 0.0;
|
r = ( r<0.0 ? -r-180.0 : -r+180.0 );
|
||||||
bool loc_backside = false; // an in-code flag indicating that we are
|
|
||||||
// on a localizer backcourse.
|
|
||||||
if ( cdi_serviceable ) {
|
|
||||||
if ( nav_slaved_to_gps_node->getBoolValue() ) {
|
|
||||||
r = gps_cdi_deflection_node->getDoubleValue();
|
|
||||||
// We want +- 5 dots deflection for the gps, so clamp
|
|
||||||
// to -12.5/12.5
|
|
||||||
SG_CLAMP_RANGE( r, -12.5, 12.5 );
|
|
||||||
} else if ( inrange ) {
|
|
||||||
r = radial - target_radial;
|
|
||||||
// cout << "Target radial = " << target_radial
|
|
||||||
// << " Actual radial = " << radial << endl;
|
|
||||||
|
|
||||||
SG_NORMALIZE_RANGE(r, -180.0, 180.0);
|
|
||||||
if ( fabs(r) > 90.0 ) {
|
|
||||||
r = ( r<0.0 ? -r-180.0 : -r+180.0 );
|
|
||||||
} else {
|
|
||||||
if (is_loc) {
|
|
||||||
loc_backside = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r = -r; // reverse, since radial is outbound
|
|
||||||
if ( is_loc ) {
|
|
||||||
// According to Robin Peel, the ILS is 4x more
|
|
||||||
// sensitive than a vor
|
|
||||||
r *= 4.0;
|
|
||||||
}
|
|
||||||
SG_CLAMP_RANGE( r, -10.0, 10.0 );
|
|
||||||
r *= signal_quality_norm;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cdi_deflection_node->setDoubleValue( r );
|
|
||||||
|
r = -r; // reverse, since radial is outbound
|
||||||
//////////////////////////////////////////////////////////
|
_cdiDeflection = r;
|
||||||
// compute the amount of cross track distance error in meters
|
if ( is_loc ) {
|
||||||
//////////////////////////////////////////////////////////
|
// According to Robin Peel, the ILS is 4x more
|
||||||
double xtrack_error = 0.0;
|
// sensitive than a vor
|
||||||
if ( inrange && nav_serviceable && cdi_serviceable ) {
|
// http://www.allstar.fiu.edu/aero/ILS.htm confirms both the 4x sensitvity
|
||||||
r = radial - target_radial;
|
// increase, and also the 'full-deflection is 10-degrees for a VOR' clamp
|
||||||
SG_NORMALIZE_RANGE(r, -180.0, 180.0);
|
_cdiDeflection *= 4.0;
|
||||||
if ( fabs(r) > 90.0 ) {
|
|
||||||
r = ( r<0.0 ? -r-180.0 : -r+180.0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
r = -r; // reverse, since radial is outbound
|
|
||||||
|
|
||||||
xtrack_error = loc_dist * sin(r * SGD_DEGREES_TO_RADIANS);
|
|
||||||
} else {
|
|
||||||
xtrack_error = 0.0;
|
|
||||||
}
|
}
|
||||||
cdi_xtrack_error_node->setDoubleValue( xtrack_error );
|
SG_CLAMP_RANGE(_cdiDeflection, -10.0, 10.0 );
|
||||||
|
_cdiDeflection *= signal_quality_norm;
|
||||||
|
|
||||||
|
// cross-track error (in metres)
|
||||||
|
_cdiCrossTrackErrorM = loc_dist * sin(r * SGD_DEGREES_TO_RADIANS);
|
||||||
|
|
||||||
|
updateGlideSlope(dt, aircraft, signal_quality_norm);
|
||||||
|
|
||||||
|
last_loc_dist = loc_dist;
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double signal_quality_norm)
|
||||||
// compute an approximate ground track heading error
|
{
|
||||||
//////////////////////////////////////////////////////////
|
if (!_gs || !inrange_node->getBoolValue()) {
|
||||||
double hdg_error = 0.0;
|
gs_dist_node->setDoubleValue( 0.0 );
|
||||||
if ( inrange && cdi_serviceable ) {
|
return;
|
||||||
double vn = fgGetDouble( "/velocities/speed-north-fps" );
|
|
||||||
double ve = fgGetDouble( "/velocities/speed-east-fps" );
|
|
||||||
double gnd_trk_true = atan2( ve, vn ) * SGD_RADIANS_TO_DEGREES;
|
|
||||||
if ( gnd_trk_true < 0.0 ) { gnd_trk_true += 360.0; }
|
|
||||||
|
|
||||||
SGPropertyNode *true_hdg
|
|
||||||
= fgGetNode("/orientation/heading-deg", true);
|
|
||||||
hdg_error = gnd_trk_true - true_hdg->getDoubleValue();
|
|
||||||
|
|
||||||
// cout << "ground track = " << gnd_trk_true
|
|
||||||
// << " orientation = " << true_hdg->getDoubleValue() << endl;
|
|
||||||
}
|
}
|
||||||
cdi_xtrack_hdg_err_node->setDoubleValue( hdg_error );
|
|
||||||
|
// find closest distance to the gs base line
|
||||||
//////////////////////////////////////////////////////////
|
double dist = sgdClosestPointToLineDistSquared(aircraft.data(), _gs->cart().data(),
|
||||||
// compute the time to intercept selected radial (based on
|
gs_base_vec.data());
|
||||||
// current and last cross track errors and dt
|
dist = sqrt(dist);
|
||||||
//////////////////////////////////////////////////////////
|
gs_dist_node->setDoubleValue(dist);
|
||||||
if (dt > 0) { // Are we paused?
|
double heightAboveStationM =
|
||||||
double t = 0.0;
|
(alt_node->getDoubleValue() - _gs->elevation()) * SG_FEET_TO_METER;
|
||||||
if ( inrange && cdi_serviceable ) {
|
|
||||||
double xrate_ms = (last_xtrack_error - xtrack_error) / dt;
|
|
||||||
if ( fabs(xrate_ms) > 0.00001 ) {
|
|
||||||
t = xtrack_error / xrate_ms;
|
|
||||||
} else {
|
|
||||||
t = 9999.9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time_to_intercept->setDoubleValue( t );
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
// compute the amount of glide slope needle deflection
|
// compute the amount of glide slope needle deflection
|
||||||
// (.i.e. the number of degrees we are off the glide slope * 5.0
|
// (.i.e. the number of degrees we are off the glide slope * 5.0
|
||||||
|
@ -584,53 +503,87 @@ bool FGNavRadio::updateWithPower(double dt)
|
||||||
// needle animation. This means that the needle should peg
|
// needle animation. This means that the needle should peg
|
||||||
// when this values is +/-3.5.
|
// when this values is +/-3.5.
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
r = 0.0;
|
double angle = atan2(heightAboveStationM, dist) * SGD_RADIANS_TO_DEGREES;
|
||||||
if (_gs && gs_serviceable_node->getBoolValue() ) {
|
double deflectionAngle = target_gs - angle;
|
||||||
if ( nav_slaved_to_gps_node->getBoolValue() ) {
|
//SG_CLAMP_RANGE(deflectionAngle, -0.7, 0.7);
|
||||||
// FIXME what should be set here?
|
_gsNeedleDeflection = deflectionAngle * 5.0;
|
||||||
} else if ( inrange ) {
|
_gsNeedleDeflection *= signal_quality_norm;
|
||||||
double x = gs_dist_node->getDoubleValue();
|
|
||||||
double y = (alt_node->getDoubleValue() - nav_elev)
|
|
||||||
* SG_FEET_TO_METER;
|
|
||||||
// cout << "dist = " << x << " height = " << y << endl;
|
|
||||||
double angle = atan2( y, x ) * SGD_RADIANS_TO_DEGREES;
|
|
||||||
r = (target_gs - angle) * 5.0;
|
|
||||||
r *= signal_quality_norm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gs_deflection_node->setDoubleValue( r );
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
// Calculate desired rate of climb for intercepting the GS
|
// Calculate desired rate of climb for intercepting the GS
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
double x = gs_dist_node->getDoubleValue();
|
double gs_diff = target_gs - angle;
|
||||||
double y = (alt_node->getDoubleValue() - nav_elev)
|
|
||||||
* SG_FEET_TO_METER;
|
|
||||||
double current_angle = atan2( y, x ) * SGD_RADIANS_TO_DEGREES;
|
|
||||||
|
|
||||||
double target_angle = target_gs;
|
|
||||||
double gs_diff = target_angle - current_angle;
|
|
||||||
|
|
||||||
// convert desired vertical path angle into a climb rate
|
// convert desired vertical path angle into a climb rate
|
||||||
double des_angle = current_angle - 10 * gs_diff;
|
double des_angle = angle - 10 * gs_diff;
|
||||||
|
|
||||||
// estimate horizontal speed towards ILS in meters per minute
|
// estimate horizontal speed towards ILS in meters per minute
|
||||||
double dist = last_x - x;
|
double elapsedDistance = last_x - dist;
|
||||||
last_x = x;
|
last_x = dist;
|
||||||
if ( dt > 0.0 ) {
|
|
||||||
// avoid nan
|
double new_vel = ( elapsedDistance / dt );
|
||||||
double new_vel = ( dist / dt );
|
horiz_vel = 0.75 * horiz_vel + 0.25 * new_vel;
|
||||||
|
|
||||||
horiz_vel = 0.75 * horiz_vel + 0.25 * new_vel;
|
gs_rate_of_climb_node
|
||||||
// double horiz_vel = cur_fdm_state->get_V_ground_speed()
|
->setDoubleValue( -sin( des_angle * SGD_DEGREES_TO_RADIANS )
|
||||||
// * SG_FEET_TO_METER * 60.0;
|
* horiz_vel * SG_METER_TO_FEET );
|
||||||
// double horiz_vel = airspeed_node->getFloatValue()
|
}
|
||||||
// * SG_FEET_TO_METER * 60.0;
|
|
||||||
|
|
||||||
gs_rate_of_climb_node
|
void FGNavRadio::updateGPSSlaved()
|
||||||
->setDoubleValue( -sin( des_angle * SGD_DEGREES_TO_RADIANS )
|
{
|
||||||
* horiz_vel * SG_METER_TO_FEET );
|
has_gs_node->setBoolValue(gps_has_gs_node->getBoolValue());
|
||||||
|
|
||||||
|
_toFlag = gps_to_flag_node->getBoolValue();
|
||||||
|
_fromFlag = gps_from_flag_node->getBoolValue();
|
||||||
|
|
||||||
|
inrange_node->setBoolValue(_toFlag | _fromFlag);
|
||||||
|
|
||||||
|
_cdiDeflection = gps_cdi_deflection_node->getDoubleValue();
|
||||||
|
// clmap to some range (+/- 10 degrees) as the regular deflection
|
||||||
|
SG_CLAMP_RANGE(_cdiDeflection, -10.0, 10.0 );
|
||||||
|
|
||||||
|
_cdiCrossTrackErrorM = 0.0; // FIXME, supply this
|
||||||
|
_gsNeedleDeflection = 0.0; // FIXME, supply this
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGNavRadio::updateCDI(double dt)
|
||||||
|
{
|
||||||
|
bool cdi_serviceable = cdi_serviceable_node->getBoolValue();
|
||||||
|
bool inrange = inrange_node->getBoolValue();
|
||||||
|
|
||||||
|
if (tofrom_serviceable_node->getBoolValue()) {
|
||||||
|
to_flag_node->setBoolValue(_toFlag);
|
||||||
|
from_flag_node->setBoolValue(_fromFlag);
|
||||||
|
} else {
|
||||||
|
to_flag_node->setBoolValue(false);
|
||||||
|
from_flag_node->setBoolValue(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!cdi_serviceable) {
|
||||||
|
_cdiDeflection = 0.0;
|
||||||
|
_cdiCrossTrackErrorM = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cdi_deflection_node->setDoubleValue(_cdiDeflection);
|
||||||
|
cdi_xtrack_error_node->setDoubleValue(_cdiCrossTrackErrorM);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
// compute an approximate ground track heading error
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
double hdg_error = 0.0;
|
||||||
|
if ( inrange && cdi_serviceable ) {
|
||||||
|
double vn = fgGetDouble( "/velocities/speed-north-fps" );
|
||||||
|
double ve = fgGetDouble( "/velocities/speed-east-fps" );
|
||||||
|
double gnd_trk_true = atan2( ve, vn ) * SGD_RADIANS_TO_DEGREES;
|
||||||
|
if ( gnd_trk_true < 0.0 ) { gnd_trk_true += 360.0; }
|
||||||
|
|
||||||
|
SGPropertyNode *true_hdg
|
||||||
|
= fgGetNode("/orientation/heading-deg", true);
|
||||||
|
hdg_error = gnd_trk_true - true_hdg->getDoubleValue();
|
||||||
|
|
||||||
|
// cout << "ground track = " << gnd_trk_true
|
||||||
|
// << " orientation = " << true_hdg->getDoubleValue() << endl;
|
||||||
|
}
|
||||||
|
cdi_xtrack_hdg_err_node->setDoubleValue( hdg_error );
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
// Calculate a suggested target heading to smoothly intercept
|
// Calculate a suggested target heading to smoothly intercept
|
||||||
|
@ -644,34 +597,52 @@ bool FGNavRadio::updateWithPower(double dt)
|
||||||
// The cdi deflection should be +/-10 for a full range of deflection
|
// The cdi deflection should be +/-10 for a full range of deflection
|
||||||
// so multiplying this by 3 gives us +/- 30 degrees heading
|
// so multiplying this by 3 gives us +/- 30 degrees heading
|
||||||
// compensation.
|
// compensation.
|
||||||
double adjustment = cdi_deflection_node->getDoubleValue() * 3.0;
|
double adjustment = _cdiDeflection * 3.0;
|
||||||
SG_CLAMP_RANGE( adjustment, -30.0, 30.0 );
|
SG_CLAMP_RANGE( adjustment, -30.0, 30.0 );
|
||||||
|
|
||||||
// determine the target heading to fly to intercept the
|
// determine the target heading to fly to intercept the
|
||||||
// tgt_radial = target radial (true) + cdi offset adjustmest -
|
// tgt_radial = target radial (true) + cdi offset adjustmest -
|
||||||
// xtrack heading error adjustment
|
// xtrack heading error adjustment
|
||||||
double nta_hdg;
|
double nta_hdg;
|
||||||
if ( is_loc && backcourse_node->getBoolValue() ) {
|
double trtrue = target_radial_true_node->getDoubleValue();
|
||||||
|
if ( loc_node->getBoolValue() && backcourse_node->getBoolValue() ) {
|
||||||
// tuned to a localizer and backcourse mode activated
|
// tuned to a localizer and backcourse mode activated
|
||||||
trtrue += 180.0; // reverse the target localizer heading
|
trtrue += 180.0; // reverse the target localizer heading
|
||||||
while ( trtrue > 360.0 ) { trtrue -= 360.0; }
|
SG_NORMALIZE_RANGE(trtrue, 0.0, 360.0);
|
||||||
nta_hdg = trtrue - adjustment - hdg_error;
|
nta_hdg = trtrue - adjustment - hdg_error;
|
||||||
} else {
|
} else {
|
||||||
nta_hdg = trtrue + adjustment - hdg_error;
|
nta_hdg = trtrue + adjustment - hdg_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( nta_hdg < 0.0 ) { nta_hdg += 360.0; }
|
SG_NORMALIZE_RANGE(nta_hdg, 0.0, 360.0);
|
||||||
while ( nta_hdg >= 360.0 ) { nta_hdg -= 360.0; }
|
|
||||||
target_auto_hdg_node->setDoubleValue( nta_hdg );
|
target_auto_hdg_node->setDoubleValue( nta_hdg );
|
||||||
|
|
||||||
last_xtrack_error = xtrack_error;
|
//////////////////////////////////////////////////////////
|
||||||
last_loc_dist = loc_dist;
|
// compute the time to intercept selected radial (based on
|
||||||
return inrange;
|
// current and last cross track errors and dt
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
double t = 0.0;
|
||||||
|
if ( inrange && cdi_serviceable ) {
|
||||||
|
double xrate_ms = (last_xtrack_error - _cdiCrossTrackErrorM) / dt;
|
||||||
|
if ( fabs(xrate_ms) > 0.00001 ) {
|
||||||
|
t = _cdiCrossTrackErrorM / xrate_ms;
|
||||||
|
} else {
|
||||||
|
t = 9999.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time_to_intercept->setDoubleValue( t );
|
||||||
|
|
||||||
|
if (!gs_serviceable_node->getBoolValue() ) {
|
||||||
|
_gsNeedleDeflection = 0.0;
|
||||||
|
}
|
||||||
|
gs_deflection_node->setDoubleValue(_gsNeedleDeflection);
|
||||||
|
|
||||||
|
last_xtrack_error = _cdiCrossTrackErrorM;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGNavRadio::updateAudio(bool aInRange)
|
void FGNavRadio::updateAudio()
|
||||||
{
|
{
|
||||||
if (!_navaid || !aInRange || !nav_serviceable_node->getBoolValue()) {
|
if (!_navaid || !inrange_node->getBoolValue() || !nav_serviceable_node->getBoolValue()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,7 +124,6 @@ class FGNavRadio : public SGSubsystem
|
||||||
bool has_dme;
|
bool has_dme;
|
||||||
double target_radial;
|
double target_radial;
|
||||||
SGVec3d gs_base_vec;
|
SGVec3d gs_base_vec;
|
||||||
double gs_dist_signed;
|
|
||||||
SGTimeStamp prev_time;
|
SGTimeStamp prev_time;
|
||||||
SGTimeStamp curr_time;
|
SGTimeStamp curr_time;
|
||||||
double effective_range;
|
double effective_range;
|
||||||
|
@ -141,6 +140,12 @@ class FGNavRadio : public SGSubsystem
|
||||||
// internal periodic station search timer
|
// internal periodic station search timer
|
||||||
double _time_before_search_sec;
|
double _time_before_search_sec;
|
||||||
|
|
||||||
|
// CDI properties
|
||||||
|
bool _toFlag, _fromFlag;
|
||||||
|
double _cdiDeflection;
|
||||||
|
double _cdiCrossTrackErrorM;
|
||||||
|
double _gsNeedleDeflection;
|
||||||
|
|
||||||
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
|
||||||
|
@ -151,9 +156,16 @@ class FGNavRadio : public SGSubsystem
|
||||||
double adjustILSRange( double stationElev, double aircraftElev,
|
double adjustILSRange( double stationElev, double aircraftElev,
|
||||||
double offsetDegrees, double distance );
|
double offsetDegrees, double distance );
|
||||||
|
|
||||||
void updateAudio(bool aInRange);
|
void updateAudio();
|
||||||
void audioNavidChanged();
|
void audioNavidChanged();
|
||||||
|
|
||||||
|
void updateReceiver(double dt);
|
||||||
|
void updateGlideSlope(double dt, const SGVec3d& aircraft, double signal_quality_norm);
|
||||||
|
void updateGPSSlaved();
|
||||||
|
void updateCDI(double dt);
|
||||||
|
|
||||||
|
void clearOutputs();
|
||||||
|
|
||||||
FGNavRecord* findPrimaryNavaid(const SGGeod& aPos, double aFreqMHz);
|
FGNavRecord* findPrimaryNavaid(const SGGeod& aPos, double aFreqMHz);
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue