#include <fstream>
#include <iostream>
+#include <cstdlib>
using namespace std;
_instrument = instrument;
_nFields = 1;
_maxFields = 2;
-
- // Units - lets default to US units - FG can set them to other units from config during startup if desired.
- _altUnits = GPS_ALT_UNITS_FT;
- _baroUnits = GPS_PRES_UNITS_IN;
- _velUnits = GPS_VEL_UNITS_KT;
- _distUnits = GPS_DIST_UNITS_NM;
_lon_node = fgGetNode("/instrumentation/gps/indicated-longitude-deg", true);
_lat_node = fgGetNode("/instrumentation/gps/indicated-latitude-deg", true);
// Configuration Initialisation
// Should this be in kln89.cxx ?
_turnAnticipationEnabled = false;
- _suaAlertEnabled = false;
- _altAlertEnabled = false;
-
- _time = new SGTime;
-
+
_messageStack.clear();
_dto = false;
}
DCLGPS::~DCLGPS() {
- delete _time;
delete _approachFP; // Don't need to delete the waypoints inside since they point to
// the waypoints in the approach database.
// TODO - may need to delete the approach database!!
}
void DCLGPS::bind() {
- fgTie("/instrumentation/gps/waypoint-alert", this, &DCLGPS::GetWaypointAlert);
- fgTie("/instrumentation/gps/leg-mode", this, &DCLGPS::GetLegMode);
- fgTie("/instrumentation/gps/obs-mode", this, &DCLGPS::GetOBSMode);
- fgTie("/instrumentation/gps/approach-arm", this, &DCLGPS::GetApproachArm);
- fgTie("/instrumentation/gps/approach-active", this, &DCLGPS::GetApproachActive);
- fgTie("/instrumentation/gps/cdi-deflection", this, &DCLGPS::GetCDIDeflection);
- fgTie("/instrumentation/gps/to-flag", this, &DCLGPS::GetToFlag);
+ _tiedProperties.setRoot(fgGetNode("/instrumentation/gps", true));
+ _tiedProperties.Tie("waypoint-alert", this, &DCLGPS::GetWaypointAlert);
+ _tiedProperties.Tie("leg-mode", this, &DCLGPS::GetLegMode);
+ _tiedProperties.Tie("obs-mode", this, &DCLGPS::GetOBSMode);
+ _tiedProperties.Tie("approach-arm", this, &DCLGPS::GetApproachArm);
+ _tiedProperties.Tie("approach-active", this, &DCLGPS::GetApproachActive);
+ _tiedProperties.Tie("cdi-deflection", this, &DCLGPS::GetCDIDeflection);
+ _tiedProperties.Tie("to-flag", this, &DCLGPS::GetToFlag);
}
void DCLGPS::unbind() {
- fgUntie("/instrumentation/gps/waypoint-alert");
- fgUntie("/instrumentation/gps/leg-mode");
- fgUntie("/instrumentation/gps/obs-mode");
- fgUntie("/instrumentation/gps/approach-arm");
- fgUntie("/instrumentation/gps/approach-active");
- fgUntie("/instrumentation/gps/cdi-deflection");
+ _tiedProperties.Untie();
}
void DCLGPS::update(double dt) {
_elapsedTime += dt;
}
- _time->update(_gpsLon * SG_DEGREES_TO_RADIANS, _gpsLat * SG_DEGREES_TO_RADIANS, 0, 0);
// FIXME - currently all the below assumes leg mode and no DTO or OBS cancelled.
if(_activeFP->IsEmpty()) {
// Not sure if we need to reset these each update or only when fp altered
_fromWaypoint = _activeWaypoint;
_activeWaypoint = *_activeFP->waypoints[idx + 1];
_dto = false;
- // TODO - course alteration message format is dependent on whether we are slaved HSI/CDI indicator or not.
- // For now assume we are not.
+ // Course alteration message format is dependent on whether we are slaved HSI/CDI indicator or not.
string s;
if(fgGetBool("/instrumentation/nav[0]/slaved-to-gps")) {
// TODO - avoid the hardwiring on nav[0]
}
/*
- Load instrument approaches from an ARINC 424-18 file.
+ Load instrument approaches from an ARINC 424 file.
+ Tested on ARINC 424-18.
Known / current best guess at the format:
Col 1: Always 'S'. If it isn't, ditch it.
Col 2-4: "Customer area" code, eg "USA", "CAN". I think that CAN is used for Alaska.
Col 107-111 MSA center fix. We can ignore this.
*/
void DCLGPS::LoadApproachData() {
- FGNPIAP* iap;
+ FGNPIAP* iap = NULL;
GPSWaypoint* wp;
GPSFlightPlan* fp;
const GPSWaypoint* cwp;
path.append("Navaids/rnav.dat");
fin.open(path.c_str(), ios::in);
if(!fin) {
- cout << "Unable to open input file " << path.c_str() << '\n';
+ //cout << "Unable to open input file " << path.c_str() << '\n';
return;
} else {
- cout << "Opened " << path.c_str() << " for reading\n";
+ //cout << "Opened " << path.c_str() << " for reading\n";
}
char tmp[256];
string s;
if(last_apt_ident != apt_ident) {
if(iap_in_progress) {
if(iap_error) {
- cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
+ //cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
nErrors++;
} else {
_np_iap[iap->_aptIdent].push_back(iap);
// This is a new approach - store the last one and trigger
// starting afresh by setting the in progress flag to false.
if(iap_error) {
- cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
+ //cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
nErrors++;
} else {
_np_iap[iap->_aptIdent].push_back(iap);
// Note: Currently fgFindAirportID returns NULL on error, but getRunwayByIdent throws an exception.
const FGAirport* apt = fgFindAirportID(iap->_aptIdent);
if(apt) {
+ string rwystr;
try {
- // TODO - sanity check the waypoint ID to ensure we have a double digit number
- FGRunway* rwy = apt->getRunwayByIdent(w.id.substr(2, 2));
+ rwystr = w.id.substr(2, 2);
+ // TODO - sanity check the rwystr at this point to ensure we have a double digit number
+ if(w.id.size() > 4) {
+ if(w.id[4] == 'L' || w.id[4] == 'C' || w.id[4] == 'R') {
+ rwystr += w.id[4];
+ }
+ }
+ FGRunway* rwy = apt->getRunwayByIdent(rwystr);
w.lat = rwy->begin().getLatitudeRad();
w.lon = rwy->begin().getLongitudeRad();
} catch(const sg_exception&) {
- SG_LOG(SG_GENERAL, SG_WARN, "Unable to find runway " << w.id.substr(2, 2) << " at airport " << iap->_aptIdent);
+ SG_LOG(SG_INSTR, SG_WARN, "Unable to find runway " << w.id.substr(2, 2) << " at airport " << iap->_aptIdent);
+ //cout << "Unable to find runway " << w.id.substr(2, 2) << " at airport " << iap->_aptIdent << " ( w.id = " << w.id << ", rwystr = " << rwystr << " )\n";
wp_error = true;
}
} else {
if(iap->_IAP[iap->_IAP.size() - 1]->appType == GPS_APP_NONE) {
iap->_IAP[iap->_IAP.size() - 1]->appType = GPS_MAHP;
} else {
- cout << "Waypoint is MAHP and another type! " << w.id << " [" << apt_ident << ", " << iap_ident << "]\n";
+ //cout << "Waypoint is MAHP and another type! " << w.id << " [" << apt_ident << ", " << iap_ident << "]\n";
}
}
}
// This is a new approach - store the last one and trigger
// starting afresh by setting the in progress flag to false.
if(iap_error) {
- cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
+ //cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
nErrors++;
} else {
_np_iap[iap->_aptIdent].push_back(iap);
// TODO - sanity check that the approach has all the required elements
if(iap_in_progress) {
if(iap_error) {
- cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
+ //cout << "ERROR: Unable to load approach " << iap->_ident << " at " << iap->_aptIdent << '\n';
nErrors++;
} else {
_np_iap[iap->_aptIdent].push_back(iap);
}
}
- cout << "Done loading approach database\n";
- cout << "Loaded: " << nLoaded << '\n';
- cout << "Failed: " << nErrors << '\n';
+ //cout << "Done loading approach database\n";
+ //cout << "Loaded: " << nLoaded << '\n';
+ //cout << "Failed: " << nErrors << '\n';
fin.close();
}
_fromWaypoint.lat = _gpsLat;
_fromWaypoint.lon = _gpsLon;
_fromWaypoint.type = GPS_WP_VIRT;
- _fromWaypoint.id = "DTOWP";
+ _fromWaypoint.id = "_DTOWP_";
delete wp;
} else {
// TODO - Should bring up the user waypoint page.
void DCLGPS::ToggleOBSMode() {
_obsMode = !_obsMode;
if(_obsMode) {
- if(!_activeWaypoint.id.empty()) {
- _obsHeading = static_cast<int>(_dtkMag);
+ // We need to set the OBS heading. The rules for this are:
+ //
+ // If the KLN89 is connected to an external HSI or CDI, then the OBS heading must be set
+ // to the OBS radial on the external indicator, and cannot be changed in the KLN89.
+ //
+ // If the KLN89 is not connected to an external indicator, then:
+ // If there is an active waypoint, the OBS heading is set such that the
+ // deviation indicator remains at the same deviation (i.e. set to DTK,
+ // although there may be some small difference).
+ //
+ // If there is not an active waypoint, I am not sure what value should be
+ // set.
+ //
+ if(fgGetBool("/instrumentation/nav/slaved-to-gps")) {
+ _obsHeading = static_cast<int>(fgGetDouble("/instrumentation/nav/radials/selected-deg") + 0.5);
+ } else if(!_activeWaypoint.id.empty()) {
+ // NOTE: I am not sure if this is completely correct. There are sometimes small differences
+ // between DTK and default OBS heading in the simulator (~ 1 or 2 degrees). It might also
+ // be that OBS heading should be stored as float and only displayed as int, in order to maintain
+ // the initial bar deviation?
+ _obsHeading = static_cast<int>(_dtkMag + 0.5);
+ //cout << "_dtkMag = " << _dtkMag << ", _dtkTrue = " << _dtkTrue << ", _obsHeading = " << _obsHeading << '\n';
+ } else {
+ // TODO - what should we really do here?
+ _obsHeading = 0;
}
+
+ // Valid OBS heading values are 0 -> 359 degrees inclusive (from kln89 simulator).
+ if(_obsHeading > 359) _obsHeading -= 360;
+ if(_obsHeading < 0) _obsHeading += 360;
+
// TODO - the _fromWaypoint location will change as the OBS heading changes.
// Might need to store the OBS initiation position somewhere in case it is needed again.
SetOBSFromWaypoint();
// TODO - base the 180 deg correction on the to/from flag.
_fromWaypoint = GetPositionOnMagRadial(_activeWaypoint, 10, _obsHeading + 180.0);
- _fromWaypoint.id = "OBSWP";
+ _fromWaypoint.type = GPS_WP_VIRT;
+ _fromWaypoint.id = "_OBSWP_";
}
void DCLGPS::CDIFSDIncrease() {
void DCLGPS::DrawText(const string& s, int field, int px, int py, bool bold) {
}
-void DCLGPS::SetBaroUnits(int n, bool wrap) {
- if(n < 1) {
- _baroUnits = (GPSPressureUnits)(wrap ? 3 : 1);
- } else if(n > 3) {
- _baroUnits = (GPSPressureUnits)(wrap ? 1 : 3);
- } else {
- _baroUnits = (GPSPressureUnits)n;
- }
-}
-
void DCLGPS::CreateDefaultFlightPlans() {}
// Get the time to the active waypoint in seconds.
class DCLGPSFilter : public FGPositioned::Filter
{
public:
- virtual bool pass(const FGPositioned* aPos) const {
+ virtual bool pass(FGPositioned* aPos) const
+ {
switch (aPos->type()) {
case FGPositioned::AIRPORT:
// how about heliports and seaports?
GPSWaypoint* DCLGPS::FindFirstById(const string& id) const
{
- DCLGPSFilter filter;
- FGPositionedRef result = FGPositioned::findNextWithPartialId(NULL, id, &filter);
- return GPSWaypoint::createFromPositioned(result);
+ DCLGPSFilter filter;
+ FGPositioned::List matches = FGPositioned::findAllWithIdent(id, &filter, false);
+ if (matches.empty()) {
+ return NULL;
+ }
+
+ FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat));
+ return GPSWaypoint::createFromPositioned(matches.front());
}
GPSWaypoint* DCLGPS::FindFirstByExactId(const string& id) const
multi = false;
FGPositioned::TypeFilter filter(ty);
- if (exact) {
- FGPositioned::List matches =
- FGPositioned::findAllWithIdent(id, &filter);
- FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat));
- multi = (matches.size() > 1);
- return matches.empty() ? NULL : matches.front().ptr();
+ FGPositioned::List matches =
+ FGPositioned::findAllWithIdent(id, &filter, exact);
+ if (matches.empty()) {
+ return NULL;
}
- return FGPositioned::findNextWithPartialId(NULL, id, &filter);
+ FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat));
+ return matches.front();
}
FGNavRecord* DCLGPS::FindFirstVorById(const string& id, bool &multi, bool exact)
h *= SG_RADIANS_TO_DEGREES;
// TODO - use the real altitude below instead of 0.0!
//cout << "MagVar = " << sgGetMagVar(_gpsLon, _gpsLat, 0.0, _time->getJD()) * SG_RADIANS_TO_DEGREES << '\n';
- h -= sgGetMagVar(_gpsLon, _gpsLat, 0.0, _time->getJD()) * SG_RADIANS_TO_DEGREES;
+ double jd = globals->get_time_params()->getJD();
+ h -= sgGetMagVar(_gpsLon, _gpsLat, 0.0, jd) * SG_RADIANS_TO_DEGREES;
while(h >= 360.0) h -= 360.0;
while(h < 0.0) h += 360.0;
return(h);
// Return a position on a radial from wp1 given distance d (nm) and magnetic heading h (degrees)
// Note that d should be less that 1/4 Earth diameter!
GPSWaypoint DCLGPS::GetPositionOnMagRadial(const GPSWaypoint& wp1, double d, double h) {
- h += sgGetMagVar(wp1.lon, wp1.lat, 0.0, _time->getJD()) * SG_RADIANS_TO_DEGREES;
+ double jd = globals->get_time_params()->getJD();
+ h += sgGetMagVar(wp1.lon, wp1.lat, 0.0, jd) * SG_RADIANS_TO_DEGREES;
return(GetPositionOnRadial(wp1, d, h));
}