Flight-path-history.
Record the historical flight-path (by default, since last takeoff). Optionally display the flight-path in the map; other visualisations (e.g., an 'in-world' view) could also be created. Nasal API to follow so Canvas-map or FMS can show the same data. Altitude and attitude (Euler angles) are currently recorded but not used anywhere.
This commit is contained in:
parent
e62649e075
commit
433af2b51a
6 changed files with 302 additions and 1 deletions
|
@ -4,12 +4,14 @@ set(SOURCES
|
|||
controls.cxx
|
||||
replay.cxx
|
||||
flightrecorder.cxx
|
||||
FlightHistory.cxx
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
controls.hxx
|
||||
replay.hxx
|
||||
flightrecorder.hxx
|
||||
FlightHistory.hxx
|
||||
)
|
||||
|
||||
|
||||
|
|
166
src/Aircraft/FlightHistory.cxx
Normal file
166
src/Aircraft/FlightHistory.cxx
Normal file
|
@ -0,0 +1,166 @@
|
|||
// FlightHistory
|
||||
//
|
||||
// Written by James Turner, started December 2012.
|
||||
//
|
||||
// Copyright (C) 2012 James Turner - zakalawe (at) mac com
|
||||
//
|
||||
// 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "FlightHistory.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <simgear/sg_inlines.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
FGFlightHistory::FGFlightHistory() :
|
||||
m_sampleInterval(5.0),
|
||||
m_validSampleCount(SAMPLE_BUCKET_WIDTH)
|
||||
{
|
||||
}
|
||||
|
||||
FGFlightHistory::~FGFlightHistory()
|
||||
{
|
||||
}
|
||||
|
||||
void FGFlightHistory::init()
|
||||
{
|
||||
m_sampleInterval = fgGetDouble("/sim/history/sample-interval-sec", 1.0);
|
||||
if (m_sampleInterval <= 0.0) { // would be bad
|
||||
SG_LOG(SG_FLIGHT, SG_INFO, "invalid flight-history sample interval:" << m_sampleInterval
|
||||
<< ", defaulting to " << m_sampleInterval);
|
||||
m_sampleInterval = 1.0;
|
||||
}
|
||||
|
||||
m_weightOnWheels = NULL;
|
||||
// reset the history when we detect a take-off
|
||||
if (fgGetBool("/sim/history/clear-on-takeoff", true)) {
|
||||
m_weightOnWheels = fgGetNode("/gear/gear[1]/wow", 0, true);
|
||||
m_lastWoW = m_weightOnWheels->getBoolValue();
|
||||
}
|
||||
|
||||
// force bucket re-allocation
|
||||
m_validSampleCount = SAMPLE_BUCKET_WIDTH;
|
||||
}
|
||||
|
||||
void FGFlightHistory::shutdown()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void FGFlightHistory::reinit()
|
||||
{
|
||||
shutdown();
|
||||
init();
|
||||
}
|
||||
|
||||
void FGFlightHistory::update(double dt)
|
||||
{
|
||||
SG_UNUSED(dt); // we care about sim-time, not frame-time
|
||||
|
||||
if (m_weightOnWheels) {
|
||||
|
||||
if (m_lastWoW && !m_weightOnWheels->getBoolValue()) {
|
||||
SG_LOG(SG_FLIGHT, SG_INFO, "history: detected main-gear takeoff, clearing history");
|
||||
clear();
|
||||
}
|
||||
} // of rest-on-takeoff enabled
|
||||
|
||||
double elapsed = globals->get_sim_time_sec() - m_lastCaptureTime;
|
||||
if (elapsed > m_sampleInterval) {
|
||||
capture();
|
||||
}
|
||||
}
|
||||
|
||||
void FGFlightHistory::allocateNewBucket()
|
||||
{
|
||||
SampleBucket* bucket = new SampleBucket;
|
||||
m_buckets.push_back(bucket);
|
||||
m_validSampleCount = 0;
|
||||
}
|
||||
|
||||
void FGFlightHistory::capture()
|
||||
{
|
||||
if (m_validSampleCount == SAMPLE_BUCKET_WIDTH) {
|
||||
// bucket is full, allocate a new one
|
||||
allocateNewBucket();
|
||||
}
|
||||
|
||||
m_lastCaptureTime = globals->get_sim_time_sec();
|
||||
Sample* sample = m_buckets.back()->samples + m_validSampleCount;
|
||||
|
||||
sample->simTimeMSec = static_cast<int>(m_lastCaptureTime * 1000.0);
|
||||
sample->position = globals->get_aircraft_position();
|
||||
|
||||
double heading, pitch, roll;
|
||||
globals->get_aircraft_orientation(heading, pitch, roll);
|
||||
sample->heading = static_cast<float>(heading);
|
||||
sample->pitch = static_cast<float>(pitch);
|
||||
sample->roll = static_cast<float>(roll);
|
||||
|
||||
++m_validSampleCount;
|
||||
}
|
||||
|
||||
SGGeodVec FGFlightHistory::pathForHistory(double minEdgeLengthM) const
|
||||
{
|
||||
SGGeodVec result;
|
||||
if (m_buckets.empty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.push_back(m_buckets.front()->samples[0].position);
|
||||
SGVec3d lastOutputCart = SGVec3d::fromGeod(result.back());
|
||||
double minLengthSqr = minEdgeLengthM * minEdgeLengthM;
|
||||
|
||||
BOOST_FOREACH(SampleBucket* bucket, m_buckets) {
|
||||
unsigned int count = (bucket == m_buckets.back() ? m_validSampleCount : SAMPLE_BUCKET_WIDTH);
|
||||
|
||||
// iterate over all the valid samples in the bucket
|
||||
for (unsigned int index = 0; index < count; ++index) {
|
||||
SGGeod g = bucket->samples[index].position;
|
||||
SGVec3d cart(SGVec3d::fromGeod(g));
|
||||
if (distSqr(cart, lastOutputCart) > minLengthSqr) {
|
||||
lastOutputCart = cart;
|
||||
result.push_back(g);
|
||||
}
|
||||
} // of samples iteration
|
||||
} // of buckets iteration
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void FGFlightHistory::clear()
|
||||
{
|
||||
BOOST_FOREACH(SampleBucket* ptr, m_buckets) {
|
||||
delete ptr;
|
||||
}
|
||||
m_buckets.clear();
|
||||
m_validSampleCount = SAMPLE_BUCKET_WIDTH;
|
||||
}
|
||||
|
105
src/Aircraft/FlightHistory.hxx
Normal file
105
src/Aircraft/FlightHistory.hxx
Normal file
|
@ -0,0 +1,105 @@
|
|||
// FlightHistory
|
||||
//
|
||||
// Written by James Turner, started December 2012.
|
||||
//
|
||||
// Copyright (C) 2012 James Turner - zakalawe (at) mac com
|
||||
//
|
||||
// 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef FG_AIRCRAFT_FLIGHT_HISTORY_HXX
|
||||
#define FG_AIRCRAFT_FLIGHT_HISTORY_HXX
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include <vector>
|
||||
#include <memory> // for std::auto_ptr
|
||||
|
||||
typedef std::vector<SGGeod> SGGeodVec;
|
||||
|
||||
/**
|
||||
* record the history of the aircraft's movements, making it available
|
||||
* as a contiguous block. This can be used to show the historical flight-path
|
||||
* over a long period of time (unlike the replay system), but only a small,
|
||||
* fixed set of properties are recorded. (Positioned and orientation, but
|
||||
* not velocity, acceleration, control inputs, or so on)
|
||||
*/
|
||||
class FGFlightHistory : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
FGFlightHistory();
|
||||
virtual ~FGFlightHistory();
|
||||
|
||||
virtual void init();
|
||||
virtual void shutdown();
|
||||
virtual void reinit();
|
||||
virtual void update(double dt);
|
||||
|
||||
/**
|
||||
* retrieve the path, collapsing segments shorter than
|
||||
* the specified minimum length
|
||||
*/
|
||||
SGGeodVec pathForHistory(double minEdgeLengthM = 50.0) const;
|
||||
private:
|
||||
/**
|
||||
* @class A single data sample in the history system.
|
||||
*/
|
||||
class Sample
|
||||
{
|
||||
public:
|
||||
SGGeod position;
|
||||
/// heading, pitch and roll can be recorded at lower precision
|
||||
/// than a double - actually 16 bits might be sufficient
|
||||
float heading, pitch, roll;
|
||||
int simTimeMSec;
|
||||
};
|
||||
|
||||
static const int SAMPLE_BUCKET_WIDTH = 1024;
|
||||
|
||||
/**
|
||||
* Bucket is a fixed-size container of samples. This is a crude slab
|
||||
* allocation of samples, in chunks defined by the width constant above.
|
||||
* Keep in mind that even with a 1Hz sample frequency, we use less than
|
||||
* 200kbytes per hour - avoiding continous malloc traffic, or expensive
|
||||
* std::vector reallocations, is the key factor here.
|
||||
*/
|
||||
class SampleBucket
|
||||
{
|
||||
public:
|
||||
Sample samples[SAMPLE_BUCKET_WIDTH];
|
||||
};
|
||||
|
||||
double m_lastCaptureTime;
|
||||
double m_sampleInterval; ///< sample interval in seconds
|
||||
/// our store of samples (in buckets). The last bucket is partially full,
|
||||
/// with the number of valid samples indicated by m_validSampleCount
|
||||
std::vector<SampleBucket*> m_buckets;
|
||||
|
||||
/// number of valid samples in the final bucket
|
||||
unsigned int m_validSampleCount;
|
||||
|
||||
SGPropertyNode_ptr m_weightOnWheels;
|
||||
bool m_lastWoW;
|
||||
|
||||
void allocateNewBucket();
|
||||
|
||||
void clear();
|
||||
void capture();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -25,6 +25,7 @@
|
|||
#include <Airports/runways.hxx>
|
||||
#include <Main/fg_os.hxx> // fgGetKeyModifiers()
|
||||
#include <Navaids/routePath.hxx>
|
||||
#include <Aircraft/FlightHistory.hxx>
|
||||
|
||||
const char* RULER_LEGEND_KEY = "ruler-legend";
|
||||
|
||||
|
@ -411,6 +412,7 @@ void MapWidget::setProperty(SGPropertyNode_ptr prop)
|
|||
_root->setIntValue("max-zoom", MAX_ZOOM);
|
||||
_root->setBoolValue("centre-on-aircraft", true);
|
||||
_root->setBoolValue("draw-data", false);
|
||||
_root->setBoolValue("draw-flight-history", false);
|
||||
_root->setBoolValue("magnetic-headings", true);
|
||||
}
|
||||
|
||||
|
@ -611,6 +613,7 @@ void MapWidget::draw(int dx, int dy)
|
|||
drawNavRadio(fgGetNode("/instrumentation/nav[0]", false));
|
||||
drawNavRadio(fgGetNode("/instrumentation/nav[1]", false));
|
||||
paintAircraftLocation(_aircraft);
|
||||
drawFlightHistory();
|
||||
paintRoute();
|
||||
paintRuler();
|
||||
|
||||
|
@ -748,6 +751,28 @@ void MapWidget::paintRoute()
|
|||
} // of second waypoint iteration
|
||||
}
|
||||
|
||||
void MapWidget::drawFlightHistory()
|
||||
{
|
||||
FGFlightHistory* history = (FGFlightHistory*) globals->get_subsystem("history");
|
||||
if (!history || !_root->getBoolValue("draw-flight-history")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// first pass, draw the actual lines
|
||||
glLineWidth(2.0);
|
||||
|
||||
SGGeodVec gv(history->pathForHistory());
|
||||
glColor4f(0.0, 0.0, 1.0, 0.7);
|
||||
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for (unsigned int i=0; i<gv.size(); ++i) {
|
||||
SGVec2d p = project(gv[i]);
|
||||
glVertex2d(p.x(), p.y());
|
||||
}
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Round a SGGeod to an arbitrary precision.
|
||||
* For example, passing precision of 0.5 will round to the nearest 0.5 of
|
||||
|
|
|
@ -43,6 +43,7 @@ private:
|
|||
void paintAircraftLocation(const SGGeod& aircraftPos);
|
||||
void paintRoute();
|
||||
void paintRuler();
|
||||
void drawFlightHistory();
|
||||
|
||||
void drawGPSData();
|
||||
void drawNavRadio(SGPropertyNode_ptr radio);
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
|
||||
#include <Aircraft/controls.hxx>
|
||||
#include <Aircraft/replay.hxx>
|
||||
#include <Aircraft/FlightHistory.hxx>
|
||||
#include <Airports/runways.hxx>
|
||||
#include <Airports/simple.hxx>
|
||||
#include <Airports/dynamics.hxx>
|
||||
|
@ -711,6 +712,7 @@ void fgCreateSubsystems() {
|
|||
// Initialize the replay subsystem
|
||||
////////////////////////////////////////////////////////////////////
|
||||
globals->add_subsystem("replay", new FGReplay);
|
||||
globals->add_subsystem("history", new FGFlightHistory);
|
||||
|
||||
#ifdef ENABLE_AUDIO_SUPPORT
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in a new issue