Enroute Flight Navigation
A navigation app for VFR pilots
FlightLog.h
1/***************************************************************************
2 * Copyright (C) 2026 by Stefan Kebekus *
3 * stefan.kebekus@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 3 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21#pragma once
22
23#include <QGeoPath>
24#include <QObjectBindableProperty>
25#include <QQmlEngine>
26#include <QStandardPaths>
27#include <QTimer>
28#include <QUuid>
29#include <QVariant>
30
31#include "GlobalObject.h"
32#include "flightlog/Flight.h"
33#include "flightlog/FlightDetector.h"
34#include "flightlog/FlightRecorder.h"
35#include "geomaps/Waypoint.h"
36
37using namespace Qt::Literals::StringLiterals;
38
39namespace Flightlog {
40
51
52class FlightLog : public GlobalObject
53{
54 Q_OBJECT
55 QML_ELEMENT
56 QML_SINGLETON
57
58public:
59
60 //
61 // Constructors and Destructors
62 //
63
68 explicit FlightLog(QObject* parent = nullptr);
69
70 // deferred initialization
71 void deferredInitialization() override;
72
73 // No default constructor, important for QML singleton
74 explicit FlightLog() = delete;
75
77 ~FlightLog() override = default;
78
79 // factory function for QML singleton
80 static FlightLog* create(QQmlEngine* /*unused*/, QJSEngine* /*unused*/)
81 {
83 }
84
85
86 //
87 // PROPERTIES
88 //
89
91 Q_PROPERTY(QList<Flightlog::Flight> flights READ flights NOTIFY flightsChanged)
92 [[nodiscard]] auto flights() const -> QList<Flight> { return m_flights; }
93
96 [[nodiscard]] auto detectionState() const -> FlightDetector::DetectionState;
97
99 Q_PROPERTY(int count READ count NOTIFY flightsChanged)
100 [[nodiscard]] auto count() const -> int { return static_cast<int>(m_flights.value().size()); }
101
109 [[nodiscard]] auto displayedTrackPath() const -> QGeoPath;
110
117 [[nodiscard]] auto displayedTrackUuid() const -> QString;
118
124 Q_PROPERTY(bool trackRecording READ trackRecording WRITE setTrackRecording NOTIFY trackRecordingChanged)
125 [[nodiscard]] auto trackRecording() const -> bool;
126 void setTrackRecording(bool enabled);
127
133 Q_PROPERTY(bool showCurrentFlightTrace READ showCurrentFlightTrace WRITE setShowCurrentFlightTrace NOTIFY showCurrentFlightTraceChanged)
134 [[nodiscard]] auto showCurrentFlightTrace() const -> bool;
135 void setShowCurrentFlightTrace(bool enabled);
136
137
138 //
139 // Methods
140 //
141
149 Q_INVOKABLE void addFlight(const Flightlog::Flight& flight);
150
157 Q_INVOKABLE void removeFlight(const QString& uuid);
158
167 Q_INVOKABLE void removeFlights(const QStringList& uuids);
168
174 Q_INVOKABLE void clearFlights();
175
184 Q_INVOKABLE void updateFlight(const QString& uuid, const Flightlog::Flight& flight);
185
203 Q_INVOKABLE static Flightlog::Flight createFlight(
204 const QString& departureICAO,
205 const QString& arrivalICAO,
206 const QString& date,
207 const QString& offBlockTimeStr,
208 const QString& startTimeStr,
209 const QString& landingTimeStr,
210 const QString& onBlockTimeStr,
211 const QString& pilotName,
212 const QString& aircraftCallsign,
213 const QString& comments);
214
221 Q_INVOKABLE void endFlight();
222
232 Q_INVOKABLE QString lastArrivalICAO(const QString& aircraftCallsign) const;
233
245 Q_INVOKABLE static GeoMaps::Waypoint nearestAirfield(const QGeoCoordinate& position = {}, double proximityM = 5000.0);
246
255 [[nodiscard]] Q_INVOKABLE QByteArray exportToIGC(const QString& uuid) const;
256
265 [[nodiscard]] Q_INVOKABLE QByteArray exportToForeFlight(const QStringList& uuids) const;
266
276 [[nodiscard]] Q_INVOKABLE QByteArray exportToJSON(const QStringList& uuids) const;
277
285 Q_INVOKABLE void removeTrack(const QString& uuid);
286
294 Q_INVOKABLE void showTrack(const QString& uuid);
295
297 Q_INVOKABLE void hideTrack();
298
299signals:
302
305
308
311
314
319 void takeoffDetected(const QString& time);
320
325 void landingDetected(const QString& time);
326
334 void saveError(const QString& message);
335
336private slots:
337 // Process position updates — delegates to the active FlightDetector
338 void onPositionUpdated();
339
340 // Handle detection state change — forwards signal
341 void onDetectionStateChanged();
342
343 // Handle auto-detection setting change — manages Android foreground service
344 void onAutoFlightDetectionChanged();
345
346#ifdef Q_OS_ANDROID
347 // Posted after a grace period when auto-detection is on but no position
348 // data is arriving. Cancelled when GPS resumes or detection is disabled.
349 void onReceivingPositionInfoChanged(bool receiving);
350#endif
351
352 // Handle takeoff detected by the FlightDetector
353 void onTakeoffDetected(const QString& departureICAO,
354 const QGeoCoordinate& departureCoordinate,
355 const QDateTime& startTime,
356 const QString& aircraftCallsign);
357
358 // Handle landing detected by the FlightDetector
359 void onLandingDetected(const QString& arrivalICAO,
360 const QGeoCoordinate& arrivalCoordinate,
361 const QDateTime& landingTime,
362 int landingCount);
363
364private:
365 Q_DISABLE_COPY_MOVE(FlightLog)
366
367 // Install signal connections for the given detector
368 void connectDetector(FlightDetector* detector);
369
370 // Persist flights to JSON file
371 void save();
372
373 // Load flights from JSON file
374 void load();
375
376 // Rename a corrupt flight log file aside and notify the user via saveError.
377 // Uses a timestamp suffix so multiple bad files don't overwrite each other.
378 void quarantineFlightLogFile(const QString& reason);
379
380 // Resolve ICAO codes to coordinates using the GeoMapProvider
381 void resolveCoordinates(Flight& flight);
382
383 // Sort flights by startTime descending
384 void sortFlights(QList<Flight>& flights);
385
386 // Build a QJsonDocument from a list of flights (shared by save() and exportToJSON())
387 static auto flightsToJsonDocument(const QList<Flight>& flights) -> QJsonDocument;
388
389 // Collect the flights matching the given UUID strings; returns all flights if the list is empty
390 [[nodiscard]] auto flightsForUuids(const QStringList& uuids) const -> QList<Flight>;
391
392 // Helper to parse a date+time string to QDateTime
393 static auto parseDateTime(const QString& date, const QString& timeStr) -> QDateTime;
394
395 Q_OBJECT_BINDABLE_PROPERTY(FlightLog, QList<Flightlog::Flight>, m_flights, &FlightLog::flightsChanged)
396
397 // UUID of the flight currently being recorded, or null if none
398 QUuid m_currentFlightUuid;
399
400 // Filename of the flight whose saved track is displayed, or empty
401 QString m_displayedTrackFile;
402
403 // Cached geo path for the displayed saved track
404 QGeoPath m_displayedTrackPath;
405
406 // The active flight detector (owned by this object)
407 FlightDetector* m_detector {nullptr};
408
409 // The flight recorder (owned by this object)
410 FlightRecorder m_recorder {this};
411
412#ifdef Q_OS_ANDROID
413 // Tracks whether the Android foreground service is running
414 bool m_foregroundServiceRunning {false};
415
416 // Fires after a grace period to post the "no GPS" notification. Started
417 // when auto-detection is enabled with no position data; cancelled when
418 // position arrives or auto-detection is disabled.
419 QTimer m_noGPSTimer;
420#endif
421
422 // Persistence
423 const QString m_fileName {QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + u"/flightlog.json"_s};
424};
425
426} // namespace Flightlog
Abstract base class for automatic flight detection strategies.
DetectionState
Detection state for automatic takeoff/landing detection.
Q_INVOKABLE QByteArray exportToForeFlight(const QStringList &uuids) const
Generate ForeFlight CSV content for selected flights.
void flightsChanged()
Notifier signal.
Flightlog::FlightDetector::DetectionState detectionState
Current state of the automatic detection state machine.
Definition FlightLog.h:95
void displayedTrackPathChanged()
Notifier signal for displayedTrackPath and displayedTrackIndex.
~FlightLog() override=default
Standard destructor.
void detectionStateChanged()
Notifier signal.
FlightLog(QObject *parent=nullptr)
Standard constructor.
void saveError(const QString &message)
Emitted when saving the flight log file fails.
Q_INVOKABLE QString lastArrivalICAO(const QString &aircraftCallsign) const
Find the last arrival ICAO for a given aircraft.
void takeoffDetected(const QString &time)
Emitted when a takeoff is detected.
Q_INVOKABLE QByteArray exportToJSON(const QStringList &uuids) const
Generate JSON content for selected flights.
int count
Number of recorded flights.
Definition FlightLog.h:99
Q_INVOKABLE void showTrack(const QString &uuid)
Show a flight's track on the map.
static Q_INVOKABLE GeoMaps::Waypoint nearestAirfield(const QGeoCoordinate &position={}, double proximityM=5000.0)
Find the nearest airfield within 5 km.
Q_INVOKABLE void removeFlights(const QStringList &uuids)
Remove multiple flights from the log by UUID.
Q_INVOKABLE void hideTrack()
Hide the currently displayed track from the map.
bool showCurrentFlightTrace
Whether the live trace of the current flight is shown on map.
Definition FlightLog.h:133
QString displayedTrackUuid
UUID of the flight whose track is displayed, or empty string if none.
Definition FlightLog.h:116
Q_INVOKABLE void endFlight()
Manually end the current in-flight recording.
void trackRecordingChanged()
Notifier signal.
void deferredInitialization() override
Non-constructor initialization.
Q_INVOKABLE void clearFlights()
Remove all flights from the log.
void showCurrentFlightTraceChanged()
Notifier signal.
Q_INVOKABLE void removeTrack(const QString &uuid)
Delete the recorded track for a flight.
bool trackRecording
Whether GPS track recording is enabled.
Definition FlightLog.h:124
static Q_INVOKABLE Flightlog::Flight createFlight(const QString &departureICAO, const QString &arrivalICAO, const QString &date, const QString &offBlockTimeStr, const QString &startTimeStr, const QString &landingTimeStr, const QString &onBlockTimeStr, const QString &pilotName, const QString &aircraftCallsign, const QString &comments)
Create a Flight value from individual field strings.
QList< Flightlog::Flight > flights
List of all recorded flights, newest first.
Definition FlightLog.h:91
Q_INVOKABLE void addFlight(const Flightlog::Flight &flight)
Add a new flight to the log.
Q_INVOKABLE void updateFlight(const QString &uuid, const Flightlog::Flight &flight)
Update an existing flight in the log.
void landingDetected(const QString &time)
Emitted when a landing is detected.
Q_INVOKABLE QByteArray exportToIGC(const QString &uuid) const
Get IGC track content for a flight.
Q_INVOKABLE void removeFlight(const QString &uuid)
Remove a flight from the log by UUID.
QGeoPath displayedTrackPath
Coordinates of the track currently displayed on the map.
Definition FlightLog.h:108
GPS track recorder and IGC file manager.
A single flight log entry.
Definition Flight.h:41
GlobalObject(QObject *parent=nullptr)
Standard constructor.
static Q_INVOKABLE Flightlog::FlightLog * flightLog()
Pointer to appplication-wide static FlightLog instance.