1
0
Fork 0

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:
James Turner 2012-12-10 18:11:44 +00:00
parent e62649e075
commit 433af2b51a
6 changed files with 302 additions and 1 deletions

View file

@ -4,12 +4,14 @@ set(SOURCES
controls.cxx
replay.cxx
flightrecorder.cxx
FlightHistory.cxx
)
set(HEADERS
controls.hxx
replay.hxx
flightrecorder.hxx
FlightHistory.hxx
)

View 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;
}

View 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

View file

@ -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

View file

@ -43,6 +43,7 @@ private:
void paintAircraftLocation(const SGGeod& aircraftPos);
void paintRoute();
void paintRuler();
void drawFlightHistory();
void drawGPSData();
void drawNavRadio(SGPropertyNode_ptr radio);

View file

@ -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,7 +712,8 @@ void fgCreateSubsystems() {
// Initialize the replay subsystem
////////////////////////////////////////////////////////////////////
globals->add_subsystem("replay", new FGReplay);
globals->add_subsystem("history", new FGFlightHistory);
#ifdef ENABLE_AUDIO_SUPPORT
////////////////////////////////////////////////////////////////////
// Initialize the sound-effects subsystem.