FlightGear next
FlightHistory.cxx
Go to the documentation of this file.
1// FlightHistory
2//
3// Written by James Turner, started December 2012.
4//
5// Copyright (C) 2012 James Turner - zakalawe (at) mac com
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License as
9// published by the Free Software Foundation; either version 2 of the
10// License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful, but
13// WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15// General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20//
22
23#ifdef HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#include "FlightHistory.hxx"
28
29#include <algorithm>
30
31#include <simgear/sg_inlines.h>
32#include <simgear/debug/logstream.hxx>
33#include <simgear/props/props_io.hxx>
34#include <simgear/misc/strutils.hxx>
35#include <simgear/structure/exception.hxx>
36#include <simgear/math/SGMath.hxx>
37
38#include <Main/fg_props.hxx>
39#include <Main/globals.hxx>
40
42 m_sampleInterval(5.0),
43 m_validSampleCount(SAMPLE_BUCKET_WIDTH)
44{
45}
46
50
52{
53 m_enabled = fgGetNode("/sim/history/enabled", true);
54 m_sampleInterval = fgGetDouble("/sim/history/sample-interval-sec", 1.0);
55 if (m_sampleInterval <= 0.0) { // would be bad
56 SG_LOG(SG_FLIGHT, SG_INFO, "invalid flight-history sample interval:" << m_sampleInterval
57 << ", defaulting to " << m_sampleInterval);
58 m_sampleInterval = 1.0;
59 }
60
61 // cap memory use at 4MB
62 m_maxMemoryUseBytes = fgGetInt("/sim/history/max-memory-use-bytes", 1024 * 1024 * 4);
63 m_weightOnWheels = NULL;
64// reset the history when we detect a take-off
65 if (fgGetBool("/sim/history/clear-on-takeoff", true)) {
66 m_weightOnWheels = fgGetNode("/gear/gear[1]/wow", 0, true);
67 m_lastWoW = m_weightOnWheels->getBoolValue();
68 }
69
70 // force bucket re-allocation
71 m_validSampleCount = SAMPLE_BUCKET_WIDTH;
72 m_lastCaptureTime = globals->get_sim_time_sec();
73}
74
76{
77 clear();
78}
79
81{
82 shutdown();
83 init();
84}
85
87{
88 if ((dt == 0.0) || !m_enabled->getBoolValue()) {
89 return; // paused or disabled
90 }
91
92 if (m_weightOnWheels) {
93
94 if (m_lastWoW && !m_weightOnWheels->getBoolValue()) {
95 SG_LOG(SG_FLIGHT, SG_INFO, "history: detected main-gear takeoff, clearing history");
96 clear();
97 }
98 } // of rest-on-takeoff enabled
99
100// spatial check - moved at least 1m since last capture
101 if (!m_buckets.empty()) {
102 SGVec3d lastCaptureCart(SGVec3d::fromGeod(m_buckets.back()->samples[m_validSampleCount - 1].position));
103 double d2 = distSqr(lastCaptureCart, globals->get_aircraft_position_cart());
104 if (d2 <= 1.0) {
105 return;
106 }
107 }
108
109 double elapsed = globals->get_sim_time_sec() - m_lastCaptureTime;
110 if (elapsed > m_sampleInterval) {
111 capture();
112 }
113}
114
115void FGFlightHistory::allocateNewBucket()
116{
117 SampleBucket* bucket = NULL;
118 if (!m_buckets.empty() && (currentMemoryUseBytes() > m_maxMemoryUseBytes)) {
119 bucket = m_buckets.front();
120 m_buckets.erase(m_buckets.begin());
121 } else {
122 bucket = new SampleBucket;
123 }
124
125 m_buckets.push_back(bucket);
126 m_validSampleCount = 0;
127}
128
129void FGFlightHistory::capture()
130{
131 if (m_validSampleCount == SAMPLE_BUCKET_WIDTH) {
132 // bucket is full, allocate a new one
133 allocateNewBucket();
134 }
135
136 m_lastCaptureTime = globals->get_sim_time_sec();
137 Sample* sample = m_buckets.back()->samples + m_validSampleCount;
138
139 sample->simTimeMSec = static_cast<size_t>(m_lastCaptureTime * 1000.0);
140 sample->position = globals->get_aircraft_position();
141
142 double heading, pitch, roll;
143 globals->get_aircraft_orientation(heading, pitch, roll);
144 sample->heading = static_cast<float>(heading);
145 sample->pitch = static_cast<float>(pitch);
146 sample->roll = static_cast<float>(roll);
147
148 ++m_validSampleCount;
149}
150
151PagedPathForHistory_ptr FGFlightHistory::pagedPathForHistory(size_t max_entries, size_t newerThan ) const
152{
154 if (m_buckets.empty()) {
155 return result;
156 }
157
158 for (auto bucket : m_buckets) {
159 unsigned int count = (bucket == m_buckets.back() ? m_validSampleCount : SAMPLE_BUCKET_WIDTH);
160
161 // iterate over all the valid samples in the bucket
162 for (unsigned int index = 0; index < count; ++index) {
163 // skip older entries
164 // TODO: bisect!
165 if( bucket->samples[index].simTimeMSec <= newerThan )
166 continue;
167
168 if( max_entries ) {
169 max_entries--;
170 SGGeod g = bucket->samples[index].position;
171 result->path.push_back(g);
172 result->last_seen = bucket->samples[index].simTimeMSec;
173 } else {
174 goto exit;
175 }
176
177 } // of samples iteration
178 } // of buckets iteration
179
180exit:
181 return result;
182}
183
184
185SGGeodVec FGFlightHistory::pathForHistory(double minEdgeLengthM) const
186{
187 SGGeodVec result;
188 if (m_buckets.empty()) {
189 return result;
190 }
191
192 result.push_back(m_buckets.front()->samples[0].position);
193 SGVec3d lastOutputCart = SGVec3d::fromGeod(result.back());
194 double minLengthSqr = minEdgeLengthM * minEdgeLengthM;
195
196 for (auto bucket : m_buckets) {
197 unsigned int count = (bucket == m_buckets.back() ? m_validSampleCount : SAMPLE_BUCKET_WIDTH);
198
199 // iterate over all the valid samples in the bucket
200 for (unsigned int index = 0; index < count; ++index) {
201 SGGeod g = bucket->samples[index].position;
202 SGVec3d cart(SGVec3d::fromGeod(g));
203 if (distSqr(cart, lastOutputCart) > minLengthSqr) {
204 lastOutputCart = cart;
205 result.push_back(g);
206 }
207 } // of samples iteration
208 } // of buckets iteration
209
210 return result;
211}
212
214{
215 for (auto ptr : m_buckets) {
216 delete ptr;
217 }
218 m_buckets.clear();
219 m_validSampleCount = SAMPLE_BUCKET_WIDTH;
220}
221
222size_t FGFlightHistory::currentMemoryUseBytes() const
223{
224 return sizeof(SampleBucket) * m_buckets.size();
225}
226
227
228// Register the subsystem.
229SGSubsystemMgr::Registrant<FGFlightHistory> registrantFGFlightHistory;
SGSubsystemMgr::Registrant< FGFlightHistory > registrantFGFlightHistory
const unsigned int SAMPLE_BUCKET_WIDTH
std::vector< SGGeod > SGGeodVec
SGSharedPtr< PagedPathForHistory > PagedPathForHistory_ptr
SGGeodVec pathForHistory(double minEdgeLengthM=50.0) const
retrieve the path, collapsing segments shorter than the specified minimum length
void update(double dt) override
void reinit() override
virtual ~FGFlightHistory()
PagedPathForHistory_ptr pagedPathForHistory(size_t max_entries, size_t newerThan=0) const
void clear()
clear the history
void shutdown() override
void init() override
SGGeod get_aircraft_position() const
Definition globals.cxx:611
void get_aircraft_orientation(double &heading, double &pitch, double &roll)
Definition globals.cxx:624
double get_sim_time_sec() const
Definition globals.hxx:185
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
Definition fg_props.cxx:532
FGGlobals * globals
Definition globals.cxx:142
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
Definition proptest.cpp:25
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
Definition proptest.cpp:30
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27