9 #include <QRegularExpression>
10 #include <QRegularExpressionMatch>
11 #include <QStringList>
30 using namespace swift::misc::physical_quantities;
31 using namespace swift::misc::aviation;
33 namespace swift::misc::weather
41 class IMetarDecoderPart
45 virtual ~IMetarDecoderPart() =
default;
48 virtual QString getDecoderType()
const = 0;
52 virtual const QRegularExpression &getRegExp()
const = 0;
54 virtual bool isRepeatable()
const {
return false; }
55 virtual bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const = 0;
56 virtual bool isMandatory()
const = 0;
60 bool parse(QString &metarString, CMetar &metar)
63 const QRegularExpression re(getRegExp());
64 Q_ASSERT(re.isValid());
69 QRegularExpressionMatch match = re.match(metarString);
73 if (!validateAndSet(match, metar)) {
return false; }
76 metarString.replace(re, QString());
82 if (!isMandatory()) { isValid =
true; }
86 while (isRepeatable());
90 CLogMessage(
static_cast<CMetarDecoder *
>(
nullptr)).debug()
91 <<
"Failed to match" << getDecoderType() <<
"in remaining METAR:" << metarString;
98 class CMetarDecoderReportType :
public IMetarDecoderPart
101 QString getDecoderType()
const override {
return "ReportType"; }
104 const QRegularExpression &getRegExp()
const override
106 static const QRegularExpression re(QStringLiteral(
"^(?<reporttype>METAR|SPECI)? "));
110 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
112 QString reportTypeAsString = match.captured(
"reporttype");
113 if (reportTypeAsString.isEmpty() || !getReportTypeHash().contains(reportTypeAsString)) {
return false; }
115 metar.setReportType(getReportTypeHash().value(reportTypeAsString));
119 bool isMandatory()
const override {
return false; }
125 {
"SPECI", CMetar::SPECI } };
130 class CMetarDecoderAirport :
public IMetarDecoderPart
133 QString getDecoderType()
const override {
return "Airport"; }
136 const QRegularExpression &getRegExp()
const override
138 static const QRegularExpression re(QStringLiteral(
"^(?<airport>\\w{4}) "));
142 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
144 QString airportAsString = match.captured(
"airport");
145 Q_ASSERT(!airportAsString.isEmpty());
150 bool isMandatory()
const override {
return true; }
153 class CMetarDecoderDayTime :
public IMetarDecoderPart
156 QString getDecoderType()
const override {
return "DayTime"; }
159 const QRegularExpression &getRegExp()
const override
161 static const QRegularExpression re(QStringLiteral(
"^(?<day>\\d{2})(?<hour>\\d{2})(?<minute>\\d{2})Z "));
165 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
168 int day = match.captured(
"day").toInt(&ok);
169 int hour = match.captured(
"hour").toInt(&ok);
170 int minute = match.captured(
"minute").toInt(&ok);
171 if (!ok)
return false;
173 if (day < 1 || day > 31)
return false;
174 if (hour < 0 || hour > 23)
return false;
175 if (minute < 0 || minute > 59)
return false;
177 physical_quantities::CTime time(hour, minute, 0);
178 metar.setDayTime(day, time);
182 bool isMandatory()
const override {
return true; }
185 class CMetarDecoderStatus :
public IMetarDecoderPart
188 QString getDecoderType()
const override {
return "Status"; }
195 const QRegularExpression &getRegExp()
const override
197 static const QRegularExpression re(QStringLiteral(
"^([A-Z]+) "));
201 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
203 if (match.captured(1) ==
"AUTO")
205 metar.setAutomated(
true);
208 else if (match.captured(1) ==
"NIL") {
return true; }
209 else if (match.captured(1).size() == 3) {
return true; }
210 else {
return false; }
213 bool isMandatory()
const override {
return false; }
216 class CMetarDecoderWind :
public IMetarDecoderPart
219 QString getDecoderType()
const override {
return "Wind"; }
231 const QRegularExpression &getRegExp()
const override
233 static const QRegularExpression re(getRegExpImpl());
237 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
240 QString directionAsString = match.captured(
"direction");
241 if (directionAsString ==
"///")
return true;
243 bool directionVariable =
false;
244 if (directionAsString ==
"VRB") { directionVariable =
true; }
247 direction = directionAsString.toInt(&ok);
248 if (!ok)
return false;
251 QString speedAsString = match.captured(
"speed");
252 if (speedAsString ==
"//")
return true;
253 int speed = speedAsString.toInt(&ok);
254 if (!ok)
return false;
255 QString gustAsString = match.captured(
"gustSpeed");
257 if (!gustAsString.isEmpty())
259 gustSpeed = gustAsString.toInt(&ok);
260 if (!ok)
return false;
262 QString unitAsString = match.captured(
"unit");
263 if (!getWindUnitHash().contains(unitAsString))
return false;
267 CSpeed(speed, getWindUnitHash().value(unitAsString)),
268 CSpeed(gustSpeed, getWindUnitHash().value(unitAsString)));
269 windLayer.setDirectionVariable(directionVariable);
270 metar.setWindLayer(windLayer);
274 bool isMandatory()
const override {
return false; }
277 QString getRegExpImpl()
const
280 const QString direction = QStringLiteral(
"(?<direction>\\d{3}|VRB|/{3})");
282 const QString speed = QStringLiteral(
"(?<speed>\\d{2,3}|/{2})");
284 const QString gustSpeed = QStringLiteral(
"(G(?<gustSpeed>\\d{2,3}))?");
286 const QString unit = QStringLiteral(
"(?<unit>") + QStringList(getWindUnitHash().keys()).join(
'|') +
")";
288 const QString regexp =
"^" + direction + speed + gustSpeed + unit +
" ?";
293 class CMetarDecoderVariationsWindDirection :
public IMetarDecoderPart
296 QString getDecoderType()
const override {
return "WindDirection"; }
299 const QRegularExpression &getRegExp()
const override
301 static const QRegularExpression re(getRegExpImpl());
305 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
307 QString directionFromAsString = match.captured(
"direction_from");
308 QString directionToAsString = match.captured(
"direction_to");
310 int directionFrom = 0;
314 directionFrom = directionFromAsString.toInt(&ok);
315 directionTo = directionToAsString.toInt(&ok);
316 if (!ok)
return false;
318 auto windLayer = metar.getWindLayer();
320 metar.setWindLayer(windLayer);
324 bool isMandatory()
const override {
return false; }
327 QString getRegExpImpl()
const
330 const QString directionFrom(
"(?<direction_from>\\d{3})V");
332 const QString directionTo(
"(?<direction_to>\\d{3})");
334 const QString regexp =
"^" + directionFrom + directionTo +
" ";
339 class CMetarDecoderVisibility :
public IMetarDecoderPart
342 QString getDecoderType()
const override {
return "Visibility"; }
348 {
"N",
"north" }, {
"NE",
"north-east" }, {
"E",
"east" }, {
"SE",
"south-east" },
349 {
"S",
"south" }, {
"SW",
"south-west" }, {
"W",
"west" }, {
"NW",
"north-west" },
354 const QRegularExpression &getRegExp()
const override
356 static const QRegularExpression re(getRegExpImpl());
360 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
363 if (!match.captured(
"cavok").isEmpty())
368 QString visibilityAsString = match.captured(
"visibility");
369 if (visibilityAsString ==
"////")
return true;
371 double visibility = 0;
372 if (!visibilityAsString.isEmpty())
374 visibility = visibilityAsString.toDouble(&ok);
375 if (!ok)
return false;
380 QString distanceAsString = match.captured(
"distance");
381 if (!distanceAsString.isEmpty())
383 visibility += distanceAsString.toDouble(&ok);
384 if (!ok)
return false;
386 QString numeratorAsString = match.captured(
"numerator");
387 QString denominatorAsString = match.captured(
"denominator");
388 if (!numeratorAsString.isEmpty() && !denominatorAsString.isEmpty())
391 double numerator = numeratorAsString.toDouble(&ok);
392 if (!ok)
return false;
394 double denominator = denominatorAsString.toDouble(&ok);
395 if (!ok)
return false;
396 if (denominator < 1 || numerator < 1)
return false;
397 visibility += (numerator / denominator);
400 QString unitAsString = match.captured(
"unit");
404 metar.setVisibility(
CLength(visibility, unit));
409 bool isMandatory()
const override {
return false; }
412 QString getRegExpImpl()
const
415 const QString cavok = QStringLiteral(
"(?<cavok>CAVOK)");
421 const QString visibility_eu = QStringLiteral(
"(?<visibility>\\d{4}|/{4})(NDV)?") +
"(" +
422 QStringList(getCardinalDirections().keys()).join(
'|') +
")?";
429 const QString visibility_us =
430 QStringLiteral(
"(?<distance>\\d{0,2}) ?M?((?<numerator>\\d)/(?<denominator>\\d))?(?<unit>SM|KM)");
431 const QString regexp =
"^(" + cavok +
"|" + visibility_eu +
"|" + visibility_us +
") ";
436 class CMetarDecoderRunwayVisualRange :
public IMetarDecoderPart
439 QString getDecoderType()
const override {
return "RunwayVisualRange"; }
442 const QRegularExpression &getRegExp()
const override
445 static const QRegularExpression re(getRegExpImpl());
449 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
451 QString runway = match.captured(
"runway");
452 QString runwayVisibilityAsString = match.captured(
"rwy_visibility");
453 Q_ASSERT(!runway.isEmpty() && !runwayVisibilityAsString.isEmpty());
456 double runwayVisibility = runwayVisibilityAsString.toDouble(&ok);
457 if (!ok)
return false;
462 Q_UNUSED(runwayVisibility)
467 bool isMandatory()
const override {
return false; }
470 QString getRegExpImpl()
const
479 const QString runway = QStringLiteral(
"R(?<runway>\\d{2}[LCR]*)");
481 const QString visibility = QStringLiteral(
"/[PM]?(?<rwy_visibility>\\d{4})");
483 static const QString variability = QStringLiteral(
"V?(?<variability>\\d{4})?");
485 const QString unit = QStringLiteral(
"(?<unit>FT)?");
487 const QString trend = QStringLiteral(
"/?(?<trend>[DNU])?");
488 const QString regexp =
"^" + runway + visibility + variability + unit + trend +
" ";
493 class CMetarDecoderPresentWeather :
public IMetarDecoderPart
496 QString getDecoderType()
const override {
return "PresentWeather"; }
502 {
"+", CPresentWeather::Heavy },
503 {
"VC", CPresentWeather::InVincinity } };
510 {
"MI", CPresentWeather::Shallow }, {
"BC", CPresentWeather::Patches },
511 {
"PR", CPresentWeather::Partial }, {
"DR", CPresentWeather::Drifting },
512 {
"BL", CPresentWeather::Blowing }, {
"SH", CPresentWeather::Showers },
513 {
"TS", CPresentWeather::Thunderstorm }, {
"FR", CPresentWeather::Freezing },
521 {
"DZ", CPresentWeather::Drizzle },
522 {
"RA", CPresentWeather::Rain },
523 {
"SN", CPresentWeather::Snow },
524 {
"SG", CPresentWeather::SnowGrains },
525 {
"IC", CPresentWeather::IceCrystals },
526 {
"PC", CPresentWeather::IcePellets },
527 {
"GR", CPresentWeather::Hail },
528 {
"GS", CPresentWeather::SnowPellets },
529 {
"UP", CPresentWeather::Unknown },
530 {
"BR", CPresentWeather::Mist },
531 {
"FG", CPresentWeather::Fog },
532 {
"FU", CPresentWeather::Smoke },
533 {
"VA", CPresentWeather::VolcanicAsh },
534 {
"DU", CPresentWeather::Dust },
535 {
"SA", CPresentWeather::Sand },
536 {
"HZ", CPresentWeather::Haze },
537 {
"PO", CPresentWeather::DustSandWhirls },
538 {
"SQ", CPresentWeather::Squalls },
539 {
"FC", CPresentWeather::TornadoOrWaterspout },
540 {
"FC", CPresentWeather::FunnelCloud },
541 {
"SS", CPresentWeather::Sandstorm },
542 {
"DS", CPresentWeather::Duststorm },
548 bool isRepeatable()
const override {
return true; }
550 const QRegularExpression &getRegExp()
const override
552 static const QRegularExpression re(getRegExpImpl());
556 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
558 QString intensityAsString = match.captured(
"intensity");
559 CPresentWeather::Intensity itensity = CPresentWeather::Moderate;
560 if (!intensityAsString.isEmpty()) { itensity = getIntensityHash().value(intensityAsString); }
562 QString descriptorAsString = match.captured(
"descriptor");
563 CPresentWeather::Descriptor descriptor = CPresentWeather::None;
564 if (!descriptorAsString.isEmpty()) { descriptor = getDescriptorHash().value(descriptorAsString); }
566 int weatherPhenomena = 0;
567 QString wp1AsString = match.captured(
"wp1");
568 if (!wp1AsString.isEmpty()) { weatherPhenomena |= getWeatherPhenomenaHash().value(wp1AsString); }
570 QString wp2AsString = match.captured(
"wp2");
571 if (!wp2AsString.isEmpty()) { weatherPhenomena |= getWeatherPhenomenaHash().value(wp2AsString); }
573 CPresentWeather presentWeather(itensity, descriptor, weatherPhenomena);
574 metar.addPresentWeather(presentWeather);
578 bool isMandatory()
const override {
return false; }
581 QString getRegExpImpl()
const
589 const QString qualifier_intensity(
"(?<intensity>[-+]|VC)?");
591 const QString qualifier_descriptor =
592 "(?<descriptor>" + QStringList(getDescriptorHash().keys()).join(
'|') +
")?";
593 const QString weatherPhenomenaJoined = QStringList(getWeatherPhenomenaHash().keys()).join(
'|');
594 const QString weather_phenomina1 =
"(?<wp1>" + weatherPhenomenaJoined +
")?";
595 const QString weather_phenomina2 =
"(?<wp2>" + weatherPhenomenaJoined +
")?";
596 const QString weather_phenomina3 =
"(?<wp3>" + weatherPhenomenaJoined +
")?";
597 const QString weather_phenomina4 =
"(?<wp4>" + weatherPhenomenaJoined +
")?";
598 const QString regexp =
"^(" + qualifier_intensity + qualifier_descriptor + weather_phenomina1 +
599 weather_phenomina2 + weather_phenomina3 + weather_phenomina4 +
") ";
604 class CMetarDecoderCloud :
public IMetarDecoderPart
607 QString getDecoderType()
const override {
return "Cloud"; }
610 const QStringList &getClearSkyTokens()
const
612 static const QStringList list = {
"SKC",
"NSC",
"CLR",
"NCD" };
619 {
"FEW", CCloudLayer::Few },
620 {
"SCT", CCloudLayer::Scattered },
621 {
"BKN", CCloudLayer::Broken },
622 {
"OVC", CCloudLayer::Overcast } };
626 bool isRepeatable()
const override {
return true; }
628 const QRegularExpression &getRegExp()
const override
630 static const QRegularExpression re(getRegExpImpl());
634 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
636 QString noClouds = match.captured(
"clear_sky");
637 if (!noClouds.isEmpty())
639 metar.removeAllClouds();
643 QString coverageAsString = match.captured(
"coverage");
644 QString baseAsString = match.captured(
"base");
645 Q_ASSERT(!coverageAsString.isEmpty() && !baseAsString.isEmpty());
646 Q_ASSERT(getCoverage().contains(coverageAsString));
647 if (baseAsString ==
"///")
return true;
650 int base = baseAsString.toInt(&ok);
653 if (!ok)
return false;
656 getCoverage().
value(coverageAsString));
657 metar.addCloudLayer(cloudLayer);
658 QString cb_tcu = match.captured(
"cb_tcu");
659 if (!cb_tcu.isEmpty()) {}
663 bool isMandatory()
const override {
return false; }
666 QString getRegExpImpl()
const
669 const QString clearSky = QString(
"(?<clear_sky>") + getClearSkyTokens().join(
'|') + QString(
")");
671 const QString coverage =
672 QString(
"(?<coverage>") + QStringList(getCoverage().keys()).join(
'|') + QString(
")");
674 const QString base = QStringLiteral(
"(?<base>\\d{3}|///)");
676 const QString extra = QStringLiteral(
"(?<cb_tcu>CB|TCU|///)?");
678 const QString regexp = QString(
"^(") + clearSky +
'|' + coverage + base + extra + QString(
") ");
683 class CMetarDecoderVerticalVisibility :
public IMetarDecoderPart
686 QString getDecoderType()
const override {
return "VerticalVisibility"; }
689 const QRegularExpression &getRegExp()
const override
691 static const QRegularExpression re(getRegExpImpl());
695 bool validateAndSet(
const QRegularExpressionMatch & , CMetar & )
const override
701 bool isMandatory()
const override {
return false; }
704 QString getRegExpImpl()
const
707 const QString verticalVisibility = QStringLiteral(
"VV(?<vertical_visibility>\\d{3}|///)");
708 const QString regexp =
"^" + verticalVisibility +
" ";
713 class CMetarDecoderTemperature :
public IMetarDecoderPart
716 QString getDecoderType()
const override {
return "Temperature"; }
719 const QRegularExpression &getRegExp()
const override
721 static const QRegularExpression re(getRegExpImpl());
725 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
727 QString temperatureAsString = match.captured(
"temperature");
728 if (temperatureAsString.isEmpty())
return false;
729 QString dewPointAsString = match.captured(
"dew_point");
730 if (dewPointAsString.isEmpty())
return false;
732 if (temperatureAsString ==
"//" || dewPointAsString ==
"//")
return true;
734 bool temperatureNegative =
false;
735 if (temperatureAsString.startsWith(
'M'))
737 temperatureNegative =
true;
738 temperatureAsString.remove(
'M');
741 bool dewPointNegative =
false;
742 if (dewPointAsString.startsWith(
'M'))
744 dewPointNegative =
true;
745 dewPointAsString.remove(
'M');
748 int temperature = temperatureAsString.toInt();
749 if (temperatureNegative) { temperature *= -1; }
752 int dewPoint = dewPointAsString.toInt();
753 if (dewPointNegative) { dewPoint *= -1; }
759 bool isMandatory()
const override {
return false; }
762 QString getRegExpImpl()
const
765 const QString temperature = QStringLiteral(
"(?<temperature>M?\\d{2}|//)");
767 const QString separator =
"/";
769 const QString dewPoint = QStringLiteral(
"(?<dew_point>M?\\d{2}|//)");
771 const QString regexp =
"^" + temperature + separator + dewPoint +
" ?";
776 class CMetarDecoderPressure :
public IMetarDecoderPart
779 QString getDecoderType()
const override {
return "Pressure"; }
789 const QRegularExpression &getRegExp()
const override
791 static const QRegularExpression re(getRegExpImpl());
795 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
797 QString unitAsString = match.captured(
"unit");
798 QString pressureAsString = match.captured(
"pressure");
799 QString qfeAsString = match.captured(
"qfe");
800 if ((unitAsString.isEmpty() || pressureAsString.isEmpty()) && qfeAsString.isEmpty())
return false;
803 if (pressureAsString ==
"////")
return true;
805 if (!unitAsString.isEmpty() && !pressureAsString.isEmpty())
807 Q_ASSERT(getPressureUnits().contains(unitAsString));
809 double pressure = pressureAsString.toDouble(&ok);
810 if (!ok)
return false;
811 CPressureUnit pressureUnit = getPressureUnits().value(unitAsString);
813 metar.setAltimeter(
CPressure(pressure, pressureUnit));
817 if (!qfeAsString.isEmpty())
825 bool isMandatory()
const override {
return false; }
828 QString getRegExpImpl()
const
832 const QString unit = QStringLiteral(
"((?<unit>Q|A)");
834 const QString pressure = QStringLiteral(
"(?<pressure>\\d{4}|////) ?)");
836 const QString qfe = QStringLiteral(
"(QFE (?<qfe>\\d+).\\d");
837 const QString regexp =
"^" + unit + pressure +
"|" + qfe +
" ?)";
842 class CMetarDecoderRecentWeather :
public IMetarDecoderPart
845 QString getDecoderType()
const override {
return "RecentWeather"; }
848 const QRegularExpression &getRegExp()
const override
850 static const QRegularExpression re(getRegExpImpl());
854 bool validateAndSet(
const QRegularExpressionMatch & , CMetar & )
const override
860 bool isMandatory()
const override {
return false; }
863 QString getRegExpImpl()
const
866 const QString qualifier_intensity(
"(?<intensity>[-+]|VC)?");
868 const QString qualifier_descriptor =
"(?<descriptor>" + m_descriptor.join(
'|') +
")?";
869 const QString weather_phenomina1 =
"(?<wp1>" + m_phenomina.join(
'|') +
")?";
870 const QString weather_phenomina2 =
"(?<wp2>" + m_phenomina.join(
'|') +
")?";
871 const QString weather_phenomina3 =
"(?<wp3>" + m_phenomina.join(
'|') +
")?";
872 const QString weather_phenomina4 =
"(?<wp4>" + m_phenomina.join(
'|') +
")?";
873 const QString regexp =
"^RE" + qualifier_intensity + qualifier_descriptor + weather_phenomina1 +
874 weather_phenomina2 + weather_phenomina3 + weather_phenomina4 +
" ";
878 const QStringList m_descriptor = QStringList {
"MI",
"BC",
"PR",
"DR",
"BL",
"SH",
"TS",
"FZ" };
879 const QStringList m_phenomina =
880 QStringList {
"DZ",
"RA",
"SN",
"SG",
"IC",
"PE",
"GR",
"GS",
"BR",
"FG",
"FU",
881 "VA",
"IC",
"DU",
"SA",
"HZ",
"PY",
"PO",
"SQ",
"FC",
"SS",
"DS" };
884 class CMetarDecoderWindShear :
public IMetarDecoderPart
887 QString getDecoderType()
const override {
return "WindShear"; }
890 const QRegularExpression &getRegExp()
const override
892 static const QRegularExpression re(getRegExpImpl());
896 bool validateAndSet(
const QRegularExpressionMatch &match, CMetar &metar)
const override
898 QString runwayAsString = match.captured(
"runway");
899 if (!runwayAsString.isEmpty())
902 Q_UNUSED(runwayAsString)
909 bool isMandatory()
const override {
return false; }
912 QString getRegExpImpl()
const
915 const QString wsAllRwy = QStringLiteral(
"WS ALL RWY");
917 const QString runway = QStringLiteral(
"RW?Y?(?<runway>\\d{2}[LCR]*)");
918 const QString regexp =
"^WS (" + wsAllRwy +
"|" + runway +
") ";
923 CMetarDecoder::CMetarDecoder() { allocateDecoders(); }
925 CMetarDecoder::~CMetarDecoder() {}
927 CMetar CMetarDecoder::decode(
const QString &metarString)
const
930 QString metarStringCopy = metarString.simplified();
932 for (
const auto &decoder : m_decoders)
934 if (!decoder->parse(metarStringCopy, metar))
936 const QString type = decoder->getDecoderType();
937 CLogMessage(
this).debug() <<
"Invalid METAR:" << metarString << type;
942 metar.setMessage(metarString);
946 void CMetarDecoder::allocateDecoders()
949 m_decoders.push_back(std::make_unique<CMetarDecoderReportType>());
950 m_decoders.push_back(std::make_unique<CMetarDecoderAirport>());
951 m_decoders.push_back(std::make_unique<CMetarDecoderDayTime>());
952 m_decoders.push_back(std::make_unique<CMetarDecoderStatus>());
953 m_decoders.push_back(std::make_unique<CMetarDecoderWind>());
954 m_decoders.push_back(std::make_unique<CMetarDecoderVariationsWindDirection>());
955 m_decoders.push_back(std::make_unique<CMetarDecoderVisibility>());
956 m_decoders.push_back(std::make_unique<CMetarDecoderRunwayVisualRange>());
957 m_decoders.push_back(std::make_unique<CMetarDecoderPresentWeather>());
958 m_decoders.push_back(std::make_unique<CMetarDecoderCloud>());
959 m_decoders.push_back(std::make_unique<CMetarDecoderVerticalVisibility>());
960 m_decoders.push_back(std::make_unique<CMetarDecoderTemperature>());
961 m_decoders.push_back(std::make_unique<CMetarDecoderPressure>());
962 m_decoders.push_back(std::make_unique<CMetarDecoderRecentWeather>());
963 m_decoders.push_back(std::make_unique<CMetarDecoderWindShear>());
Value object encapsulating information of airport ICAO data.
Altitude as used in aviation, can be AGL or MSL altitude.
Physical unit angle (radians, degrees)
static CAngleUnit deg()
Degrees.
Physical unit length (length)
Specialized class for distance units (meter, foot, nautical miles).
static CLengthUnit km()
Kilometer km.
static CLengthUnit m()
Meter m.
static CLengthUnit SM()
Statute mile.
static CLengthUnit ft()
Foot ft.
double value(MU unit) const
Value in given unit.
Specialized class for pressure (psi, hPa, bar).
static CPressureUnit inHg()
Inch of mercury at 0°C.
static CPressureUnit hPa()
Hectopascal.
static CSpeedUnit km_h()
Kilometer/hour km/h.
static CSpeedUnit m_s()
Meter/second m/s.
static CSpeedUnit kts()
Knots.
Physical unit temperature.
static CTemperatureUnit C()
Centigrade C.