Working on modeling more realistic VOR and ILS ranges.
This commit is contained in:
parent
6e0a10b757
commit
61c8cefcf2
7 changed files with 162 additions and 106 deletions
|
@ -63,65 +63,6 @@ static double kludgeRange ( double stationElev, double aircraftElev,
|
|||
}
|
||||
|
||||
|
||||
// model standard VOR/DME/TACAN service volumes as per AIM 1-1-8
|
||||
static double adjustNavRange( double stationElev, double aircraftElev,
|
||||
double nominalRange )
|
||||
{
|
||||
// assumptions we model the standard service volume, plus
|
||||
// ... rather than specifying a cylinder, we model a cone that
|
||||
// contains the cylinder. Then we put an upside down cone on top
|
||||
// to model diminishing returns at too-high altitudes.
|
||||
|
||||
// altitude difference
|
||||
double alt = ( aircraftElev - stationElev ) * METER_TO_FEET;
|
||||
|
||||
if ( nominalRange < 25.0 + FG_EPSILON ) {
|
||||
// Standard Terminal Service Volume
|
||||
if ( alt <= 1000.0 ) {
|
||||
return nominalRange;
|
||||
} else if ( alt <= 12000.0 ) {
|
||||
return nominalRange + nominalRange * ( alt - 1000.0 ) / 11000.0;
|
||||
} else if ( alt <= 18000.0 ) {
|
||||
return nominalRange * 2 * ( 1 - ( alt - 12000.0 ) / 6000.0 );
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
} else if ( nominalRange < 50.0 + FG_EPSILON ) {
|
||||
// Standard Low Altitude Service Volume
|
||||
if ( alt <= 1000.0 ) {
|
||||
return nominalRange;
|
||||
} else if ( alt <= 18000.0 ) {
|
||||
return nominalRange + nominalRange * ( alt - 1000.0 ) / 17000.0;
|
||||
} else if ( alt <= 18000.0 ) {
|
||||
return nominalRange * 2 * ( 1 - ( alt - 18000.0 ) / 9000.0 );
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
} else {
|
||||
// Standard High Altitude Service Volume
|
||||
double lc = nominalRange * 0.31;
|
||||
double mc = nominalRange * 0.77;
|
||||
double hc = nominalRange;
|
||||
|
||||
if ( alt <= 1000.0 ) {
|
||||
return lc;
|
||||
} else if ( alt <= 14500.0 ) {
|
||||
return lc + (mc-lc) * ( alt - 1000.0 ) / 13500.0;
|
||||
} else if ( alt <= 18000.0 ) {
|
||||
return mc + (hc-mc) * ( alt - 14500.0 ) / 3500.0;
|
||||
} else if ( alt <= 45000.0 ) {
|
||||
return hc + hc * ( 1 - fabs(alt - 31500.0) / 13500.0 );
|
||||
} else if ( alt <= 60000.0 ) {
|
||||
return mc + (hc-mc) * (60000.0 - alt) / 15000.0;
|
||||
} else if ( alt <= 75000.0 ) {
|
||||
return mc * ( 75000.0 - alt ) / 15000.0;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// periodic radio station search wrapper
|
||||
static void fgRadioSearch( void ) {
|
||||
current_radiostack->search();
|
||||
|
@ -155,6 +96,18 @@ FGRadioStack::init ()
|
|||
search();
|
||||
update();
|
||||
|
||||
FGPath path( globals->get_fg_root() );
|
||||
FGPath term = path;
|
||||
term.append( "Navaids/range.term" );
|
||||
FGPath low = path;
|
||||
low.append( "Navaids/range.low" );
|
||||
FGPath high = path;
|
||||
high.append( "Navaids/range.high" );
|
||||
|
||||
term_tbl = new SGInterpTable( term.str() );
|
||||
low_tbl = new SGInterpTable( low.str() );
|
||||
high_tbl = new SGInterpTable( high.str() );
|
||||
|
||||
// Search radio database once per second
|
||||
global_events.Register( "fgRadioSearch()", fgRadioSearch,
|
||||
fgEVENT::FG_EVENT_READY, 1000);
|
||||
|
@ -264,6 +217,58 @@ FGRadioStack::unbind ()
|
|||
fgUntie("/radios/adf/rotation");
|
||||
}
|
||||
|
||||
|
||||
// model standard VOR/DME/TACAN service volumes as per AIM 1-1-8
|
||||
double FGRadioStack::adjustNavRange( double stationElev, double aircraftElev,
|
||||
double nominalRange )
|
||||
{
|
||||
// assumptions we model the standard service volume, plus
|
||||
// ... rather than specifying a cylinder, we model a cone that
|
||||
// contains the cylinder. Then we put an upside down cone on top
|
||||
// to model diminishing returns at too-high altitudes.
|
||||
|
||||
// altitude difference
|
||||
double alt = ( aircraftElev * METER_TO_FEET - stationElev );
|
||||
// cout << "aircraft elev = " << aircraftElev * METER_TO_FEET
|
||||
// << " station elev = " << stationElev << endl;
|
||||
|
||||
if ( nominalRange < 25.0 + FG_EPSILON ) {
|
||||
// Standard Terminal Service Volume
|
||||
return term_tbl->interpolate( alt );
|
||||
} else if ( nominalRange < 50.0 + FG_EPSILON ) {
|
||||
// Standard Low Altitude Service Volume
|
||||
// table is based on range of 40, scale to actual range
|
||||
return low_tbl->interpolate( alt ) * nominalRange / 40.0;
|
||||
} else {
|
||||
// Standard High Altitude Service Volume
|
||||
// table is based on range of 130, scale to actual range
|
||||
return high_tbl->interpolate( alt ) * nominalRange / 130.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// model standard ILS service volumes as per AIM 1-1-9
|
||||
double FGRadioStack::adjustILSRange( double stationElev, double aircraftElev,
|
||||
double offsetDegrees, double distance )
|
||||
{
|
||||
// assumptions we model the standard service volume, plus
|
||||
|
||||
// altitude difference
|
||||
// double alt = ( aircraftElev * METER_TO_FEET - stationElev );
|
||||
double offset = fabs( offsetDegrees );
|
||||
|
||||
if ( offset < 10 ) {
|
||||
return FG_ILS_DEFAULT_RANGE;
|
||||
} else if ( offset < 35 ) {
|
||||
return 10 + (35 - offset) * (FG_ILS_DEFAULT_RANGE - 10) / 25;
|
||||
} else if ( offset < 45 ) {
|
||||
return (45 - offset);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update the various nav values based on position and valid tuned in navs
|
||||
void
|
||||
FGRadioStack::update()
|
||||
|
@ -297,26 +302,39 @@ FGRadioStack::update()
|
|||
nav1_gs_dist = 0.0;
|
||||
}
|
||||
|
||||
nav1_effective_range = kludgeRange(nav1_elev, elev, nav1_range);
|
||||
// wgs84 heading
|
||||
geo_inverse_wgs_84( elev, lat * RAD_TO_DEG, lon * RAD_TO_DEG,
|
||||
nav1_loclat, nav1_loclon,
|
||||
&az1, &az2, &s );
|
||||
// cout << "az1 = " << az1 << " magvar = " << nav1_magvar << endl;
|
||||
nav1_heading = az1 - nav1_magvar;
|
||||
// cout << " heading = " << nav1_heading
|
||||
// << " dist = " << nav1_dist << endl;
|
||||
|
||||
if ( nav1_loc ) {
|
||||
double offset = nav1_heading - nav1_radial;
|
||||
while ( offset < -180.0 ) { offset += 360.0; }
|
||||
while ( offset > 180.0 ) { offset -= 360.0; }
|
||||
cout << "ils offset = " << offset << endl;
|
||||
nav1_effective_range = adjustILSRange(nav1_elev, elev, offset,
|
||||
nav1_loc_dist * METER_TO_NM );
|
||||
} else {
|
||||
nav1_effective_range = adjustNavRange(nav1_elev, elev, nav1_range);
|
||||
}
|
||||
cout << "nav1 range = " << nav1_effective_range
|
||||
<< " (" << nav1_range << ")" << endl;
|
||||
|
||||
if ( nav1_loc_dist < nav1_effective_range * NM_TO_METER ) {
|
||||
nav1_inrange = true;
|
||||
|
||||
// wgs84 heading
|
||||
geo_inverse_wgs_84( elev, lat * RAD_TO_DEG, lon * RAD_TO_DEG,
|
||||
nav1_loclat, nav1_loclon,
|
||||
&az1, &az2, &s );
|
||||
// cout << "az1 = " << az1 << " magvar = " << nav1_magvar << endl;
|
||||
nav1_heading = az1 - nav1_magvar;
|
||||
// Alex: nav1_heading = - (az1 - FGBFI::getMagVar() / RAD_TO_DEG);
|
||||
|
||||
// cout << " heading = " << nav1_heading
|
||||
// << " dist = " << nav1_dist << endl;
|
||||
} else if ( nav1_loc_dist < 2 * nav1_effective_range * NM_TO_METER ) {
|
||||
nav1_inrange = sg_random() <
|
||||
( 2 * nav1_effective_range * NM_TO_METER - nav1_loc_dist ) /
|
||||
(nav1_effective_range * NM_TO_METER);
|
||||
} else {
|
||||
nav1_inrange = false;
|
||||
}
|
||||
|
||||
if ( nav1_loc ) {
|
||||
} else {
|
||||
if ( !nav1_loc ) {
|
||||
nav1_radial = nav1_sel_radial;
|
||||
}
|
||||
} else {
|
||||
|
@ -373,19 +391,33 @@ FGRadioStack::update()
|
|||
nav2_gs_dist = 0.0;
|
||||
}
|
||||
|
||||
nav2_effective_range = kludgeRange(nav2_elev, elev, nav2_range);
|
||||
// wgs84 heading
|
||||
geo_inverse_wgs_84( elev, lat * RAD_TO_DEG, lon * RAD_TO_DEG,
|
||||
nav2_loclat, nav2_loclon,
|
||||
&az1, &az2, &s );
|
||||
nav2_heading = az1 - nav2_magvar;
|
||||
// cout << " heading = " << nav2_heading
|
||||
// << " dist = " << nav2_dist << endl;
|
||||
|
||||
if ( nav2_loc ) {
|
||||
double offset = nav2_heading - nav2_radial;
|
||||
while ( offset < -180.0 ) { offset += 360.0; }
|
||||
while ( offset > 180.0 ) { offset -= 360.0; }
|
||||
cout << "ils offset = " << offset << endl;
|
||||
nav2_effective_range = adjustILSRange(nav2_elev, elev, offset,
|
||||
nav2_loc_dist * METER_TO_NM );
|
||||
} else {
|
||||
nav2_effective_range = adjustNavRange(nav2_elev, elev, nav2_range);
|
||||
}
|
||||
cout << "nav2 range = " << nav2_effective_range
|
||||
<< " (" << nav2_range << ")" << endl;
|
||||
|
||||
if ( nav2_loc_dist < nav2_effective_range * NM_TO_METER ) {
|
||||
nav2_inrange = true;
|
||||
|
||||
// wgs84 heading
|
||||
geo_inverse_wgs_84( elev, lat * RAD_TO_DEG, lon * RAD_TO_DEG,
|
||||
nav2_loclat, nav2_loclon,
|
||||
&az1, &az2, &s );
|
||||
nav2_heading = az1 - nav2_magvar;
|
||||
// Alex: nav2_heading = - (az1 - FGBFI::getMagVar() / RAD_TO_DEG);
|
||||
|
||||
// cout << " heading = " << nav2_heading
|
||||
// << " dist = " << nav2_dist << endl;
|
||||
} else if ( nav2_loc_dist < 2 * nav2_effective_range * NM_TO_METER ) {
|
||||
nav2_inrange = sg_random() <
|
||||
( 2 * nav2_effective_range * NM_TO_METER - nav2_loc_dist ) /
|
||||
(nav2_effective_range * NM_TO_METER);
|
||||
} else {
|
||||
nav2_inrange = false;
|
||||
}
|
||||
|
@ -395,6 +427,7 @@ FGRadioStack::update()
|
|||
}
|
||||
} else {
|
||||
nav2_inrange = false;
|
||||
nav2_dme_dist = 0.0;
|
||||
// cout << "not picking up vor. :-(" << endl;
|
||||
}
|
||||
|
||||
|
@ -404,18 +437,21 @@ FGRadioStack::update()
|
|||
station = Point3D( adf_x, adf_y, adf_z );
|
||||
adf_dist = aircraft.distance3D( station );
|
||||
|
||||
// wgs84 heading
|
||||
geo_inverse_wgs_84( elev, lat * RAD_TO_DEG, lon * RAD_TO_DEG,
|
||||
adf_lat, adf_lon,
|
||||
&az1, &az2, &s );
|
||||
adf_heading = az1;
|
||||
// cout << " heading = " << nav2_heading
|
||||
// << " dist = " << nav2_dist << endl;
|
||||
|
||||
adf_effective_range = kludgeRange(adf_elev, elev, adf_range);
|
||||
if ( adf_dist < adf_effective_range * NM_TO_METER ) {
|
||||
adf_inrange = true;
|
||||
|
||||
// wgs84 heading
|
||||
geo_inverse_wgs_84( elev, lat * RAD_TO_DEG, lon * RAD_TO_DEG,
|
||||
adf_lat, adf_lon,
|
||||
&az1, &az2, &s );
|
||||
adf_heading = az1;
|
||||
|
||||
// cout << " heading = " << nav2_heading
|
||||
// << " dist = " << nav2_dist << endl;
|
||||
} else if ( adf_dist < 2 * adf_effective_range * NM_TO_METER ) {
|
||||
adf_inrange = sg_random() <
|
||||
( 2 * adf_effective_range * NM_TO_METER - adf_dist ) /
|
||||
(adf_effective_range * NM_TO_METER);
|
||||
} else {
|
||||
adf_inrange = false;
|
||||
}
|
||||
|
@ -516,7 +552,7 @@ void FGRadioStack::search()
|
|||
nav1_elev = nav.get_elev();
|
||||
nav1_magvar = nav.get_magvar();
|
||||
nav1_range = nav.get_range();
|
||||
nav1_effective_range = kludgeRange(nav1_elev, elev, nav1_range);
|
||||
nav1_effective_range = adjustNavRange(nav1_elev, elev, nav1_range);
|
||||
nav1_target_gs = 0.0;
|
||||
nav1_radial = nav1_sel_radial;
|
||||
nav1_x = nav1_dme_x = nav.get_x();
|
||||
|
@ -607,7 +643,7 @@ void FGRadioStack::search()
|
|||
nav2_elev = nav.get_elev();
|
||||
nav2_magvar = nav.get_magvar();
|
||||
nav2_range = nav.get_range();
|
||||
nav2_effective_range = kludgeRange(nav2_elev, elev, nav2_range);
|
||||
nav2_effective_range = adjustNavRange(nav2_elev, elev, nav2_range);
|
||||
nav2_target_gs = 0.0;
|
||||
nav2_radial = nav2_sel_radial;
|
||||
nav2_x = nav2_dme_x = nav.get_x();
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <simgear/math/interpolater.hxx>
|
||||
|
||||
#include <Navaids/ilslist.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
#include <Sound/morse.hxx>
|
||||
|
@ -39,6 +41,10 @@ class FGRadioStack : public FGSubsystem
|
|||
{
|
||||
FGMorse morse;
|
||||
|
||||
SGInterpTable *term_tbl;
|
||||
SGInterpTable *low_tbl;
|
||||
SGInterpTable *high_tbl;
|
||||
|
||||
SGValue * latitudeVal;
|
||||
SGValue * longitudeVal;
|
||||
SGValue * altitudeVal;
|
||||
|
@ -137,6 +143,14 @@ class FGRadioStack : public FGSubsystem
|
|||
double adf_y;
|
||||
double adf_z;
|
||||
|
||||
// model standard VOR/DME/TACAN service volumes as per AIM 1-1-8
|
||||
double adjustNavRange( double stationElev, double aircraftElev,
|
||||
double nominalRange );
|
||||
|
||||
// model standard ILS service volumes as per AIM 1-1-9
|
||||
double adjustILSRange( double stationElev, double aircraftElev,
|
||||
double offsetDegrees, double distance );
|
||||
|
||||
public:
|
||||
|
||||
FGRadioStack();
|
||||
|
|
|
@ -44,7 +44,7 @@ FG_USING_STD(istream);
|
|||
#endif
|
||||
|
||||
|
||||
#define FG_ILS_DEFAULT_RANGE 30
|
||||
#define FG_ILS_DEFAULT_RANGE 18
|
||||
|
||||
class FGILS {
|
||||
|
||||
|
|
|
@ -135,8 +135,11 @@ bool FGILSList::query( double lon, double lat, double elev, double freq,
|
|||
// << ")" << endl;
|
||||
|
||||
// cout << " dist = " << s << endl;
|
||||
if ( d < (FG_ILS_DEFAULT_RANGE * NM_TO_METER
|
||||
* FG_ILS_DEFAULT_RANGE * NM_TO_METER) ) {
|
||||
|
||||
// match up to twice the published range so we can model
|
||||
// reduced signal strength
|
||||
if ( d < (2* FG_ILS_DEFAULT_RANGE * NM_TO_METER
|
||||
* 2 * FG_ILS_DEFAULT_RANGE * NM_TO_METER) ) {
|
||||
*ils = *current;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -130,8 +130,11 @@ bool FGNavList::query( double lon, double lat, double elev, double freq,
|
|||
|
||||
// cout << " dist = " << sqrt(d)
|
||||
// << " range = " << current->get_range() * NM_TO_METER << endl;
|
||||
if ( d < (current->get_range() * NM_TO_METER
|
||||
* current->get_range() * NM_TO_METER * 5.0) ) {
|
||||
|
||||
// match up to twice the published range so we can model
|
||||
// reduced signal strength
|
||||
if ( d < (2 * current->get_range() * NM_TO_METER
|
||||
* 2 * current->get_range() * NM_TO_METER ) ) {
|
||||
// cout << "matched = " << current->get_ident() << endl;
|
||||
*n = *current;
|
||||
return true;
|
||||
|
|
|
@ -84,13 +84,13 @@ void fgLIGHT::Init( void ) {
|
|||
sky.append( "Lighting/sky" );
|
||||
|
||||
// initialize ambient table
|
||||
ambient_tbl = new fgINTERPTABLE( ambient.str() );
|
||||
ambient_tbl = new SGInterpTable( ambient.str() );
|
||||
|
||||
// initialize diffuse table
|
||||
diffuse_tbl = new fgINTERPTABLE( diffuse.str() );
|
||||
diffuse_tbl = new SGInterpTable( diffuse.str() );
|
||||
|
||||
// initialize sky table
|
||||
sky_tbl = new fgINTERPTABLE( sky.str() );
|
||||
sky_tbl = new SGInterpTable( sky.str() );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -51,9 +51,9 @@
|
|||
class fgLIGHT {
|
||||
|
||||
// Lighting look up tables (based on sun angle with local horizon)
|
||||
fgINTERPTABLE *ambient_tbl;
|
||||
fgINTERPTABLE *diffuse_tbl;
|
||||
fgINTERPTABLE *sky_tbl;
|
||||
SGInterpTable *ambient_tbl;
|
||||
SGInterpTable *diffuse_tbl;
|
||||
SGInterpTable *sky_tbl;
|
||||
|
||||
public:
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue