diff --git a/src/Settings/App.SettingsGroup.json b/src/Settings/App.SettingsGroup.json index a86453df89db0ae955468a428e3bfa5168e3de80..eaa88ac86840772c3afccf951a98e5f902f98c8a 100644 --- a/src/Settings/App.SettingsGroup.json +++ b/src/Settings/App.SettingsGroup.json @@ -263,5 +263,12 @@ "longDescription": "Use Link Pairing.", "type": "bool", "defaultValue": false +}, +{ + "name": "saveCsvTelemetry", + "shortDescription": "Save CSV Telementry Logs", + "longDescription": "If this option is enabled, all Facts will be written to a CSV file with a 1 Hertz frequency.", + "type": "bool", + "defaultValue": false } ] diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index b53690392e28e40415efaa419039dc5e4eecdb4c..7d33e95dc606b81558b23392cfcbeb9445f2b5b4 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -97,6 +97,7 @@ DECLARE_SETTINGSFACT(AppSettings, enableMicrohard) DECLARE_SETTINGSFACT(AppSettings, language) DECLARE_SETTINGSFACT(AppSettings, disableAllPersistence) DECLARE_SETTINGSFACT(AppSettings, usePairing) +DECLARE_SETTINGSFACT(AppSettings, saveCsvTelemetry) DECLARE_SETTINGSFACT_NO_FUNC(AppSettings, indoorPalette) { diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index 6cee9d65d9d76b872134e5eef77573b15122d5d0..6ecbb042475f426c12785b4bef90f58d8a6360b2 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -52,6 +52,7 @@ public: DEFINE_SETTINGFACT(language) DEFINE_SETTINGFACT(disableAllPersistence) DEFINE_SETTINGFACT(usePairing) + DEFINE_SETTINGFACT(saveCsvTelemetry) // Although this is a global setting it only affects ArduPilot vehicle since PX4 automatically starts the stream from the vehicle side DEFINE_SETTINGFACT(apmStartMavlinkStreams) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 02a98daad7489c159be803bfda2e9b00fab55f9a..2419ed135c8774bbeeb99aed626d5872da7899f8 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -111,6 +111,7 @@ Vehicle::Vehicle(LinkInterface* link, , _soloFirmware(false) , _toolbox(qgcApp()->toolbox()) , _settingsManager(_toolbox->settingsManager()) + , _csvLogTimer(this) , _joystickMode(JoystickModeRC) , _joystickEnabled(false) , _uas(nullptr) @@ -302,6 +303,10 @@ Vehicle::Vehicle(LinkInterface* link, connect(&_adsbTimer, &QTimer::timeout, this, &Vehicle::_adsbTimerTimeout); _adsbTimer.setSingleShot(false); _adsbTimer.start(1000); + + // Start csv logger + connect(&_csvLogTimer, &QTimer::timeout, this, &Vehicle::_writeCsvLine); + _csvLogTimer.start(1000); } // Disconnected Vehicle for offline editing @@ -323,6 +328,7 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _soloFirmware(false) , _toolbox(qgcApp()->toolbox()) , _settingsManager(_toolbox->settingsManager()) + , _csvLogTimer(this) , _joystickMode(JoystickModeRC) , _joystickEnabled(false) , _uas(nullptr) @@ -4005,6 +4011,63 @@ void Vehicle::_pidTuningAdjustRates(bool setRatesForTuning) _setpointFactGroup.setLiveUpdates(setRatesForTuning); } +void Vehicle::_initializeCsv() +{ + if(!_toolbox->settingsManager()->appSettings()->saveCsvTelemetry()->rawValue().toBool()){ + return; + } + QString now = QDateTime::currentDateTime().toString("yyyy-MM-dd hh-mm-ss"); + QString fileName = QString("%1 vehicle%2.csv").arg(now).arg(_id); + QDir saveDir(_toolbox->settingsManager()->appSettings()->telemetrySavePath()); + _csvLogFile.setFileName(saveDir.absoluteFilePath(fileName)); + + if (!_csvLogFile.open(QIODevice::Append)) { + qCWarning(VehicleLog) << "unable to open file for csv logging, Stopping csv logging!"; + return; + } + + QTextStream stream(&_csvLogFile); + QStringList allFactNames; + allFactNames << factNames(); + for (const QString& groupName: factGroupNames()) { + for(const QString& factName: getFactGroup(groupName)->factNames()){ + allFactNames << QString("%1.%2").arg(groupName, factName); + } + } + qCDebug(VehicleLog) << "Facts logged to csv:" << allFactNames; + stream << "Timestamp," << allFactNames.join(",") << "\n"; +} + +void Vehicle::_writeCsvLine() +{ + // Only save the logs after the the vehicle gets armed, unless "Save logs even if vehicle was not armed" is checked + if(!_csvLogFile.isOpen() && + (_armed || _toolbox->settingsManager()->appSettings()->telemetrySaveNotArmed()->rawValue().toBool())){ + _initializeCsv(); + } + + if(!_csvLogFile.isOpen()){ + return; + } + + QStringList allFactValues; + QTextStream stream(&_csvLogFile); + + // Write timestamp to csv file + allFactValues << QDateTime::currentDateTime().toString(); + // Write Vehicle's own facts + for (const QString& factName : factNames()) { + allFactValues << getFact(factName)->cookedValueString(); + } + // write facts from Vehicle's FactGroups + for (const QString& groupName: factGroupNames()) { + for (const QString& factName : getFactGroup(groupName)->factNames()) { + allFactValues << getFactGroup(groupName)->getFact(factName)->cookedValueString(); + } + } + + stream << allFactValues.join(",") << "\n"; +} #if !defined(NO_ARDUPILOT_DIALECT) void Vehicle::flashBootloader(void) diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index b989976ab326e02a409409499b94781a63a17060..56d0fa2542fc1901954d46095a26256a46f9f9e4 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -1331,6 +1331,8 @@ private: void _pidTuningAdjustRates(bool setRatesForTuning); void _handleUnsupportedRequestAutopilotCapabilities(void); void _handleUnsupportedRequestProtocolVersion(void); + void _initializeCsv(); + void _writeCsvLine(); int _id; ///< Mavlink system id int _defaultComponentId; @@ -1347,6 +1349,9 @@ private: QGCToolbox* _toolbox; SettingsManager* _settingsManager; + QTimer _csvLogTimer; + QFile _csvLogFile; + QList _links; JoystickMode_t _joystickMode; diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 411ca6abb3f248acdae8b8e4655f37450c88ab61..1ad8c600b65713f00e8440df83b2aeb2644a982b 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -420,6 +420,14 @@ Rectangle { enabled: promptSaveLog.checked && !disableDataPersistence.checked property Fact _telemetrySaveNotArmed: QGroundControl.settingsManager.appSettings.telemetrySaveNotArmed } + FactCheckBox { + id: promptSaveCsv + text: qsTr("Save CSV log of telemetry data") + fact: _saveCsvTelemetry + visible: _saveCsvTelemetry.visible + enabled: !disableDataPersistence.checked + property Fact _saveCsvTelemetry: QGroundControl.settingsManager.appSettings.saveCsvTelemetry + } } }