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 <QElapsedTimer>
11 #include <QTest>
12 
13 #include "test.h"
14 
15 #include "config/buildconfig.h"
18 #include "misc/cputime.h"
19 #include "misc/network/fsdsetup.h"
20 
21 using namespace swift::config;
22 using namespace swift::misc;
23 using namespace swift::misc::aviation;
24 using namespace swift::misc::physical_quantities;
25 using namespace swift::misc::network;
26 using namespace swift::misc::geo;
27 using namespace swift::misc::math;
28 
29 namespace MiscTest
30 {
33  {
34  Q_OBJECT
35 
36  private slots:
37  // Default constructor
38  void defaultConstructor();
39 
41  void allGndFlagsAndTakeOff() const;
42 
44  void allNotGndFlagsAndTouchdown() const;
45 
47  void ascending();
48 
50  void descending();
51 
53  void rotateUp();
54 
56  void sortOrder() const;
57 
59  void altitudeCorrection();
60 
62  void sortHint();
63 
64  void isGfLanding();
65 
66  void isGfStarting();
67 
68  void isGfEqualAirborne();
69 
70  void isGfEqualOnGround();
71 
72  private:
74  static swift::misc::aviation::CAircraftSituationList testSituations();
75 
78  testSetDescendingAltitudes(const swift::misc::aviation::CAircraftSituationList &situations);
79 
82  testSetRotateUpPitch(const swift::misc::aviation::CAircraftSituationList &situations);
83 
86  };
87 
88  void CTestAircraftSituation::defaultConstructor()
89  {
91 
92  QVERIFY2(sit.isNull(), "Situation should be null");
93  QVERIFY2(!sit.isOnGround(), "Should not be on ground");
94  QVERIFY2(!sit.isOnGroundFromNetwork(), "Should not be on ground from network");
95  QVERIFY2(!sit.isOnGroundFromParts(), "Should not be on ground from network");
96  QVERIFY2(!sit.isOnGroundInfoAvailable(), "Should not be on ground from network");
97  }
98 
99  void CTestAircraftSituation::allGndFlagsAndTakeOff() const
100  {
101  CAircraftSituationList situations = testSituations();
102  situations.setOnGroundInfo({ COnGroundInfo::OnGround, COnGroundInfo::NotSetGroundDetails });
103  const CAircraftSituationChange change(situations, cg(), false);
104  QVERIFY2(change.isConstOnGround(), "Expect const on ground");
105  QVERIFY(!change.isConstNotOnGround());
106  QVERIFY(!change.isJustTakingOff());
107  QVERIFY(!change.isJustTouchingDown());
108  QVERIFY(change.wasConstOnGround());
109  QVERIFY(!change.wasConstNotOnGround());
111 
112  CAircraftSituation f = situations.front();
113  f.setOnGroundInfo({ COnGroundInfo::NotOnGround, COnGroundInfo::NotSetGroundDetails });
114  situations.pop_front();
115  situations.push_front(f);
116  const CAircraftSituationChange change2(situations, cg(), false);
117  QVERIFY2(change2.isJustTakingOff(), "Expect just take off");
118  QVERIFY(!change2.isJustTouchingDown());
119  QVERIFY(change.wasConstOnGround());
120  QVERIFY(!change.wasConstNotOnGround());
121  }
122 
123  void CTestAircraftSituation::allNotGndFlagsAndTouchdown() const
124  {
125  CAircraftSituationList situations = testSetDescendingAltitudes(testSituations());
126  situations.setOnGroundInfo({ COnGroundInfo::NotOnGround, COnGroundInfo::NotSetGroundDetails });
127  const CAircraftSituationChange change(situations, cg(), false);
128  QVERIFY2(change.isConstNotOnGround(), "Expect const not on ground");
129  QVERIFY(!change.isConstOnGround());
130  QVERIFY(!change.isJustTakingOff());
131  QVERIFY(!change.isJustTouchingDown());
132  QVERIFY(!change.wasConstOnGround());
133  QVERIFY(change.wasConstNotOnGround());
135 
136  CAircraftSituation f = situations.front();
137  f.setOnGroundInfo({ COnGroundInfo::OnGround, COnGroundInfo::NotSetGroundDetails });
138  situations.pop_front();
139  situations.push_front(f);
140  const CAircraftSituationChange change2(situations, cg(), false);
141  QVERIFY2(change2.isJustTouchingDown(), "Expect just touchdown");
142  QVERIFY(!change2.isJustTakingOff());
143  QVERIFY(!change.wasConstOnGround());
144  QVERIFY(change.wasConstNotOnGround());
145  }
146 
147  void CTestAircraftSituation::ascending()
148  {
149  const CAircraftSituationList situations = testSituations();
150  QVERIFY2(situations.isConstAscending(), "Expect ascending");
151  }
152 
153  void CTestAircraftSituation::descending()
154  {
155  const CAircraftSituationList situations = testSetDescendingAltitudes(testSituations());
156  QVERIFY2(situations.isConstDescending(), "Expect descending");
157  }
158 
159  void CTestAircraftSituation::rotateUp()
160  {
161  CAircraftSituationList situations = testSetRotateUpPitch(testSituations());
162  const CAircraftSituationChange change(situations, cg(), false);
163  QVERIFY2(!change.isRotatingUp(), "Do not expect rotate up");
164 
165  CAircraftSituation f = situations.front();
166  situations.pop_front();
167  f.setPitch(CAngle(7.3, CAngleUnit::deg()));
168  situations.push_front(f);
169 
170  const CAircraftSituationChange change2(situations, cg(), false);
171  QVERIFY2(change2.isRotatingUp(), "Expect rotate up");
172  }
173 
174  void CTestAircraftSituation::sortOrder() const
175  {
176  CAircraftSituationList situations = testSituations();
177  QVERIFY2(situations.isSortedAdjustedLatestFirstWithoutNullPositions(), "Expect latest first");
178  QVERIFY(!situations.isSortedLatestLast());
179 
180  situations.reverse();
181  QVERIFY2(situations.isSortedAdjustedLatestLast(), "Expect latest first");
182  QVERIFY(!situations.isSortedAdjustedLatestFirst());
183  }
184 
185  void CTestAircraftSituation::altitudeCorrection()
186  {
187  CAircraftSituation::AltitudeCorrection correction = CAircraftSituation::NoCorrection;
188  const CAircraftSituationList situations = testSituations();
189  CAircraftSituation situation = situations.front();
190  CAltitude alt(100, CAltitude::MeanSeaLevel, CLengthUnit::ft());
191 
192  situation.setAltitude(alt);
193  CAltitude corAlt = situation.getCorrectedAltitude(true, &correction);
194 
195  // no elevation, expect same values
196  QVERIFY2(corAlt == alt, "Expect same altitude");
197 
198  CElevationPlane ep(situation, CElevationPlane::singlePointRadius());
199  situation.setGroundElevation(ep, CAircraftSituation::Test);
200 
201  // now we have same alt and elevation values
202  // no elevation, expect same values
203  corAlt = situation.getCorrectedAltitude(true, &correction);
204  QVERIFY2(corAlt == alt, "Still expect same altitude");
205 
206  // now we use CG underflow detection
207  const CLength cg(3, CLengthUnit::m());
208  situation.setCG(cg);
209  corAlt = situation.getCorrectedAltitude(true, &correction);
210  QVERIFY2(correction == CAircraftSituation::Underflow, "Expect underflow correction");
211  QVERIFY2(corAlt > alt, "Expect corrected altitude");
212  QVERIFY2((corAlt - cg) == alt, "Expect correction by CG");
213 
214  // Unit of CG must be irrelevant
215  CLength cg2(cg);
216  cg2.switchUnit(CLengthUnit::m());
217  situation.setCG(cg2);
218  CAltitude corAlt2 = situation.getCorrectedAltitude(true, &correction);
219  QVERIFY2(correction == CAircraftSituation::Underflow, "Expect underflow correction");
220  QVERIFY2(corAlt2 == corAlt, "Expect same value for corrected altitude");
221 
222  // now we test a negative altitude
223  alt = CAltitude(-1000, CAltitude::MeanSeaLevel, CLengthUnit::ft());
224  ep.setGeodeticHeight(alt);
225  situation.setAltitude(alt);
226  situation.setGroundElevation(ep, CAircraftSituation::Test);
227  corAlt = situation.getCorrectedAltitude(true, &correction);
228  QVERIFY2(correction == CAircraftSituation::Underflow, "Expect underflow correction");
229  QVERIFY2(corAlt > alt, "Expect corrected altitude");
230  QVERIFY2((corAlt - cg) == alt, "Expect correction by CG");
231 
232  // overflow detection
233  alt = CAltitude(1000, CAltitude::MeanSeaLevel, CLengthUnit::ft());
234  situation.setAltitude(alt);
235  ep.setGeodeticHeight(alt);
236  ep.addAltitudeOffset(CLength(-100, CLengthUnit::ft()));
237  situation.setGroundElevation(ep, CAircraftSituation::Test);
238  corAlt = situation.getCorrectedAltitude(true, &correction);
239  QVERIFY2(corAlt == alt, "Expect same altitude, no overflow since not on gnd.");
240 
241  situation.setOnGroundInfo({ COnGroundInfo::OnGround, COnGroundInfo::InFromNetwork });
242 
243  corAlt = situation.getCorrectedAltitude(true, &correction);
244  QVERIFY2(correction == CAircraftSituation::DraggedToGround, "Expect dragged to gnd.");
245  QVERIFY2(corAlt < alt, "Expect corrected altitude dragged to gnd.");
246  QVERIFY2(corAlt == (ep.getAltitude() + cg), "Expect correction by CG");
247  }
248 
249  void CTestAircraftSituation::sortHint()
250  {
251  constexpr int Lists = 50000;
252  constexpr int Loops = 20;
253  CAircraftSituationList situations = testSituations();
254  situations.sortAdjustedLatestFirst();
257  for (int i = 0; i < Lists; ++i)
258  {
259  listOfLists1.push_back(situations);
260  listOfLists2.push_back(situations);
261  listOfLists1.back().detach();
262  listOfLists2.back().detach();
263  listOfLists2.back().setAdjustedSortHint(CAircraftSituationList::AdjustedTimestampLatestFirst);
264  }
265 
266  QElapsedTimer time;
267  int cpuTime = getThreadCpuTimeMs();
268  time.start();
269  for (int i = 0; i < Loops; ++i)
270  {
271  for (const CAircraftSituationList &s : std::as_const(listOfLists1))
272  {
273  const CAircraftSituation s1 = s.oldestAdjustedObject();
274  const CAircraftSituation s2 = s.latestAdjustedObject();
276  }
277  }
278  const auto noHint = std::make_pair(time.elapsed(), getThreadCpuTimeMs() - cpuTime);
279 
280  cpuTime = getThreadCpuTimeMs();
281  time.start();
282  for (int i = 0; i < Loops; ++i)
283  {
284  for (const CAircraftSituationList &s : std::as_const(listOfLists2))
285  {
286  const CAircraftSituation s1 = s.oldestAdjustedObject();
287  const CAircraftSituation s2 = s.latestAdjustedObject();
289  }
290  }
291  const auto hint = std::make_pair(time.elapsed(), getThreadCpuTimeMs() - cpuTime);
292  const double ratio = static_cast<double>(hint.second) / static_cast<double>(noHint.second); // expected <1.0
293 
294  // qDebug() << "MacOS:" << boolToYesNo(CBuildConfig::isRunningOnMacOSPlatform());
295  qDebug() << "Access without hint" << noHint.first << "ms (CPU time" << noHint.second << "ms)";
296  qDebug() << "Access with hint" << hint.first << "ms (CPU time" << hint.second << "ms)";
297  qDebug() << "Access ratio" << ratio;
298 
299 #if 0
300  // remark On Win/Linux access with hint is faster
301  // on MacOS the times are the same, maybe with hint it is even slightly slower
302  if (noHint >= hint)
303  {
304  // on MacOS we accept up to 10% overhead and SKIP the test
306  {
307  if (ratio <= 1.1) { return; } // on MacOS 10% overhead accepted
308 
309  // more than 10% overhead!
311  QSKIP("Skipped sort hint on MacOS");
312  return;
313  }
314  }
315 #endif
316 
319  const bool ok = hint <= noHint;
320  if (ok || CBuildConfig::isLocalDeveloperDebugBuild()) { QVERIFY2(ok, "Expected hinted sort being faster"); }
321  }
322 
323  CAircraftSituationList CTestAircraftSituation::testSituations()
324  {
325  // "Kugaaruk Airport","Pelly
326  // Bay","Canada","YBB","CYBB",68.534401,-89.808098,56,-7,"A","America/Edmonton","airport","OurAirports" "Baie
327  // Comeau Airport","Baie
328  // Comeau","Canada","YBC","CYBC",49.13249969482422,-68.20439910888672,71,-5,"A","America/Toronto","airport","OurAirports"
329  // "CFB
330  // Bagotville","Bagotville","Canada","YBG","CYBG",48.33060073852539,-70.99639892578125,522,-5,"A","America/Toronto","airport","OurAirports"
331 
332  CAircraftSituationList situations;
333  const qint64 ts = QDateTime::currentSecsSinceEpoch();
334  const qint64 os = CFsdSetup::c_positionTimeOffsetMsec;
335  CAltitude alt(10000, CAltitude::MeanSeaLevel, CLengthUnit::m());
336  static const CCoordinateGeodetic dummyPos(48.33060073852539, -70.99639892578125, 522);
337 
338  for (int i = 0; i < 10; i++)
339  {
340  CAircraftSituation s(dummyPos);
341  const qint64 cTs = ts - i * os;
342  s.setMSecsSinceEpoch(cTs);
343  s.setTimeOffsetMs(os);
344  CAltitude altitude(alt);
345  altitude.addValueSameUnit(-100 * i); // 10000, 9900, 9800 .... (newer->older)
346  s.setAltitude(altitude);
347  situations.push_back(s);
348  }
349  return situations;
350  }
351 
352  CAircraftSituationList CTestAircraftSituation::testSetDescendingAltitudes(const CAircraftSituationList &situations)
353  {
354  CAircraftSituationList newSituations;
355  CAltitude alt(0, CAltitude::MeanSeaLevel, CLengthUnit::m());
356 
357  for (const CAircraftSituation &situation : situations)
358  {
359  CAircraftSituation s(situation);
360  s.setAltitude(alt);
361  newSituations.push_back(s);
362  alt.addValueSameUnit(100); // 0, 100, 200 ... (newer->older)
363  }
364  return newSituations;
365  }
366 
367  CAircraftSituationList CTestAircraftSituation::testSetRotateUpPitch(const CAircraftSituationList &situations)
368  {
369  CAircraftSituationList newSituations;
370  double average = 0;
371  for (const CAircraftSituation &situation : situations)
372  {
373  CAircraftSituation s(situation);
374  const double pitch = CMathUtils::randomDouble(1.5);
375  average += pitch;
376  s.setPitch(CAngle(pitch, CAngleUnit::deg()));
377  newSituations.push_back(s);
378  }
379  average = average / newSituations.size();
380  CAircraftSituation avg = newSituations.front();
381  avg.setPitch(CAngle(average, CAngleUnit::deg()));
382  avg.addMsecs(1000); // make this the latest situation
383  newSituations.push_front(avg); // first value is average pitch, so it will NOT be detected
384  return newSituations;
385  }
386 
387  void CTestAircraftSituation::isGfLanding()
388  {
389 
390  QVERIFY2(CAircraftSituation::isGfLanding(1.0, 0.0), "Should be landing");
391  QVERIFY2(!CAircraftSituation::isGfLanding(0.0, 1.0), "Should be landing");
392  QVERIFY2(!CAircraftSituation::isGfLanding(1.0, 0.9), "Should be landing");
393  QVERIFY2(!CAircraftSituation::isGfLanding(0.5, 0.5), "Should be landing");
394  }
395 
396  void CTestAircraftSituation::isGfStarting()
397  {
398  QVERIFY2(CAircraftSituation::isGfStarting(0.0, 1.0), "Should be starting");
399  QVERIFY2(!CAircraftSituation::isGfStarting(1.0, 0.0), "Should be starting");
400  QVERIFY2(!CAircraftSituation::isGfStarting(0.9, 1.0), "Should be starting");
401  QVERIFY2(!CAircraftSituation::isGfStarting(0.5, 0.5), "Should be starting");
402  }
403 
404  void CTestAircraftSituation::isGfEqualAirborne()
405  {
406  QVERIFY2(CAircraftSituation::isGfEqualAirborne(0.0, 0.0), "Should be airborne");
407  QVERIFY2(!CAircraftSituation::isGfEqualAirborne(1.0, 1.0), "Should be airborne");
408  QVERIFY2(!CAircraftSituation::isGfEqualAirborne(0.0, 0.1), "Should be airborne");
409  QVERIFY2(!CAircraftSituation::isGfEqualAirborne(0.5, 0.5), "Should be airborne");
410  }
411 
412  void CTestAircraftSituation::isGfEqualOnGround()
413  {
414  QVERIFY2(CAircraftSituation::isGfEqualOnGround(1.0, 1.0), "Should be ground");
415  QVERIFY2(!CAircraftSituation::isGfEqualOnGround(0.0, 0.0), "Should be ground");
416  QVERIFY2(!CAircraftSituation::isGfEqualOnGround(1.0, 0.9), "Should be ground");
417  QVERIFY2(!CAircraftSituation::isGfEqualOnGround(0.5, 0.5), "Should be on ground");
418  }
419 
420  const CLength &CTestAircraftSituation::cg()
421  {
422  static const CLength cg(2.0, CLengthUnit::m());
423  return cg;
424  }
425 } // namespace MiscTest
426 
429 
430 #include "testaircraftsituation.moc"
431 
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
qint64 currentSecsSinceEpoch()
qint64 elapsed() const const
QSKIP(description)
QVERIFY(condition)
QVERIFY2(condition, message)
SWIFTTEST_MAIN(MiscTest::CTestAircraftSituation)
main