swift
testaircraftsituation.cpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
7 
8 #include <QDateTime>
9 #include <QDebug>
10 #include <QTest>
11 
12 #include "test.h"
13 
14 #include "config/buildconfig.h"
17 #include "misc/cputime.h"
18 #include "misc/network/fsdsetup.h"
19 
20 using namespace swift::config;
21 using namespace swift::misc;
22 using namespace swift::misc::aviation;
23 using namespace swift::misc::physical_quantities;
24 using namespace swift::misc::network;
25 using namespace swift::misc::geo;
26 using namespace swift::misc::math;
27 
28 namespace MiscTest
29 {
31  class CTestAircraftSituation : public QObject
32  {
33  Q_OBJECT
34 
35  private slots:
36  // Default constructor
37  void defaultConstructor();
38 
40  void allGndFlagsAndTakeOff() const;
41 
43  void allNotGndFlagsAndTouchdown() const;
44 
46  void ascending();
47 
49  void descending();
50 
52  void rotateUp();
53 
55  void sortOrder() const;
56 
58  void altitudeCorrection();
59 
61  void sortHint();
62 
63  void isGfLanding();
64 
65  void isGfStarting();
66 
67  void isGfEqualAirborne();
68 
69  void isGfEqualOnGround();
70 
71  private:
73  static swift::misc::aviation::CAircraftSituationList testSituations();
74 
77  testSetDescendingAltitudes(const swift::misc::aviation::CAircraftSituationList &situations);
78 
81  testSetRotateUpPitch(const swift::misc::aviation::CAircraftSituationList &situations);
82 
85  };
86 
87  void CTestAircraftSituation::defaultConstructor()
88  {
90 
91  QVERIFY2(sit.isNull(), "Situation should be null");
92  QVERIFY2(!sit.isOnGround(), "Should not be on ground");
93  QVERIFY2(!sit.isOnGroundFromNetwork(), "Should not be on ground from network");
94  QVERIFY2(!sit.isOnGroundFromParts(), "Should not be on ground from network");
95  QVERIFY2(!sit.isOnGroundInfoAvailable(), "Should not be on ground from network");
96  }
97 
98  void CTestAircraftSituation::allGndFlagsAndTakeOff() const
99  {
100  CAircraftSituationList situations = testSituations();
101  situations.setOnGroundInfo({ COnGroundInfo::OnGround, COnGroundInfo::NotSetGroundDetails });
102  const CAircraftSituationChange change(situations, cg(), false);
103  QVERIFY2(change.isConstOnGround(), "Expect const on ground");
104  QVERIFY(!change.isConstNotOnGround());
105  QVERIFY(!change.isJustTakingOff());
106  QVERIFY(!change.isJustTouchingDown());
107  QVERIFY(change.wasConstOnGround());
108  QVERIFY(!change.wasConstNotOnGround());
110 
111  CAircraftSituation f = situations.front();
112  f.setOnGroundInfo({ COnGroundInfo::NotOnGround, COnGroundInfo::NotSetGroundDetails });
113  situations.pop_front();
114  situations.push_front(f);
115  const CAircraftSituationChange change2(situations, cg(), false);
116  QVERIFY2(change2.isJustTakingOff(), "Expect just take off");
117  QVERIFY(!change2.isJustTouchingDown());
118  QVERIFY(change.wasConstOnGround());
119  QVERIFY(!change.wasConstNotOnGround());
120  }
121 
122  void CTestAircraftSituation::allNotGndFlagsAndTouchdown() const
123  {
124  CAircraftSituationList situations = testSetDescendingAltitudes(testSituations());
125  situations.setOnGroundInfo({ COnGroundInfo::NotOnGround, COnGroundInfo::NotSetGroundDetails });
126  const CAircraftSituationChange change(situations, cg(), false);
127  QVERIFY2(change.isConstNotOnGround(), "Expect const not on ground");
128  QVERIFY(!change.isConstOnGround());
129  QVERIFY(!change.isJustTakingOff());
130  QVERIFY(!change.isJustTouchingDown());
131  QVERIFY(!change.wasConstOnGround());
132  QVERIFY(change.wasConstNotOnGround());
134 
135  CAircraftSituation f = situations.front();
136  f.setOnGroundInfo({ COnGroundInfo::OnGround, COnGroundInfo::NotSetGroundDetails });
137  situations.pop_front();
138  situations.push_front(f);
139  const CAircraftSituationChange change2(situations, cg(), false);
140  QVERIFY2(change2.isJustTouchingDown(), "Expect just touchdown");
141  QVERIFY(!change2.isJustTakingOff());
142  QVERIFY(!change.wasConstOnGround());
143  QVERIFY(change.wasConstNotOnGround());
144  }
145 
146  void CTestAircraftSituation::ascending()
147  {
148  const CAircraftSituationList situations = testSituations();
149  QVERIFY2(situations.isConstAscending(), "Expect ascending");
150  }
151 
152  void CTestAircraftSituation::descending()
153  {
154  const CAircraftSituationList situations = testSetDescendingAltitudes(testSituations());
155  QVERIFY2(situations.isConstDescending(), "Expect descending");
156  }
157 
158  void CTestAircraftSituation::rotateUp()
159  {
160  CAircraftSituationList situations = testSetRotateUpPitch(testSituations());
161  const CAircraftSituationChange change(situations, cg(), false);
162  QVERIFY2(!change.isRotatingUp(), "Do not expect rotate up");
163 
164  CAircraftSituation f = situations.front();
165  situations.pop_front();
166  f.setPitch(CAngle(7.3, CAngleUnit::deg()));
167  situations.push_front(f);
168 
169  const CAircraftSituationChange change2(situations, cg(), false);
170  QVERIFY2(change2.isRotatingUp(), "Expect rotate up");
171  }
172 
173  void CTestAircraftSituation::sortOrder() const
174  {
175  CAircraftSituationList situations = testSituations();
176  QVERIFY2(situations.isSortedAdjustedLatestFirstWithoutNullPositions(), "Expect latest first");
177  QVERIFY(!situations.isSortedLatestLast());
178 
179  situations.reverse();
180  QVERIFY2(situations.isSortedAdjustedLatestLast(), "Expect latest first");
181  QVERIFY(!situations.isSortedAdjustedLatestFirst());
182  }
183 
184  void CTestAircraftSituation::altitudeCorrection()
185  {
186  CAircraftSituation::AltitudeCorrection correction = CAircraftSituation::NoCorrection;
187  const CAircraftSituationList situations = testSituations();
188  CAircraftSituation situation = situations.front();
189  CAltitude alt(100, CAltitude::MeanSeaLevel, CLengthUnit::ft());
190 
191  situation.setAltitude(alt);
192  CAltitude corAlt = situation.getCorrectedAltitude(true, &correction);
193 
194  // no elevation, expect same values
195  QVERIFY2(corAlt == alt, "Expect same altitude");
196 
197  CElevationPlane ep(situation, CElevationPlane::singlePointRadius());
198  situation.setGroundElevation(ep, CAircraftSituation::Test);
199 
200  // now we have same alt and elevation values
201  // no elevation, expect same values
202  corAlt = situation.getCorrectedAltitude(true, &correction);
203  QVERIFY2(corAlt == alt, "Still expect same altitude");
204 
205  // now we use CG underflow detection
206  const CLength cg(3, CLengthUnit::m());
207  situation.setCG(cg);
208  corAlt = situation.getCorrectedAltitude(true, &correction);
209  QVERIFY2(correction == CAircraftSituation::Underflow, "Expect underflow correction");
210  QVERIFY2(corAlt > alt, "Expect corrected altitude");
211  QVERIFY2((corAlt - cg) == alt, "Expect correction by CG");
212 
213  // Unit of CG must be irrelevant
214  CLength cg2(cg);
215  cg2.switchUnit(CLengthUnit::m());
216  situation.setCG(cg2);
217  CAltitude corAlt2 = situation.getCorrectedAltitude(true, &correction);
218  QVERIFY2(correction == CAircraftSituation::Underflow, "Expect underflow correction");
219  QVERIFY2(corAlt2 == corAlt, "Expect same value for corrected altitude");
220 
221  // now we test a negative altitude
222  alt = CAltitude(-1000, CAltitude::MeanSeaLevel, CLengthUnit::ft());
223  ep.setGeodeticHeight(alt);
224  situation.setAltitude(alt);
225  situation.setGroundElevation(ep, CAircraftSituation::Test);
226  corAlt = situation.getCorrectedAltitude(true, &correction);
227  QVERIFY2(correction == CAircraftSituation::Underflow, "Expect underflow correction");
228  QVERIFY2(corAlt > alt, "Expect corrected altitude");
229  QVERIFY2((corAlt - cg) == alt, "Expect correction by CG");
230 
231  // overflow detection
232  alt = CAltitude(1000, CAltitude::MeanSeaLevel, CLengthUnit::ft());
233  situation.setAltitude(alt);
234  ep.setGeodeticHeight(alt);
235  ep.addAltitudeOffset(CLength(-100, CLengthUnit::ft()));
236  situation.setGroundElevation(ep, CAircraftSituation::Test);
237  corAlt = situation.getCorrectedAltitude(true, &correction);
238  QVERIFY2(corAlt == alt, "Expect same altitude, no overflow since not on gnd.");
239 
240  situation.setOnGroundInfo({ COnGroundInfo::OnGround, COnGroundInfo::InFromNetwork });
241 
242  corAlt = situation.getCorrectedAltitude(true, &correction);
243  QVERIFY2(correction == CAircraftSituation::DraggedToGround, "Expect dragged to gnd.");
244  QVERIFY2(corAlt < alt, "Expect corrected altitude dragged to gnd.");
245  QVERIFY2(corAlt == (ep.getAltitude() + cg), "Expect correction by CG");
246  }
247 
248  void CTestAircraftSituation::sortHint()
249  {
250  constexpr int Lists = 50000;
251  constexpr int Loops = 20;
252  CAircraftSituationList situations = testSituations();
253  situations.sortAdjustedLatestFirst();
256  for (int i = 0; i < Lists; ++i)
257  {
258  listOfLists1.push_back(situations);
259  listOfLists2.push_back(situations);
260  listOfLists1.back().detach();
261  listOfLists2.back().detach();
262  listOfLists2.back().setAdjustedSortHint(CAircraftSituationList::AdjustedTimestampLatestFirst);
263  }
264 
265  QElapsedTimer time;
266  int cpuTime = getThreadCpuTimeMs();
267  time.start();
268  for (int i = 0; i < Loops; ++i)
269  {
270  for (const CAircraftSituationList &s : std::as_const(listOfLists1))
271  {
272  const CAircraftSituation s1 = s.oldestAdjustedObject();
273  const CAircraftSituation s2 = s.latestAdjustedObject();
275  }
276  }
277  const auto noHint = std::make_pair(time.elapsed(), getThreadCpuTimeMs() - cpuTime);
278 
279  cpuTime = getThreadCpuTimeMs();
280  time.start();
281  for (int i = 0; i < Loops; ++i)
282  {
283  for (const CAircraftSituationList &s : std::as_const(listOfLists2))
284  {
285  const CAircraftSituation s1 = s.oldestAdjustedObject();
286  const CAircraftSituation s2 = s.latestAdjustedObject();
288  }
289  }
290  const auto hint = std::make_pair(time.elapsed(), getThreadCpuTimeMs() - cpuTime);
291  const double ratio = static_cast<double>(hint.second) / static_cast<double>(noHint.second); // expected <1.0
292 
293  // qDebug() << "MacOS:" << boolToYesNo(CBuildConfig::isRunningOnMacOSPlatform());
294  qDebug() << "Access without hint" << noHint.first << "ms (CPU time" << noHint.second << "ms)";
295  qDebug() << "Access with hint" << hint.first << "ms (CPU time" << hint.second << "ms)";
296  qDebug() << "Access ratio" << ratio;
297 
298 #if 0
299  // remark On Win/Linux access with hint is faster
300  // on MacOS the times are the same, maybe with hint it is even slightly slower
301  if (noHint >= hint)
302  {
303  // on MacOS we accept up to 10% overhead and SKIP the test
305  {
306  if (ratio <= 1.1) { return; } // on MacOS 10% overhead accepted
307 
308  // more than 10% overhead!
310  QSKIP("Skipped sort hint on MacOS");
311  return;
312  }
313  }
314 #endif
315 
318  const bool ok = hint <= noHint;
319  if (ok || CBuildConfig::isLocalDeveloperDebugBuild()) { QVERIFY2(ok, "Expected hinted sort being faster"); }
320  }
321 
322  CAircraftSituationList CTestAircraftSituation::testSituations()
323  {
324  // "Kugaaruk Airport","Pelly
325  // Bay","Canada","YBB","CYBB",68.534401,-89.808098,56,-7,"A","America/Edmonton","airport","OurAirports" "Baie
326  // Comeau Airport","Baie
327  // Comeau","Canada","YBC","CYBC",49.13249969482422,-68.20439910888672,71,-5,"A","America/Toronto","airport","OurAirports"
328  // "CFB
329  // Bagotville","Bagotville","Canada","YBG","CYBG",48.33060073852539,-70.99639892578125,522,-5,"A","America/Toronto","airport","OurAirports"
330 
331  CAircraftSituationList situations;
332  const qint64 ts = QDateTime::currentSecsSinceEpoch();
333  const qint64 os = CFsdSetup::c_positionTimeOffsetMsec;
334  CAltitude alt(10000, CAltitude::MeanSeaLevel, CLengthUnit::m());
335  static const CCoordinateGeodetic dummyPos(48.33060073852539, -70.99639892578125, 522);
336 
337  for (int i = 0; i < 10; i++)
338  {
339  CAircraftSituation s(dummyPos);
340  const qint64 cTs = ts - i * os;
341  s.setMSecsSinceEpoch(cTs);
342  s.setTimeOffsetMs(os);
343  CAltitude altitude(alt);
344  altitude.addValueSameUnit(-100 * i); // 10000, 9900, 9800 .... (newer->older)
345  s.setAltitude(altitude);
346  situations.push_back(s);
347  }
348  return situations;
349  }
350 
351  CAircraftSituationList CTestAircraftSituation::testSetDescendingAltitudes(const CAircraftSituationList &situations)
352  {
353  CAircraftSituationList newSituations;
354  CAltitude alt(0, CAltitude::MeanSeaLevel, CLengthUnit::m());
355 
356  for (const CAircraftSituation &situation : situations)
357  {
358  CAircraftSituation s(situation);
359  s.setAltitude(alt);
360  newSituations.push_back(s);
361  alt.addValueSameUnit(100); // 0, 100, 200 ... (newer->older)
362  }
363  return newSituations;
364  }
365 
366  CAircraftSituationList CTestAircraftSituation::testSetRotateUpPitch(const CAircraftSituationList &situations)
367  {
368  CAircraftSituationList newSituations;
369  double average = 0;
370  for (const CAircraftSituation &situation : situations)
371  {
372  CAircraftSituation s(situation);
373  const double pitch = CMathUtils::randomDouble(1.5);
374  average += pitch;
375  s.setPitch(CAngle(pitch, CAngleUnit::deg()));
376  newSituations.push_back(s);
377  }
378  average = average / newSituations.size();
379  CAircraftSituation avg = newSituations.front();
380  avg.setPitch(CAngle(average, CAngleUnit::deg()));
381  avg.addMsecs(1000); // make this the latest situation
382  newSituations.push_front(avg); // first value is average pitch, so it will NOT be detected
383  return newSituations;
384  }
385 
386  void CTestAircraftSituation::isGfLanding()
387  {
388 
389  QVERIFY2(CAircraftSituation::isGfLanding(1.0, 0.0), "Should be landing");
390  QVERIFY2(!CAircraftSituation::isGfLanding(0.0, 1.0), "Should be landing");
391  QVERIFY2(!CAircraftSituation::isGfLanding(1.0, 0.9), "Should be landing");
392  QVERIFY2(!CAircraftSituation::isGfLanding(0.5, 0.5), "Should be landing");
393  }
394 
395  void CTestAircraftSituation::isGfStarting()
396  {
397  QVERIFY2(CAircraftSituation::isGfStarting(0.0, 1.0), "Should be starting");
398  QVERIFY2(!CAircraftSituation::isGfStarting(1.0, 0.0), "Should be starting");
399  QVERIFY2(!CAircraftSituation::isGfStarting(0.9, 1.0), "Should be starting");
400  QVERIFY2(!CAircraftSituation::isGfStarting(0.5, 0.5), "Should be starting");
401  }
402 
403  void CTestAircraftSituation::isGfEqualAirborne()
404  {
405  QVERIFY2(CAircraftSituation::isGfEqualAirborne(0.0, 0.0), "Should be airborne");
406  QVERIFY2(!CAircraftSituation::isGfEqualAirborne(1.0, 1.0), "Should be airborne");
407  QVERIFY2(!CAircraftSituation::isGfEqualAirborne(0.0, 0.1), "Should be airborne");
408  QVERIFY2(!CAircraftSituation::isGfEqualAirborne(0.5, 0.5), "Should be airborne");
409  }
410 
411  void CTestAircraftSituation::isGfEqualOnGround()
412  {
413  QVERIFY2(CAircraftSituation::isGfEqualOnGround(1.0, 1.0), "Should be ground");
414  QVERIFY2(!CAircraftSituation::isGfEqualOnGround(0.0, 0.0), "Should be ground");
415  QVERIFY2(!CAircraftSituation::isGfEqualOnGround(1.0, 0.9), "Should be ground");
416  QVERIFY2(!CAircraftSituation::isGfEqualOnGround(0.5, 0.5), "Should be on ground");
417  }
418 
419  const CLength &CTestAircraftSituation::cg()
420  {
421  static const CLength cg(2.0, CLengthUnit::m());
422  return cg;
423  }
424 } // namespace MiscTest
425 
428 
429 #include "testaircraftsituation.moc"
430 
CAircraftSituation and CAircraftSituationChange tests.
static constexpr bool isRunningOnMacOSPlatform()
Running on MacOS platform?
static bool isLocalDeveloperDebugBuild()
Local build for developers.
Generic sequential container with value semantics.
Definition: sequence.h:86
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
reference front()
Access the first element.
Definition: sequence.h:225
void push_front(const T &value)
Insert as first element.
Definition: sequence.h:308
reference back()
Access the last element.
Definition: sequence.h:249
void reverse()
In-place reverse.
Definition: sequence.h:548
void pop_front()
Removes an element at the front of the sequence.
Definition: sequence.h:374
void addMsecs(qint64 ms)
Add the given number of milliseconds to the timestamp.
bool isSortedLatestLast() const
Is completely sorted: latest last.
qint64 getAdjustedMSecsSinceEpoch() const
Timestamp with offset added for interpolation.
bool isSortedAdjustedLatestFirst() const
Is completely sorted: latest last.
bool isSortedAdjustedLatestLast() const
Is completely sorted: latest last.
void sortAdjustedLatestFirst()
Sort by adjusted timestamp.
Value object about changes in situations.
Value object encapsulating information of an aircraft's situation.
void setCG(const physical_quantities::CLength &cg)
Set CG.
bool setGroundElevation(const aviation::CAltitude &altitude, GndElevationInfo info, bool transferred=false)
Elevation of the ground directly beneath at the given situation.
AltitudeCorrection
How was altitude corrected?
bool isOnGroundInfoAvailable() const
On ground info available?
void setAltitude(const CAltitude &altitude)
Set altitude.
CAltitude getCorrectedAltitude(bool enableDragToGround=true, AltitudeCorrection *correction=nullptr) const
Get altitude under consideration of ground elevation and ground flag.
void setOnGroundInfo(const aviation::COnGroundInfo &info)
Set the on ground info.
void setPitch(const physical_quantities::CAngle &pitch)
Set pitch.
virtual bool isNull() const
Null situation.
bool isOnGroundFromNetwork() const
On ground by network flag?
bool isOnGroundFromParts() const
On ground by parts?
bool isConstDescending(bool alreadySortedLatestFirst=false) const
Constantly descending?
void setOnGroundInfo(const COnGroundInfo &info)
Set on ground.
bool isSortedAdjustedLatestFirstWithoutNullPositions() const
Latest first and no null positions?
bool isConstAscending(bool alreadySortedLatestFirst=false) const
Constantly ascending?
Altitude as used in aviation, can be AGL or MSL altitude.
Definition: altitude.h:52
Plane of same elevation, can be a single point or larger area (e.g. airport)
Physical unit angle (radians, degrees)
Definition: angle.h:23
Physical unit length (length)
Definition: length.h:18
Free functions in swift::misc.
int getThreadCpuTimeMs()
Get the time in milliseconds that the CPU has spent executing the current thread.
Definition: cputime.cpp:65
SWIFTTEST_MAIN(MiscTest::CTestAircraftSituation)
main