b2b33f7582
- FG now directly supports Robin's native nav database file format. - His latest data now separates out dme, gs, loc, and marker beacon transmitters rather than lumping them all into a single "ILS" record. - These new data structure changes prompted me to do some code restructuring so that internally these different types of navaids are all kept as separate lists and searched and handled separately. - This structural change had a cascading affect on any code that references or uses the nav databases. I've gone and "touched" a lot of nav related code in a lot of places. - As an added bonus, the new data (and code) adds DME bias so these will all now read as they do in real life. - Added Navaids/navdb.cxx and Navaids/navdb.hxx which provide a front end loaders for the nav data. - Added Navaids/navrecord.hxx which is a new "generic" nav data record. - Removed Navaids/ils.hxx, Navaids/ilslist.cxx, Navaids/ilslist.hxx, Navaids/mkrbeacons.cxx, and Navaids/mkrbeacons.hxx which are all now depricated.
558 lines
17 KiB
C++
558 lines
17 KiB
C++
// kr-87.cxx -- class to impliment the King KR 87 Digital ADF
|
|
//
|
|
// Written by Curtis Olson, started April 2002.
|
|
//
|
|
// Copyright (C) 2002 Curtis L. Olson - curt@flightgear.org
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
//
|
|
// $Id$
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h> // snprintf
|
|
|
|
#include <simgear/compiler.h>
|
|
#include <simgear/math/sg_random.h>
|
|
|
|
#include <Aircraft/aircraft.hxx>
|
|
#include <Navaids/navlist.hxx>
|
|
|
|
#include "kr_87.hxx"
|
|
|
|
#include <string>
|
|
SG_USING_STD(string);
|
|
|
|
static int play_count = 0;
|
|
static time_t last_time = 0;
|
|
|
|
|
|
/**
|
|
* Boy, this is ugly! Make the VOR range vary by altitude difference.
|
|
*/
|
|
static double kludgeRange ( double stationElev, double aircraftElev,
|
|
double nominalRange)
|
|
{
|
|
// Assume that the nominal range (usually
|
|
// 50nm) applies at a 5,000 ft difference.
|
|
// Just a wild guess!
|
|
double factor = (aircraftElev - stationElev)*SG_METER_TO_FEET / 5000.0;
|
|
double range = fabs(nominalRange * factor);
|
|
|
|
// Clamp the range to keep it sane; for
|
|
// now, never less than 50% or more than
|
|
// 500% of nominal range.
|
|
if (range < nominalRange/2.0) {
|
|
range = nominalRange/2.0;
|
|
} else if (range > nominalRange*5.0) {
|
|
range = nominalRange*5.0;
|
|
}
|
|
|
|
return range;
|
|
}
|
|
|
|
|
|
// Constructor
|
|
FGKR_87::FGKR_87() :
|
|
lon_node(fgGetNode("/position/longitude-deg", true)),
|
|
lat_node(fgGetNode("/position/latitude-deg", true)),
|
|
alt_node(fgGetNode("/position/altitude-ft", true)),
|
|
bus_power(fgGetNode("/systems/electrical/outputs/adf", true)),
|
|
serviceable(fgGetNode("/instrumentation/adf/serviceable", true)),
|
|
need_update(true),
|
|
valid(false),
|
|
inrange(false),
|
|
dist(0.0),
|
|
heading(0.0),
|
|
goal_needle_deg(0.0),
|
|
et_flash_time(0.0),
|
|
ant_mode(0),
|
|
stby_mode(0),
|
|
timer_mode(0),
|
|
count_mode(0),
|
|
rotation(0),
|
|
power_btn(true),
|
|
audio_btn(true),
|
|
vol_btn(0.5),
|
|
adf_btn(true),
|
|
bfo_btn(false),
|
|
frq_btn(false),
|
|
last_frq_btn(false),
|
|
flt_et_btn(false),
|
|
last_flt_et_btn(false),
|
|
set_rst_btn(false),
|
|
last_set_rst_btn(false),
|
|
freq(0),
|
|
stby_freq(0),
|
|
needle_deg(0.0),
|
|
flight_timer(0.0),
|
|
elapsed_timer(0.0),
|
|
tmp_timer(0.0)
|
|
{
|
|
}
|
|
|
|
|
|
// Destructor
|
|
FGKR_87::~FGKR_87() {
|
|
}
|
|
|
|
|
|
void FGKR_87::init () {
|
|
serviceable->setBoolValue( true );
|
|
morse.init();
|
|
}
|
|
|
|
|
|
void FGKR_87::bind () {
|
|
// internal values
|
|
fgTie("/radios/kr-87/internal/valid", this, &FGKR_87::get_valid);
|
|
fgTie("/radios/kr-87/internal/inrange", this, &FGKR_87::get_inrange);
|
|
fgTie("/radios/kr-87/internal/dist", this, &FGKR_87::get_dist);
|
|
fgTie("/radios/kr-87/internal/heading", this, &FGKR_87::get_heading);
|
|
|
|
// modes
|
|
fgTie("/radios/kr-87/modes/ant", this,
|
|
&FGKR_87::get_ant_mode);
|
|
fgTie("/radios/kr-87/modes/stby", this,
|
|
&FGKR_87::get_stby_mode);
|
|
fgTie("/radios/kr-87/modes/timer", this,
|
|
&FGKR_87::get_timer_mode);
|
|
fgTie("/radios/kr-87/modes/count", this,
|
|
&FGKR_87::get_count_mode);
|
|
|
|
// input and buttons
|
|
fgTie("/radios/kr-87/inputs/rotation-deg", this,
|
|
&FGKR_87::get_rotation, &FGKR_87::set_rotation);
|
|
fgSetArchivable("/radios/kr-87/inputs/rotation-deg");
|
|
fgTie("/radios/kr-87/inputs/power-btn", this,
|
|
&FGKR_87::get_power_btn,
|
|
&FGKR_87::set_power_btn);
|
|
fgSetArchivable("/radios/kr-87/inputs/power-btn");
|
|
fgTie("/radios/kr-87/inputs/audio-btn", this,
|
|
&FGKR_87::get_audio_btn,
|
|
&FGKR_87::set_audio_btn);
|
|
fgSetArchivable("/radios/kr-87/inputs/audio-btn");
|
|
fgTie("/radios/kr-87/inputs/volume", this,
|
|
&FGKR_87::get_vol_btn,
|
|
&FGKR_87::set_vol_btn);
|
|
fgSetArchivable("/radios/kr-87/inputs/volume");
|
|
fgTie("/radios/kr-87/inputs/adf-btn", this,
|
|
&FGKR_87::get_adf_btn,
|
|
&FGKR_87::set_adf_btn);
|
|
fgTie("/radios/kr-87/inputs/bfo-btn", this,
|
|
&FGKR_87::get_bfo_btn,
|
|
&FGKR_87::set_bfo_btn);
|
|
fgTie("/radios/kr-87/inputs/frq-btn", this,
|
|
&FGKR_87::get_frq_btn,
|
|
&FGKR_87::set_frq_btn);
|
|
fgTie("/radios/kr-87/inputs/flt-et-btn", this,
|
|
&FGKR_87::get_flt_et_btn,
|
|
&FGKR_87::set_flt_et_btn);
|
|
fgTie("/radios/kr-87/inputs/set-rst-btn", this,
|
|
&FGKR_87::get_set_rst_btn,
|
|
&FGKR_87::set_set_rst_btn);
|
|
|
|
// outputs
|
|
fgTie("/radios/kr-87/outputs/selected-khz", this,
|
|
&FGKR_87::get_freq, &FGKR_87::set_freq);
|
|
fgSetArchivable("/radios/kr-87/outputs/selected-khz");
|
|
fgTie("/radios/kr-87/outputs/standby-khz", this,
|
|
&FGKR_87::get_stby_freq, &FGKR_87::set_stby_freq);
|
|
fgSetArchivable("/radios/kr-87/outputs/standby-khz");
|
|
fgTie("/radios/kr-87/outputs/needle-deg", this,
|
|
&FGKR_87::get_needle_deg);
|
|
fgTie("/radios/kr-87/outputs/flight-timer", this, &FGKR_87::get_flight_timer);
|
|
fgTie("/radios/kr-87/outputs/elapsed-timer", this,
|
|
&FGKR_87::get_elapsed_timer,
|
|
&FGKR_87::set_elapsed_timer);
|
|
|
|
// annunciators
|
|
fgTie("/radios/kr-87/annunciators/ant", this, &FGKR_87::get_ant_ann );
|
|
fgTie("/radios/kr-87/annunciators/adf", this, &FGKR_87::get_adf_ann );
|
|
fgTie("/radios/kr-87/annunciators/bfo", this, &FGKR_87::get_bfo_ann );
|
|
fgTie("/radios/kr-87/annunciators/frq", this, &FGKR_87::get_frq_ann );
|
|
fgTie("/radios/kr-87/annunciators/flt", this, &FGKR_87::get_flt_ann );
|
|
fgTie("/radios/kr-87/annunciators/et", this, &FGKR_87::get_et_ann );
|
|
}
|
|
|
|
|
|
void FGKR_87::unbind () {
|
|
// internal values
|
|
fgUntie("/radios/kr-87/internal/valid");
|
|
fgUntie("/radios/kr-87/internal/inrange");
|
|
fgUntie("/radios/kr-87/internal/dist");
|
|
fgUntie("/radios/kr-87/internal/heading");
|
|
|
|
// modes
|
|
fgUntie("/radios/kr-87/modes/ant");
|
|
fgUntie("/radios/kr-87/modes/stby");
|
|
fgUntie("/radios/kr-87/modes/timer");
|
|
fgUntie("/radios/kr-87/modes/count");
|
|
|
|
// input and buttons
|
|
fgUntie("/radios/kr-87/inputs/rotation-deg");
|
|
fgUntie("/radios/kr-87/inputs/power-btn");
|
|
fgUntie("/radios/kr-87/inputs/volume");
|
|
fgUntie("/radios/kr-87/inputs/adf-btn");
|
|
fgUntie("/radios/kr-87/inputs/bfo-btn");
|
|
fgUntie("/radios/kr-87/inputs/frq-btn");
|
|
fgUntie("/radios/kr-87/inputs/flt-et-btn");
|
|
fgUntie("/radios/kr-87/inputs/set-rst-btn");
|
|
fgUntie("/radios/kr-87/inputs/ident-btn");
|
|
|
|
// outputs
|
|
fgUntie("/radios/kr-87/outputs/selected-khz");
|
|
fgUntie("/radios/kr-87/outputs/standby-khz");
|
|
fgUntie("/radios/kr-87/outputs/needle-deg");
|
|
fgUntie("/radios/kr-87/outputs/flight-timer");
|
|
fgUntie("/radios/kr-87/outputs/elapsed-timer");
|
|
|
|
// annunciators
|
|
fgUntie("/radios/kr-87/annunciators/ant");
|
|
fgUntie("/radios/kr-87/annunciators/adf");
|
|
fgUntie("/radios/kr-87/annunciators/bfo");
|
|
fgUntie("/radios/kr-87/annunciators/frq");
|
|
fgUntie("/radios/kr-87/annunciators/flt");
|
|
fgUntie("/radios/kr-87/annunciators/et");
|
|
}
|
|
|
|
|
|
// Update the various nav values based on position and valid tuned in navs
|
|
void FGKR_87::update( double dt ) {
|
|
double acft_lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
|
|
double acft_lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
|
|
double acft_elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
|
|
|
|
need_update = false;
|
|
|
|
Point3D aircraft = sgGeodToCart( Point3D( acft_lon, acft_lat, acft_elev ) );
|
|
Point3D station;
|
|
double az1, az2, s;
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Radio
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
if ( has_power() && serviceable->getBoolValue() ) {
|
|
// buttons
|
|
if ( adf_btn == 0 ) {
|
|
ant_mode = 1;
|
|
} else {
|
|
ant_mode = 0;
|
|
}
|
|
// cout << "ant_mode = " << ant_mode << endl;
|
|
|
|
if ( frq_btn && frq_btn != last_frq_btn && stby_mode == 0 ) {
|
|
int tmp = freq;
|
|
freq = stby_freq;
|
|
stby_freq = tmp;
|
|
} else if ( frq_btn ) {
|
|
stby_mode = 0;
|
|
count_mode = 0;
|
|
}
|
|
last_frq_btn = frq_btn;
|
|
|
|
if ( flt_et_btn && flt_et_btn != last_flt_et_btn ) {
|
|
if ( stby_mode == 0 ) {
|
|
timer_mode = 0;
|
|
} else {
|
|
timer_mode = !timer_mode;
|
|
}
|
|
stby_mode = 1;
|
|
}
|
|
last_flt_et_btn = flt_et_btn;
|
|
|
|
if ( set_rst_btn == 1 && set_rst_btn != last_set_rst_btn ) {
|
|
// button depressed
|
|
tmp_timer = 0.0;
|
|
}
|
|
if ( set_rst_btn == 1 && set_rst_btn == last_set_rst_btn ) {
|
|
// button depressed and was last iteration too
|
|
tmp_timer += dt;
|
|
// cout << "tmp_timer = " << tmp_timer << endl;
|
|
if ( tmp_timer > 2.0 ) {
|
|
// button held depressed for 2 seconds
|
|
// cout << "entering elapsed count down mode" << endl;
|
|
timer_mode = 1;
|
|
count_mode = 2;
|
|
elapsed_timer = 0.0;
|
|
}
|
|
}
|
|
if ( set_rst_btn == 0 && set_rst_btn != last_set_rst_btn ) {
|
|
// button released
|
|
if ( tmp_timer > 2.0 ) {
|
|
// button held depressed for 2 seconds, don't adjust
|
|
// mode, just exit
|
|
} else if ( count_mode == 2 ) {
|
|
count_mode = 1;
|
|
} else {
|
|
count_mode = 0;
|
|
elapsed_timer = 0.0;
|
|
}
|
|
}
|
|
last_set_rst_btn = set_rst_btn;
|
|
|
|
// timers
|
|
flight_timer += dt;
|
|
|
|
if ( set_rst_btn == 0 ) {
|
|
// only count if set/rst button not depressed
|
|
if ( count_mode == 0 ) {
|
|
elapsed_timer += dt;
|
|
} else if ( count_mode == 1 ) {
|
|
elapsed_timer -= dt;
|
|
if ( elapsed_timer < 1.0 ) {
|
|
count_mode = 0;
|
|
elapsed_timer = 0.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// annunciators
|
|
ant_ann = !adf_btn;
|
|
adf_ann = adf_btn;
|
|
bfo_ann = bfo_btn;
|
|
frq_ann = !stby_mode;
|
|
flt_ann = stby_mode && !timer_mode;
|
|
if ( count_mode < 2 ) {
|
|
et_ann = stby_mode && timer_mode;
|
|
} else {
|
|
et_flash_time += dt;
|
|
if ( et_ann && et_flash_time > 0.5 ) {
|
|
et_ann = false;
|
|
et_flash_time -= 0.5;
|
|
} else if ( !et_ann && et_flash_time > 0.2 ) {
|
|
et_ann = true;
|
|
et_flash_time -= 0.2;
|
|
}
|
|
}
|
|
|
|
if ( valid ) {
|
|
// cout << "adf is valid" << endl;
|
|
// staightline distance
|
|
station = Point3D( x, y, z );
|
|
dist = aircraft.distance3D( station );
|
|
|
|
// wgs84 heading
|
|
geo_inverse_wgs_84( acft_elev,
|
|
acft_lat * SGD_RADIANS_TO_DEGREES,
|
|
acft_lon * SGD_RADIANS_TO_DEGREES,
|
|
stn_lat, stn_lon,
|
|
&az1, &az2, &s );
|
|
heading = az1;
|
|
// cout << " heading = " << heading
|
|
// << " dist = " << dist << endl;
|
|
|
|
effective_range = kludgeRange(stn_elev, acft_elev, range);
|
|
if ( dist < effective_range * SG_NM_TO_METER ) {
|
|
inrange = true;
|
|
} else if ( dist < 2 * effective_range * SG_NM_TO_METER ) {
|
|
inrange = sg_random() <
|
|
( 2 * effective_range * SG_NM_TO_METER - dist ) /
|
|
(effective_range * SG_NM_TO_METER);
|
|
} else {
|
|
inrange = false;
|
|
}
|
|
|
|
// cout << "inrange = " << inrange << endl;
|
|
|
|
if ( inrange ) {
|
|
goal_needle_deg = heading
|
|
- fgGetDouble("/orientation/heading-deg");
|
|
}
|
|
} else {
|
|
inrange = false;
|
|
}
|
|
|
|
if ( ant_mode ) {
|
|
goal_needle_deg = 90.0;
|
|
}
|
|
} else {
|
|
// unit turned off
|
|
goal_needle_deg = 0.0;
|
|
flight_timer = 0.0;
|
|
elapsed_timer = 0.0;
|
|
ant_ann = false;
|
|
adf_ann = false;
|
|
bfo_ann = false;
|
|
frq_ann = false;
|
|
flt_ann = false;
|
|
et_ann = false;
|
|
}
|
|
|
|
// formatted timer
|
|
double time;
|
|
int hours, min, sec;
|
|
if ( timer_mode == 0 ) {
|
|
time = flight_timer;
|
|
} else {
|
|
time = elapsed_timer;
|
|
}
|
|
// cout << time << endl;
|
|
hours = (int)(time / 3600.0);
|
|
time -= hours * 3600.00;
|
|
min = (int)(time / 60.0);
|
|
time -= min * 60.0;
|
|
sec = (int)time;
|
|
int big, little;
|
|
if ( hours > 0 ) {
|
|
big = hours;
|
|
if ( big > 99 ) {
|
|
big = 99;
|
|
}
|
|
little = min;
|
|
} else {
|
|
big = min;
|
|
little = sec;
|
|
}
|
|
if ( big > 99 ) {
|
|
big = 99;
|
|
}
|
|
char formatted_timer[128];
|
|
// cout << big << ":" << little << endl;
|
|
snprintf(formatted_timer, 6, "%02d:%02d", big, little);
|
|
fgSetString( "/radios/kr-87/outputs/timer-string", formatted_timer );
|
|
|
|
while ( goal_needle_deg < 0.0 ) { goal_needle_deg += 360.0; }
|
|
while ( goal_needle_deg >= 360.0 ) { goal_needle_deg -= 360.0; }
|
|
|
|
double diff = goal_needle_deg - needle_deg;
|
|
while ( diff < -180.0 ) { diff += 360.0; }
|
|
while ( diff > 180.0 ) { diff -= 360.0; }
|
|
|
|
needle_deg += diff * dt * 4;
|
|
while ( needle_deg < 0.0 ) { needle_deg += 360.0; }
|
|
while ( needle_deg >= 360.0 ) { needle_deg -= 360.0; }
|
|
|
|
// cout << "goal = " << goal_needle_deg << " actual = " << needle_deg
|
|
// << endl;
|
|
// cout << "flt = " << flight_timer << " et = " << elapsed_timer
|
|
// << " needle = " << needle_deg << endl;
|
|
|
|
if ( valid && inrange && serviceable->getBoolValue() ) {
|
|
// play station ident via audio system if on + ant mode,
|
|
// otherwise turn it off
|
|
if ( vol_btn >= 0.01 && audio_btn ) {
|
|
SGSoundSample *sound;
|
|
sound = globals->get_soundmgr()->find( "adf-ident" );
|
|
if ( sound != NULL ) {
|
|
if ( !adf_btn ) {
|
|
sound->set_volume( vol_btn );
|
|
} else {
|
|
sound->set_volume( vol_btn / 4.0 );
|
|
}
|
|
} else {
|
|
SG_LOG( SG_COCKPIT, SG_ALERT, "Can't find adf-ident sound" );
|
|
}
|
|
if ( last_time <
|
|
globals->get_time_params()->get_cur_time() - 30 ) {
|
|
last_time = globals->get_time_params()->get_cur_time();
|
|
play_count = 0;
|
|
}
|
|
if ( play_count < 4 ) {
|
|
// play ADF ident
|
|
if ( !globals->get_soundmgr()->is_playing("adf-ident") ) {
|
|
globals->get_soundmgr()->play_once( "adf-ident" );
|
|
++play_count;
|
|
}
|
|
}
|
|
} else {
|
|
globals->get_soundmgr()->stop( "adf-ident" );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Update current nav/adf radio stations based on current postition
|
|
void FGKR_87::search() {
|
|
double acft_lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
|
|
double acft_lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
|
|
double acft_elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
|
|
|
|
// FIXME: the panel should handle this
|
|
static string last_ident = "";
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// ADF.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
FGNavRecord *adf
|
|
= globals->get_navlist()->findByFreq( freq, acft_lon, acft_lat,
|
|
acft_elev );
|
|
if ( adf != NULL ) {
|
|
char sfreq[128];
|
|
snprintf( sfreq, 10, "%d", freq );
|
|
ident = sfreq;
|
|
ident += adf->get_ident();
|
|
// cout << "adf ident = " << ident << endl;
|
|
valid = true;
|
|
if ( last_ident != ident ) {
|
|
last_ident = ident;
|
|
|
|
trans_ident = adf->get_trans_ident();
|
|
stn_lon = adf->get_lon();
|
|
stn_lat = adf->get_lat();
|
|
stn_elev = adf->get_elev_ft();
|
|
range = adf->get_range();
|
|
effective_range = kludgeRange(stn_elev, acft_elev, range);
|
|
x = adf->get_x();
|
|
y = adf->get_y();
|
|
z = adf->get_z();
|
|
|
|
if ( globals->get_soundmgr()->exists( "adf-ident" ) ) {
|
|
globals->get_soundmgr()->remove( "adf-ident" );
|
|
}
|
|
SGSoundSample *sound;
|
|
sound = morse.make_ident( trans_ident, LO_FREQUENCY );
|
|
sound->set_volume( 0.3 );
|
|
globals->get_soundmgr()->add( sound, "adf-ident" );
|
|
|
|
int offset = (int)(sg_random() * 30.0);
|
|
play_count = offset / 4;
|
|
last_time = globals->get_time_params()->get_cur_time() -
|
|
offset;
|
|
// cout << "offset = " << offset << " play_count = "
|
|
// << play_count << " last_time = "
|
|
// << last_time << " current time = "
|
|
// << globals->get_time_params()->get_cur_time() << endl;
|
|
|
|
// cout << "Found an adf station in range" << endl;
|
|
// cout << " id = " << nav->get_ident() << endl;
|
|
}
|
|
} else {
|
|
valid = false;
|
|
ident = "";
|
|
trans_ident = "";
|
|
globals->get_soundmgr()->remove( "adf-ident" );
|
|
last_ident = "";
|
|
// cout << "not picking up adf. :-(" << endl;
|
|
}
|
|
}
|
|
|
|
|
|
int FGKR_87::get_stby_freq() const {
|
|
if ( stby_mode == 0 ) {
|
|
return stby_freq;
|
|
} else {
|
|
if ( timer_mode == 0 ) {
|
|
return (int)flight_timer;
|
|
} else {
|
|
return (int)elapsed_timer;
|
|
}
|
|
}
|
|
}
|