QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
CameraCalc.cc
Go to the documentation of this file.
1#include "CameraCalc.h"
2#include "JsonHelper.h"
3#include "Vehicle.h"
4#include "CameraMetaData.h"
6
7#include <QtQml/QQmlEngine>
8
9CameraCalc::CameraCalc(PlanMasterController* masterController, const QString& settingsGroup, QObject* parent)
10 : CameraSpec (settingsGroup, parent)
11 , _distanceMode (masterController->missionController()->globalAltitudeModeDefault())
12 , _knownCameraList (masterController->controllerVehicle()->staticCameraList())
13 , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraCalc.FactMetaData.json"), this))
14 , _cameraNameFact (settingsGroup, _metaDataMap[cameraNameName])
15 , _valueSetIsDistanceFact (settingsGroup, _metaDataMap[valueSetIsDistanceName])
16 , _distanceToSurfaceFact (settingsGroup, _metaDataMap[distanceToSurfaceName])
17 , _imageDensityFact (settingsGroup, _metaDataMap[imageDensityName])
18 , _frontalOverlapFact (settingsGroup, _metaDataMap[frontalOverlapName])
19 , _sideOverlapFact (settingsGroup, _metaDataMap[sideOverlapName])
20 , _adjustedFootprintSideFact (settingsGroup, _metaDataMap[adjustedFootprintSideName])
21 , _adjustedFootprintFrontalFact (settingsGroup, _metaDataMap[adjustedFootprintFrontalName])
22{
23 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
24
25 connect(&_valueSetIsDistanceFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
26 connect(&_distanceToSurfaceFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
27 connect(&_imageDensityFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
28 connect(&_frontalOverlapFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
29 connect(&_sideOverlapFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
30 connect(&_adjustedFootprintSideFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
31 connect(&_adjustedFootprintFrontalFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
32 connect(&_cameraNameFact, &Fact::valueChanged, this, &CameraCalc::_setDirty);
33 connect(this, &CameraCalc::distanceModeChanged, this, &CameraCalc::_setDirty);
34
35 connect(&_cameraNameFact, &Fact::valueChanged, this, &CameraCalc::_cameraNameChanged);
36 connect(&_cameraNameFact, &Fact::valueChanged, this, &CameraCalc::isManualCameraChanged);
37 connect(&_cameraNameFact, &Fact::valueChanged, this, &CameraCalc::isCustomCameraChanged);
38
39 connect(&_distanceToSurfaceFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
40 connect(&_imageDensityFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
41 connect(&_frontalOverlapFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
42 connect(&_sideOverlapFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
43 connect(sensorWidth(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
44 connect(sensorHeight(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
45 connect(imageWidth(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
46 connect(imageHeight(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
47 connect(focalLength(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
48 connect(landscape(), &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance);
49
50 // Build the brand list from known cameras
51 _cameraBrandList.append(xlatManualCameraName());
52 _cameraBrandList.append(xlatCustomCameraName());
53 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
54 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
55 if (!_cameraBrandList.contains(cameraMetaData->brand)) {
56 _cameraBrandList.append(cameraMetaData->brand);
57 }
58 }
59
60 _cameraNameChanged();
61 _setBrandModelFromCanonicalName(_cameraNameFact.rawValue().toString());
62
63 setDirty(false);
64}
65
66void CameraCalc::_cameraNameChanged(void)
67{
68 if (_disableRecalc) {
69 return;
70 }
71
72 QString cameraName = _cameraNameFact.rawValue().toString();
73
74 if (isManualCamera() || isCustomCamera()) {
75 fixedOrientation()->setRawValue(false);
76 minTriggerInterval()->setRawValue(0);
77 if (isManualCamera() && !valueSetIsDistance()->rawValue().toBool()) {
78 valueSetIsDistance()->setRawValue(true);
79 }
80 } else {
81 // Look for known camera
82 CameraMetaData* knownCameraMetaData = nullptr;
83 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
84 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
85 if (cameraName == cameraMetaData->canonicalName) {
86 knownCameraMetaData = cameraMetaData;
87 break;
88 }
89 }
90
91 if (!knownCameraMetaData) {
92 // Lookup failed. Force to custom as fallback.
93 // This will cause another camera changed signal which will recurse back into this routine
94 _cameraNameFact.setRawValue(canonicalCustomCameraName());
95 return;
96 }
97
98 _disableRecalc = true;
99
100 sensorWidth()->setRawValue (knownCameraMetaData->sensorWidth);
101 sensorHeight()->setRawValue (knownCameraMetaData->sensorHeight);
102 imageWidth()->setRawValue (knownCameraMetaData->imageWidth);
103 imageHeight()->setRawValue (knownCameraMetaData->imageHeight);
104 focalLength()->setRawValue (knownCameraMetaData->focalLength);
105 landscape()->setRawValue (knownCameraMetaData->landscape);
106 fixedOrientation()->setRawValue (knownCameraMetaData->fixedOrientation);
107 minTriggerInterval()->setRawValue (knownCameraMetaData->minTriggerInterval);
108
109 _disableRecalc = false;
110 }
111
112 _recalcTriggerDistance();
114 // Manual grids support absolute alts whereas nothing else does. Make sure we are not left in absolute
116 }
117}
118
119void CameraCalc::_recalcTriggerDistance(void)
120{
121 if (_disableRecalc || isManualCamera()) {
122 return;
123 }
124
125 _disableRecalc = true;
126
127 double focalLength = this->focalLength()->rawValue().toDouble();
128 double sensorWidth = this->sensorWidth()->rawValue().toDouble();
129 double sensorHeight = this->sensorHeight()->rawValue().toDouble();
130 double imageWidth = this->imageWidth()->rawValue().toDouble();
131 double imageHeight = this->imageHeight()->rawValue().toDouble();
132 double imageDensity = _imageDensityFact.rawValue().toDouble();
133
134 if (focalLength <= 0 || sensorWidth <= 0 || sensorHeight <= 0 || imageWidth <= 0 || imageHeight <= 0 || imageDensity <= 0) {
135 return;
136 }
137
138 if (_valueSetIsDistanceFact.rawValue().toBool()) {
139 _imageDensityFact.setRawValue((_distanceToSurfaceFact.rawValue().toDouble() * sensorWidth * 100.0) / (imageWidth * focalLength));
140 } else {
141 _distanceToSurfaceFact.setRawValue((imageWidth * _imageDensityFact.rawValue().toDouble() * focalLength) / (sensorWidth * 100.0));
142 }
143
144 imageDensity = _imageDensityFact.rawValue().toDouble();
145
146 if (landscape()->rawValue().toBool()) {
147 _imageFootprintSide = (imageWidth * imageDensity) / 100.0;
148 _imageFootprintFrontal = (imageHeight * imageDensity) / 100.0;
149 } else {
150 _imageFootprintSide = (imageHeight * imageDensity) / 100.0;
151 _imageFootprintFrontal = (imageWidth * imageDensity) / 100.0;
152 }
153 _adjustedFootprintSideFact.setRawValue (_imageFootprintSide * ((100.0 - _sideOverlapFact.rawValue().toDouble()) / 100.0));
154 _adjustedFootprintFrontalFact.setRawValue (_imageFootprintFrontal * ((100.0 - _frontalOverlapFact.rawValue().toDouble()) / 100.0));
155
156 emit imageFootprintSideChanged (_imageFootprintSide);
157 emit imageFootprintFrontalChanged (_imageFootprintFrontal);
158
159 _disableRecalc = false;
160}
161
162void CameraCalc::save(QJsonObject& json) const
163{
165 json[adjustedFootprintSideName] = _adjustedFootprintSideFact.rawValue().toDouble();
166 json[adjustedFootprintFrontalName] = _adjustedFootprintFrontalFact.rawValue().toDouble();
167 json[distanceToSurfaceName] = _distanceToSurfaceFact.rawValue().toDouble();
168 json[distanceModeName] = _distanceMode;
169 json[cameraNameName] = _cameraNameFact.rawValue().toString();
170
171 if (!isManualCamera()) {
172 CameraSpec::save(json);
173 json[valueSetIsDistanceName] = _valueSetIsDistanceFact.rawValue().toBool();
174 json[imageDensityName] = _imageDensityFact.rawValue().toDouble();
175 json[frontalOverlapName] = _frontalOverlapFact.rawValue().toDouble();
176 json[sideOverlapName] = _sideOverlapFact.rawValue().toDouble();
177 }
178}
179
180bool CameraCalc::load(const QJsonObject& originalJson, bool deprecatedFollowTerrain, QString& errorString, bool forPresets)
181{
182 QJsonObject json = originalJson;
183
184 int version = 0;
185 if (json.contains(JsonHelper::jsonVersionKey)) {
186 version = json[JsonHelper::jsonVersionKey].toInt();
187 }
188
189 if (version == 0) {
190 // Version 0->1 differences:
191 // - JsonHelper::jsonVersionKey not stored
192 // - _jsonCameraSpecTypeKeyDeprecated is only in v0 files and stores CameraSpecType. V2 files store same info in cameraNameName.
193 // - _jsonCameraNameKey only set if CameraSpecKnown
194 int cameraSpec = json[_jsonCameraSpecTypeKeyDeprecated].toInt(CameraSpecNone);
195 if (cameraSpec == CameraSpecCustom) {
197 } else if (cameraSpec == CameraSpecNone) {
199 }
200 json.remove(_jsonCameraSpecTypeKeyDeprecated);
201 version = 1;
202 }
203 if (version == 1) {
204 // Version 1->2 differences:
205 // - _jsonDistanceToSurfaceRelativeKeyDeprecated changed to distanceMode
206 // - deprecatedFollowTerrain value was loaded from upper level callers and represents AltitudeModeCalcAboveTerrain. AtitudeModeTerrainFrame was not supported yet.
207 if (deprecatedFollowTerrain) {
209 } else {
210 json[distanceModeName] = json[_jsonDistanceToSurfaceRelativeKeyDeprecated].toBool() ? QGroundControlQmlGlobal::AltitudeModeRelative : QGroundControlQmlGlobal::AltitudeModeAbsolute;
211 }
212 json.remove(_jsonDistanceToSurfaceRelativeKeyDeprecated);
213 version = 2;
214 }
215 if (version != 2) {
216 errorString = tr("CameraCalc section version %1 not supported").arg(version);
217 return false;
218 }
219
220 QList<JsonHelper::KeyValidateInfo> keyInfoList1 = {
221 { cameraNameName, QJsonValue::String, true },
222 { adjustedFootprintSideName, QJsonValue::Double, true },
223 { adjustedFootprintFrontalName, QJsonValue::Double, true },
224 { distanceToSurfaceName, QJsonValue::Double, true },
225 { distanceModeName, QJsonValue::Double, true },
226 };
227 if (!JsonHelper::validateKeys(json, keyInfoList1, errorString)) {
228 return false;
229 }
230
231 _disableRecalc = !forPresets;
232
233 // We have to clean up camera names. Older builds incorrectly used translated the camera names in the persisted plan file.
234 // Newer builds use a canonical english camera name in plan files.
235 QString canonicalCameraName = _validCanonicalCameraName(json[cameraNameName].toString());
236 _cameraNameFact.setRawValue(canonicalCameraName);
237
239
240 _adjustedFootprintSideFact.setRawValue (json[adjustedFootprintSideName].toDouble());
241 _adjustedFootprintFrontalFact.setRawValue (json[adjustedFootprintFrontalName].toDouble());
242 _distanceToSurfaceFact.setRawValue (json[distanceToSurfaceName].toDouble());
243
244 if (!isManualCamera()) {
245 QList<JsonHelper::KeyValidateInfo> keyInfoList2 = {
246 { valueSetIsDistanceName, QJsonValue::Bool, true },
247 { imageDensityName, QJsonValue::Double, true },
248 { frontalOverlapName, QJsonValue::Double, true },
249 { sideOverlapName, QJsonValue::Double, true },
250 };
251 if (!JsonHelper::validateKeys(json, keyInfoList2, errorString)) {
252 _disableRecalc = false;
253 return false;
254 }
255
256 _valueSetIsDistanceFact.setRawValue (json[valueSetIsDistanceName].toBool());
257 _frontalOverlapFact.setRawValue (json[frontalOverlapName].toDouble());
258 _sideOverlapFact.setRawValue (json[sideOverlapName].toDouble());
259 _imageDensityFact.setRawValue (json[imageDensityName].toDouble());
260
261 if (!CameraSpec::load(json, errorString)) {
262 _disableRecalc = false;
263 return false;
264 }
265 }
266
267 _disableRecalc = false;
268
269 _setBrandModelFromCanonicalName(canonicalCameraName);
270
271 return true;
272}
273
275{
276 // This string should NOT be translated
277 return "Custom Camera";
278}
279
281{
282 // This string should NOT be translated
283 return "Manual (no camera specs)";
284}
285
286QString CameraCalc::xlatCustomCameraName(void)
287{
288 return tr("Custom Camera");
289}
290
292{
293 return tr("Manual (no camera specs)");
294}
295
297{
298 if (altMode != _distanceMode) {
299 _distanceMode = altMode;
300 emit distanceModeChanged(_distanceMode);
301 }
302}
303
304void CameraCalc::_setDirty(void)
305{
306 setDirty(true);
307}
308
309void CameraCalc::setCameraBrand(const QString& cameraBrand)
310{
311 // Note that cameraBrand can also be manual or custom camera
312
313 if (cameraBrand != _cameraBrand) {
314 QString newCameraName = cameraBrand;
315
316 _cameraBrand = cameraBrand;
317 _cameraModel.clear();
318
319 if (_cameraBrand != xlatManualCameraName() && _cameraBrand != xlatCustomCameraName()) {
320 CameraMetaData* firstCameraMetaData = nullptr;
321 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
322 firstCameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
323 if (firstCameraMetaData->brand == _cameraBrand) {
324 break;
325 }
326 }
327 newCameraName = firstCameraMetaData->canonicalName;
328 _cameraModel = firstCameraMetaData->model;
329 }
330 emit cameraBrandChanged();
331 emit cameraModelChanged();
332
333 _rebuildCameraModelList();
334
335 _cameraNameFact.setRawValue(newCameraName);
336 }
337}
338
339void CameraCalc::setCameraModel(const QString& cameraModel)
340{
341 if (cameraModel != _cameraModel) {
342 _cameraModel = cameraModel;
343 emit cameraModelChanged();
344
345 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
346 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
347 if (cameraMetaData->brand == _cameraBrand && cameraMetaData->model == _cameraModel) {
348 _cameraNameFact.setRawValue(cameraMetaData->canonicalName);
349 break;
350 }
351 }
352
353 }
354}
355
356void CameraCalc::_setBrandModelFromCanonicalName(const QString& cameraName)
357{
358 _cameraBrand = cameraName;
359 _cameraModel.clear();
360 _cameraModelList.clear();
361
362 if (cameraName != canonicalManualCameraName() && cameraName != canonicalCustomCameraName()) {
363 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
364 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
365 if (cameraMetaData->canonicalName == cameraName) {
366 _cameraBrand = cameraMetaData->brand;
367 _cameraModel = cameraMetaData->model;
368 break;
369 }
370 }
371 }
372 emit cameraBrandChanged();
373 emit cameraModelChanged();
374
375 _rebuildCameraModelList();
376}
377
378void CameraCalc::_rebuildCameraModelList(void)
379{
380 _cameraModelList.clear();
381
382 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
383 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
384 if (cameraMetaData->brand == _cameraBrand) {
385 _cameraModelList.append(cameraMetaData->model);
386 }
387 }
388
390}
391
392void CameraCalc::_setCameraNameFromV3TransectLoad(const QString& cameraName)
393{
394 // We don't recalc here since the rest of the camera values are already loaded from the json
395 _disableRecalc = true;
396 QString canonicalCameraName = _validCanonicalCameraName(cameraName);
397 _cameraNameFact.setRawValue(cameraName);
398 _disableRecalc = true;
399
400 _setBrandModelFromCanonicalName(canonicalCameraName);
401}
402
403QString CameraCalc::_validCanonicalCameraName(const QString& cameraName)
404{
405 QString canonicalCameraName = cameraName;
406
407 if (canonicalCameraName != canonicalCustomCameraName() && canonicalCameraName != canonicalManualCameraName()) {
408 if (cameraName == xlatManualCameraName()) {
409 canonicalCameraName = canonicalManualCameraName();
410 } else if (cameraName == xlatCustomCameraName()) {
411 canonicalCameraName = canonicalCustomCameraName();
412 } else {
413 // Look for known camera
414 for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
415 CameraMetaData* cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
416 if (cameraName == cameraMetaData->canonicalName || cameraName == cameraMetaData->deprecatedTranslatedName) {
417 return cameraMetaData->canonicalName;
418 }
419 }
420
421 canonicalCameraName = canonicalCustomCameraName();
422 }
423 }
424
425 return canonicalCameraName;
426}
QString errorString
static constexpr const char * distanceModeName
Definition CameraCalc.h:86
void save(QJsonObject &json) const
void _setCameraNameFromV3TransectLoad(const QString &cameraName)
static constexpr const char * sideOverlapName
Definition CameraCalc.h:89
void cameraModelListChanged(void)
Fact * imageDensity(void)
Definition CameraCalc.h:54
QString xlatCustomCameraName READ xlatCustomCameraName static CONSTANT(QString xlatManualCameraName READ xlatManualCameraName CONSTANT) 1(bool isManualCamera READ isManualCamera NOTIFY isManualCameraChanged) 1(bool isCustomCamera READ isCustomCamera NOTIFY isCustomCameraChanged) 1(QString cameraBrand MEMBER _cameraBrand WRITE setCameraBrand NOTIFY cameraBrandChanged) 1(QString cameraModel MEMBER _cameraModel WRITE setCameraModel NOTIFY cameraModelChanged) 1(QStringList cameraBrandList MEMBER _cameraBrandList CONSTANT) 1(QStringList cameraModelList MEMBER _cameraModelList NOTIFY cameraModelListChanged) 1(Fact *valueSetIsDistance READ valueSetIsDistance CONSTANT) 1(Fact *distanceToSurface READ distanceToSurface CONSTANT) 1(Fact *imageDensity READ imageDensity CONSTANT) 1(Fact *frontalOverlap READ frontalOverlap CONSTANT) 1(Fact *sideOverlap READ sideOverlap CONSTANT) 1(Fact *adjustedFootprintSide READ adjustedFootprintSide CONSTANT) 1(Fact *adjustedFootprintFrontal READ adjustedFootprintFrontal CONSTANT) 1(QGroundControlQmlGlobal QStrin xlatManualCameraName)(void)
< User visible camera name for custom camera setting
Definition CameraCalc.h:48
CameraCalc(PlanMasterController *masterController, const QString &settingsGroup, QObject *parent=nullptr)
Definition CameraCalc.cc:9
static constexpr const char * frontalOverlapName
Definition CameraCalc.h:88
static constexpr const char * adjustedFootprintFrontalName
Definition CameraCalc.h:90
bool isCustomCamera(void) const
Definition CameraCalc.h:69
bool isManualCamera(void) const
Definition CameraCalc.h:68
void setDistanceMode(QGroundControlQmlGlobal::AltMode altMode)
void imageFootprintSideChanged(double imageFootprintSide)
static constexpr const char * adjustedFootprintSideName
Definition CameraCalc.h:91
static constexpr const char * valueSetIsDistanceName
Definition CameraCalc.h:84
void cameraBrandChanged(void)
static constexpr const char * cameraNameName
Definition CameraCalc.h:83
void distanceModeChanged(int altMode)
void isCustomCameraChanged(void)
bool load(const QJsonObject &json, bool deprecatedFollowTerrain, QString &errorString, bool forPresets)
QGroundControlQmlGlobal::AltMode distanceMode(void) const
Definition CameraCalc.h:72
static QString canonicalCustomCameraName(void)
void setCameraBrand(const QString &cameraBrand)
static constexpr const char * distanceToSurfaceName
Definition CameraCalc.h:85
static constexpr const char * imageDensityName
Definition CameraCalc.h:87
void isManualCameraChanged(void)
Fact * valueSetIsDistance(void)
Definition CameraCalc.h:52
void cameraModelChanged(void)
void imageFootprintFrontalChanged(double imageFootprintFrontal)
void setCameraModel(const QString &cameraModel)
static QString canonicalManualCameraName(void)
Set of meta data which describes a camera available on the vehicle.
SettingsFact * minTriggerInterval(void)
Definition CameraSpec.h:31
SettingsFact * imageHeight(void)
Definition CameraSpec.h:27
SettingsFact * fixedOrientation(void)
Definition CameraSpec.h:30
void save(QJsonObject &json) const
Definition CameraSpec.cc:44
SettingsFact * focalLength(void)
Definition CameraSpec.h:28
void setDirty(bool dirty)
Definition CameraSpec.cc:36
SettingsFact * sensorHeight(void)
Definition CameraSpec.h:25
SettingsFact * imageWidth(void)
Definition CameraSpec.h:26
bool load(const QJsonObject &json, QString &errorString)
Definition CameraSpec.cc:56
SettingsFact * landscape(void)
Definition CameraSpec.h:29
void rawValueChanged(const QVariant &value)
void valueChanged(const QVariant &value)
This signal is only meant for use by the QT property system. It should not be connected to by client ...
Master controller for mission, fence, rally.
constexpr const char * jsonVersionKey
Definition JsonHelper.h:109
bool validateKeys(const QJsonObject &jsonObject, const QList< KeyValidateInfo > &keyInfo, QString &errorString)