_turnRate(3.0), // degrees-per-second, so 180 degree turn takes 60 seconds
_overflightArmDistance(0.5),
_waypointAlertTime(30.0),
- _tuneRadio1ToRefVor(true),
+ _tuneRadio1ToRefVor(false),
_minRunwayLengthFt(0.0),
_requireHardSurface(true),
- _cdiMaxDeflectionNm(-1) // default to angular mode
+ _cdiMaxDeflectionNm(-1), // default to angular mode
+ _driveAutopilot(true)
{
_enableTurnAnticipation = false;
- _obsCourseSource = fgGetNode("/instrumentation/nav[0]/radials/selected-deg", true);
+ _extCourseSource = fgGetNode("/instrumentation/nav[0]/radials/selected-deg", true);
}
void GPS::Config::init(SGPropertyNode* aCfgNode)
aCfgNode->tie("min-runway-length-ft", SGRawValuePointer<double>(&_minRunwayLengthFt));
aCfgNode->tie("hard-surface-runways-only", SGRawValuePointer<bool>(&_requireHardSurface));
- aCfgNode->tie("obs-course-source", SGRawValueMethods<GPS::Config, const char*>
- (*this, &GPS::Config::getOBSCourseSource, &GPS::Config::setOBSCourseSource));
+ aCfgNode->tie("course-source", SGRawValueMethods<GPS::Config, const char*>
+ (*this, &GPS::Config::getCourseSource, &GPS::Config::setCourseSource));
aCfgNode->tie("cdi-max-deflection-nm", SGRawValuePointer<double>(&_cdiMaxDeflectionNm));
+ aCfgNode->tie("drive-autopilot", SGRawValuePointer<bool>(&_driveAutopilot));
}
const char*
-GPS::Config::getOBSCourseSource() const
+GPS::Config::getCourseSource() const
{
- if (!_obsCourseSource) {
+ if (!_extCourseSource) {
return "";
}
- return _obsCourseSource->getPath(true);
+ return _extCourseSource->getPath(true);
}
void
-GPS::Config::setOBSCourseSource(const char* aPath)
+GPS::Config::setCourseSource(const char* aPath)
{
SGPropertyNode* nd = fgGetNode(aPath, false);
if (!nd) {
- SG_LOG(SG_INSTR, SG_WARN, "couldn't find OBS course source at:" << aPath);
- _obsCourseSource = NULL;
+ SG_LOG(SG_INSTR, SG_WARN, "couldn't find course source at:" << aPath);
+ _extCourseSource = NULL;
}
- _obsCourseSource = nd;
+ _extCourseSource = nd;
}
double
-GPS::Config::getOBSCourse() const
+GPS::Config::getExternalCourse() const
{
- if (!_obsCourseSource) {
+ if (!_extCourseSource) {
return 0.0;
}
- return _obsCourseSource->getDoubleValue();
+ return _extCourseSource->getDoubleValue();
}
+void
+GPS::Config::setExternalCourse(double aCourseDeg)
+{
+ if (!_extCourseSource) {
+ return;
+ }
+
+ _extCourseSource->setDoubleValue(aCourseDeg);
+}
+
////////////////////////////////////////////////////////////////////////////
GPS::GPS ( SGPropertyNode *node) :
(*this, &GPS::getWP1CourseErrorNm, NULL));
wp1_node->tie("to-flag", SGRawValueMethods<GPS, bool>
(*this, &GPS::getWP1ToFlag, NULL));
-
+ wp1_node->tie("from-flag", SGRawValueMethods<GPS, bool>
+ (*this, &GPS::getWP1FromFlag, NULL));
+
_tracking_bug_node = node->getChild("tracking-bug", 0, true);
// leg properties (only valid in DTO/LEG modes, not OBS)
_route_current_wp_node->addChangeListener(_listener);
_route_active_node = fgGetNode("/autopilot/route-manager/active", true);
_route_active_node->addChangeListener(_listener);
-
_ref_navaid_id_node->addChangeListener(_listener);
-
+
// navradio slaving properties
node->tie("cdi-deflection", SGRawValueMethods<GPS,double>
(*this, &GPS::getCDIDeflection));
SGPropertyNode* toFlag = node->getChild("to-flag", 0, true);
toFlag->alias(wp1_node->getChild("to-flag"));
+
+// autopilot drive properties
+ _apTrueHeading = fgGetNode("/autopilot/settings/true-heading-deg",true);
+ _apTargetAltitudeFt = fgGetNode("/autopilot/settings/target-altitude-ft", true);
+ _apAltitudeLock = fgGetNode("/autopilot/locks/altitude", true);
- _fromFlagNode = node->getChild("from-flag", 0, true);
+// realism prop[s]
+ _realismSimpleGps = fgGetNode("/sim/realism/simple-gps", true);
+ if (!_realismSimpleGps->hasValue()) {
+ _realismSimpleGps->setBoolValue(true);
+ }
// last thing, add the deprecated prop watcher
new DeprecatedPropListener(node);
+
+ // initialise in OBS mode, with waypt set to the nearest airport.
+ // keep in mind at this point, _last_valid is not set
+
+ auto_ptr<FGPositioned::Filter> f(createFilter(FGPositioned::AIRPORT));
+ FGPositionedRef apt = FGPositioned::findClosest(_position.get(), 20.0, f.get());
+ if (apt) {
+ setScratchFromPositioned(apt, 0);
+ selectOBSMode();
+ }
}
void
_last_vertical_speed = 0.0;
_last_true_track = 0.0;
- _raim_node->setDoubleValue(false);
+ _raim_node->setDoubleValue(0.0);
_indicated_pos = SGGeod();
_wp1DistanceM = 0.0;
_wp1TrueBearing = 0.0;
_tracking_bug_node->setDoubleValue(0);
_true_bug_error_node->setDoubleValue(0);
_magnetic_bug_error_node->setDoubleValue(0);
-
- _fromFlagNode->setBoolValue(false);
}
void
GPS::update (double delta_time_sec)
{
- // If it's off, don't bother.
- if (!_serviceable_node->getBoolValue() || !_electrical_node->getBoolValue()) {
- clearOutput();
- return;
+ if (!_realismSimpleGps->getBoolValue()) {
+ // If it's off, don't bother.
+ if (!_serviceable_node->getBoolValue() || !_electrical_node->getBoolValue()) {
+ clearOutput();
+ return;
+ }
}
-
+
if (delta_time_sec <= 0.0) {
return; // paused, don't bother
}
printf("%f %f \n", error_length, error_angle);
*/
- _raim_node->setBoolValue(true);
+ _raim_node->setDoubleValue(1.0);
_indicated_pos = _position.get();
if (_last_valid) {
} else {
_last_valid = true;
- if (_route_active_node->getBoolValue()) {
- // GPS init with active route
- SG_LOG(SG_INSTR, SG_INFO, "GPS init with active route");
- _listener->setGuard(true);
- routeActivated();
- routeManagerSequenced();
- _listener->setGuard(false);
- }
+ if (_route_active_node->getBoolValue()) {
+ // GPS init with active route
+ SG_LOG(SG_INSTR, SG_INFO, "GPS init with active route");
+ _listener->setGuard(true);
+ routeActivated();
+ routeManagerSequenced();
+ _listener->setGuard(false);
+ }
}
_last_pos = _indicated_pos;
updateBasicData(dt);
if (_mode == "obs") {
- _selectedCourse = _config.getOBSCourse();
+ _selectedCourse = _config.getExternalCourse();
} else {
updateTurn();
}
updateTrackingBug();
updateReferenceNavaid(dt);
updateRouteData();
+ driveAutopilot();
}
void
{
double az2;
SGGeodesy::inverse(_indicated_pos, _wp1_position, _wp1TrueBearing, az2,_wp1DistanceM);
- bool toWp = getWP1ToFlag();
- _fromFlagNode->setBoolValue(!toWp);
}
void GPS::updateReferenceNavaid(double dt)
{
if (_route_active_node->getBoolValue()) {
SG_LOG(SG_INSTR, SG_INFO, "GPS::route activated, switching to LEG mode");
- _mode = "leg";
- _computeTurnData = true;
+ selectLegMode();
} else if (_mode == "leg") {
SG_LOG(SG_INSTR, SG_INFO, "GPS::route deactivated, switching to OBS mode");
- _mode = "obs";
+ selectOBSMode();
}
}
_wp1_position = wp1.get_target();
_selectedCourse = getLegMagCourse();
+ wp1Changed();
}
void GPS::updateTurn()
}
}
+void GPS::driveAutopilot()
+{
+ if (!_config.driveAutopilot() || !_realismSimpleGps->getBoolValue()) {
+ return;
+ }
+
+ // FIXME: we want to set desired track, not heading, here
+ _apTrueHeading->setDoubleValue(getWP1Bearing());
+}
+
+void GPS::wp1Changed()
+{
+ // update external HSI/CDI/NavDisplay/PFD/etc
+ _config.setExternalCourse(getLegMagCourse());
+
+ if (!_config.driveAutopilot()) {
+ return;
+ }
+
+ double altFt = _wp1_position.getElevationFt();
+ if (altFt < -9990.0) {
+ _apTargetAltitudeFt->setDoubleValue(0.0);
+ _apAltitudeLock->setBoolValue(false);
+ } else {
+ _apTargetAltitudeFt->setDoubleValue(altFt);
+ _apAltitudeLock->setBoolValue(true);
+ }
+}
+
/////////////////////////////////////////////////////////////////////////////
// property getter/setters
bool GPS::getWP1ToFlag() const
{
if (!_last_valid) {
- return 0.0;
+ return false;
}
double dev = getWP1MagBearing() - _selectedCourse;
return (fabs(dev) < 90.0);
}
+bool GPS::getWP1FromFlag() const
+{
+ if (!_last_valid) {
+ return false;
+ }
+
+ return !getWP1ToFlag();
+}
+
double GPS::getScratchDistance() const
{
if (!_scratchValid) {
_mode = "dto";
_selectedCourse = getLegMagCourse();
clearScratch();
+
+ wp1Changed();
}
void GPS::loadRouteWaypoint()
_wp1Name = _scratchNode->getStringValue("name");
_wp1_position = _scratchPos;
_wp0_position = _indicated_pos;
+ wp1Changed();
}
void GPS::selectLegMode()
double minRunwayLengthFt() const
{ return _minRunwayLengthFt; }
- double getOBSCourse() const;
+ double getExternalCourse() const;
+
+ void setExternalCourse(double aCourseDeg);
bool cdiDeflectionIsAngular() const
{ return (_cdiMaxDeflectionNm <= 0.0); }
assert(_cdiMaxDeflectionNm > 0.0);
return _cdiMaxDeflectionNm;
}
+
+ bool driveAutopilot() const
+ { return _driveAutopilot; }
private:
bool _enableTurnAnticipation;
// should we require a hard-surfaced runway when filtering?
bool _requireHardSurface;
- // helpers to tie obs-course-source property
- const char* getOBSCourseSource() const;
- void setOBSCourseSource(const char* aPropPath);
+ // helpers to tie course-source property
+ const char* getCourseSource() const;
+ void setCourseSource(const char* aPropPath);
- // property to retrieve the OBS course from
- SGPropertyNode_ptr _obsCourseSource;
+ // property to retrieve the external course from
+ SGPropertyNode_ptr _extCourseSource;
double _cdiMaxDeflectionNm;
+
+ bool _driveAutopilot;
};
class SearchFilter : public FGPositioned::Filter
void referenceNavaidSet(const std::string& aNavaid);
void tuneNavRadios();
void updateRouteData();
+ void driveAutopilot();
void routeActivated();
void routeManagerSequenced();
void computeTurnData();
void updateTurnData();
double computeTurnRadiusNm(double aGroundSpeedKts) const;
-
-
+
+ /**
+ * Update one-shot things when WP1 / leg data change
+ */
+ void wp1Changed();
+
// scratch maintenence utilities
void setScratchFromPositioned(FGPositioned* aPos, int aIndex);
void setScratchFromCachedSearchResult();
double getWP1CourseDeviation() const;
double getWP1CourseErrorNm() const;
bool getWP1ToFlag() const;
+ bool getWP1FromFlag() const;
+
// true-bearing-error and mag-bearing-error
SGPropertyNode_ptr _route_current_wp_node;
SGPropertyNode_ptr _routeDistanceNm;
SGPropertyNode_ptr _routeETE;
-
- SGPropertyNode_ptr _fromFlagNode;
-
+
double _selectedCourse;
bool _last_valid;
double _turnRadius; // radius of turn in nm
SGGeod _turnPt;
SGGeod _turnCentre;
+
+ SGPropertyNode_ptr _realismSimpleGps; ///< should the GPS be simple or realistic?
+
+// autopilot drive properties
+ SGPropertyNode_ptr _apTrueHeading;
+ SGPropertyNode_ptr _apTargetAltitudeFt;
+ SGPropertyNode_ptr _apAltitudeLock;
};