Files
flightgear/src/Instrumentation/rnav_waypt_controller.hxx
2022-10-20 20:29:11 +08:00

345 lines
8.4 KiB
C++

// rnav_waypt_controller.hxx - Waypoint-specific behaviours for RNAV systems
// Written by James Turner, started 2009.
//
// Copyright (C) 2009 Curtis L. Olson
//
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef FG_WAYPT_CONTROLLER_HXX
#define FG_WAYPT_CONTROLLER_HXX
#include <Navaids/waypoint.hxx>
#include <simgear/misc/simgear_optional.hxx>
namespace flightgear
{
/**
* Abstract RNAV interface, for devices which implement an RNAV
* system - INS / GPS / FMS
*/
class RNAV
{
public:
virtual SGGeod position() = 0;
/**
* True track in degrees
*/
virtual double trackDeg() = 0;
/**
* Ground speed (along the track) in knots
*/
virtual double groundSpeedKts() = 0;
/**
* Vertical speed in ft/minute
*/
virtual double vspeedFPM()= 0;
/**
* Magnetic variation at current position
*/
virtual double magvarDeg() = 0;
/**
* device selected course (eg, from autopilot / MCP / OBS) in degrees
*/
virtual double selectedMagCourse() = 0;
virtual bool canFlyBy() const
{
return false;
}
/**
* maximum angle in degrees where flyBy is permitted. Turns larger
* than this value will always be executed as fly-over to avoid
* a very large cut.
*/
virtual double maxFlyByTurnAngleDeg() const
{
return 90.0;
}
/**
* minimum distance to switch next waypoint.
*/
virtual double overflightDistanceM() = 0;
/**
* minimum distance to a waypoint for overflight sequencing.
*/
virtual double overflightArmDistanceM() = 0;
/**
* angle for overflight sequencing.
*/
virtual double overflightArmAngleDeg() = 0;
struct LegData {
SGGeod position;
bool didFlyBy = false;
SGGeod flyByTurnCenter;
double flyByRadius = 0.0;
double turnAngle = 0.0;
};
/**
* device leg previous waypoint position(eg, from route manager)
*/
virtual simgear::optional<LegData> previousLegData()
{
return simgear::optional<LegData>();
}
virtual simgear::optional<double> nextLegTrack()
{
return simgear::optional<double>{};
}
/**
* @brief compute turn radius based on current ground-speed
*/
double turnRadiusNm()
{
return turnRadiusNm(groundSpeedKts());
}
/**
* @brief compute the turn radius (based on standard rate turn) for
* a given ground speed in knots.
*/
virtual double turnRadiusNm(const double groundSpeedKnots) = 0;
};
class WayptController
{
public:
virtual ~WayptController();
virtual bool init();
virtual void update(double dt) = 0;
/**
* Compute time until the waypoint is done
*/
virtual double timeToWaypt() const;
/**
* Compute distance until the waypoint is done
*/
virtual double distanceToWayptM() const = 0;
/**
* Bearing to the waypoint, if this value is meaningful.
* Default implementation returns the target track
*/
virtual double trueBearingDeg() const;
virtual double targetTrackDeg() const;
virtual double xtrackErrorNm() const;
virtual double courseDeviationDeg() const;
/**
* Position associated with the waypt. For static waypoints, this is
* simply the waypoint position itself; for dynamic points, it's the
* estimated location at which the controller will be done.
*/
virtual SGGeod position() const = 0;
/**
* Is this controller finished?
*/
bool isDone() const;
/**
* to/from flag - true = to, false = from. Defaults to 'true' because
* nearly all waypoint controllers become done as soon as this value would
* become false.
*/
virtual bool toFlag() const
{ return true; }
/**
* Allow waypoints to indicate a status value as a string.
* Useful for more complex controllers, which may have capture / exit
* states
*/
virtual std::string status() const;
virtual simgear::optional<RNAV::LegData> legData() const
{
// defer to our subcontroller if it exists
if (_subController)
return _subController->legData();
return simgear::optional<RNAV::LegData>();
}
/**
* Static factory method, given a waypoint, return a controller bound
* to it, of the appropriate type
*/
static WayptController* createForWaypt(RNAV* rnav, const WayptRef& aWpt);
WayptRef waypoint() const
{ return _waypt; }
protected:
WayptController(RNAV* aRNAV, const WayptRef& aWpt) :
_waypt(aWpt),
_targetTrack(0),
_rnav(aRNAV),
_isDone(false)
{ }
WayptRef _waypt;
double _targetTrack;
RNAV* _rnav;
void setDone();
// take asubcontroller ref (will be destroyed automatically)
// pass nullptr to clear any activ esubcontroller
// the subcontroller will be initialised
void setSubController(WayptController* sub);
// if a subcontroller exists, we can delegate to it automatically
std::unique_ptr<WayptController> _subController;
private:
bool _isDone;
};
/**
* Controller supports 'directTo' (DTO) navigation to a waypoint. This
* creates a course from a starting point, to the waypoint, and reports
* deviation from that course.
*
* The controller is done when the waypoint is reached (to/from goes to 'from')
*/
class DirectToController : public WayptController
{
public:
DirectToController(RNAV* aRNAV, const WayptRef& aWpt, const SGGeod& aOrigin);
bool init() override;
virtual void update(double dt);
virtual double distanceToWayptM() const;
virtual double xtrackErrorNm() const;
virtual double courseDeviationDeg() const;
virtual double trueBearingDeg() const;
virtual SGGeod position() const;
private:
SGGeod _origin;
double _distanceAircraftTargetMeter;
double _courseDev;
double _courseAircraftToTarget;
};
/**
*
*/
class OBSController : public WayptController
{
public:
OBSController(RNAV* aRNAV, const WayptRef& aWpt);
bool init() override;
virtual void update(double dt);
virtual double distanceToWayptM() const;
virtual double xtrackErrorNm() const;
virtual double courseDeviationDeg() const;
virtual double trueBearingDeg() const;
virtual bool toFlag() const;
virtual SGGeod position() const;
private:
double _distanceAircraftTargetMeter;
double _courseDev;
double _courseAircraftToTarget;
};
class HoldCtl : public WayptController
{
enum HoldState {
HOLD_INIT,
LEG_TO_HOLD,
ENTRY_DIRECT,
ENTRY_PARALLEL, // flying the outbound part of a parallel entry
ENTRY_PARALLEL_OUTBOUND,
ENTRY_PARALLEL_INBOUND,
ENTRY_TEARDROP, // flying the outbound leg of teardrop entry
HOLD_OUTBOUND,
HOLD_INBOUND,
HOLD_EXITING, // we are going to exit the hold
};
HoldState _state = HOLD_INIT;
double _holdCourse = 0.0;
double _holdLegTime = 60.0;
double _holdLegDistance = 0.0;
double _holdCount = 0;
bool _leftHandTurns = false;
bool _inTurn = false;
SGGeod _turnCenter;
double _turnEndAngle, _turnRadius;
SGGeod _segmentEnd;
bool checkOverHold();
void checkInitialEntry(double dNm);
void startInboundTurn();
void startOutboundTurn();
void startParallelEntryTurn();
void exitTurn();
SGGeod outboundEndPoint();
SGGeod outboundTurnCenter();
SGGeod inboundTurnCenter();
double holdLegLengthNm() const;
/**
* are we turning left? This is basically the )leftHandTurns member,
* but if we're in the inbound turn of a parallel entry, it's flipped
*/
bool inLeftTurn() const;
void computeEntry();
public:
HoldCtl(RNAV* aRNAV, const WayptRef& aWpt);
void setHoldCount(int count);
void exitHold();
bool init() override;
void update(double) override;
double distanceToWayptM() const override;
SGGeod position() const override;
double xtrackErrorNm() const override;
double courseDeviationDeg() const override;
std::string status() const override;
};
} // of namespace flightgear
#endif