From 43cd78c2a45b1771d9e5b40dad81e20fe179fb7c Mon Sep 17 00:00:00 2001 From: TwinFan Date: Sun, 28 Jul 2024 22:02:02 +0200 Subject: [PATCH] Feat/RT/Weath: Rwy State Group --- Data/RealTraffic/Weather.xls | Bin 43520 -> 49664 bytes Src/LTRealTraffic.cpp | 5 -- Src/LTWeather.cpp | 87 ++++++++++++++++++++++++++++------- 3 files changed, 70 insertions(+), 22 deletions(-) diff --git a/Data/RealTraffic/Weather.xls b/Data/RealTraffic/Weather.xls index 726a35c6123c7d951fdf46809e561dcf726dc794..b2b59936dc456f37283538b2280bd064399522a4 100644 GIT binary patch delta 10852 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

(YMr?RhppRTtxQ~XR3Yzt z*2*-0_|AvJ{Ia|n*H@1E{X&nqHf!#@FwtV0d=l>Go zWWZ+PcIvty9x6p*02@$N8EByen9orpBktpB(AU|=7UHyp%|vaAowqD(W8XKroT7{47(^+S@v^7U@ zP&KwvmS7LA86%{vN$IwbL+_Ac2$$i=i;?n4-&heR12)qh&3z@rW7;#DR`f5$QQpX# z<`~-9v{Rza9RmIB;Ey&N%rpm+!=NAL48Y;{#Pn^(FQ4ot{z1{kWn*Pg#4rSC{D zrep($1A$IhKqieGq? z&+!HO>&qR~hIGf@!-ZEBlpF56U^H)a_9-*h#@Km!Y?IFx7>`3{zVkmo@SQ(=k6U96 zo=02zMv5qN6u-$NRK1vQul@I#l%FJG{^$R(V}u;0UpL}pz`&p8SE%cVIPvGTK^JeN z>KM?dekv#9((_qL);eGpxo2i<;$Ic&aW&xDgzMi|uU_4ZJD&qK0M{0onK)7gRh-MDs&nlleQdiK+dmeb<1v-^}r5k6-YwxT8e51Ib~6>u;@ delta 2742 zcmb7Gdu&rx82{aF>q^%#IyT0}fHMsb2N?q;5kYZdxQxkQbTE*>=(bx|>DsQ3J=j>w z7&2hW<3mM6K>jdF6p;Ie5JYA)Xw(p6RMZF|J`yy>M1l!1=={#z<-#8S*ps&B_xrx{ zz0Ud0z47jZ_*a%ag^FwM>r6wsdqiozA-&+tQpV9`=VfI)olMW7^-e2& zp1xr!8|{x2lc~TQYMM1kJC~o8Zt*wLR}0pRN>zZvZ1w@FDaq8zA6?~qcrH*J zjR?0d91vku6-yUa6|I;lS{l4{4Whvv7PV?5qK1So(89lUzCg5Ic>SJ$_FBc+gjhLH z-QbR}Vlb%s!@}zkEvk4X8ji5Bh){iMvxo#lp>X@_MU%gwCdd|<2&--uslJ+ENDYUh zAyo<%uKT~S%rb(pmw21K^{S|C6SE5orGvVFzgZ1MgeMee6jSsV3JPXxPpzm^#;gSD z+3zBWM>YB}|}?u$JKxGI)aR~0Q?D9VJ#%Q4GTMZhm24XW@2LXGaoOi|1A z@&_V1SMQFvg*OsbeVzjQGS&*Uc*ClwSL>s7hW8fLEqT2J^)l%|Py{$Q5%D&vA`lJn z;$}5mP>@p~t+J$zV^izc&Kf}94?+EFUD{(+MafAEP-cY|TYE{NBkPj2HS5BOv3?xm z!&*HLaPa1qJ_6k4e|0{PV*&1e6!kTAwCulDGFds5;2V#R2xv_^W=3N@b@s17SWDV^@s>M6W z+~8{Qt}1dlv*+Dnkra7zdf+0)b(_W4Snz_lP?4ywYUSbfzO-sqCgp@vJ0i z(@(#}UGw%BaXDFIDFP;v`3FrT^MBby1NnDv`A#XN^<8xnn+!q%v${)M>FJM~_zd`WxL!j&5bM_Q$phin5PRY|o16MPlX4 zD8WI#0eqHTbn@smN03X@%PxDd2kUuAwBFDekV&SBM=cL|Jqxxs^diaAxw~y8x1IH< zq^E2Kv1|Zaxg4D)l8MAjWMlLSUF>m{?l-tRE_6BKCQ8O~Y%!6I(IFGbJi8cO=7P*o zz;AJ#do!?`gcgu2iEB)QKNhzl22QddfTg?~3{{Z2iA$ { positionTy posField; ///< position/altitude of the METAR's field, bool bCloseToGnd = false; ///< is position close enough to ground to weigh METAR higher than weather data? size_t iCloud = 0; ///< which cloud layer is to be filled next? - int bThunderstorms = 0; ///< thunderstorms anywhere? (0-None, 1-Light, 2-Normal, 3-Heavy) + int bThunderstorms = 0; ///< thunderstorms anywhere? (0-None, 1-Light, 2-Normal, 3-Heavy) -> PostProcessing() makes sure CB exists and adds turbulence + bool bRwyFrictionDefined = false; ///< Rwy friction defined based on Rwy State Group in METAR, don't touch again in PostProcessing() public: /// Constructor sets the reference to the weather that we are to modify @@ -941,27 +942,79 @@ class LTWeatherVisitor : public Visitor { return false; } - // TODO: Rwy State Group + /// Rwy State Group + bool visitRunwayStateGroup(const RunwayStateGroup& rsg, + ReportPart, const std::string&) override + { + // XP provides just one `runway_friction` dataRef, not one per runway, + // so we just process the first runway state group, assuming that + // the states of different runways on the same airport won't be much different anyway. + if (!bRwyFrictionDefined && rsg.deposits() != RunwayStateGroup::Deposits::NOT_REPORTED) { + bRwyFrictionDefined = true; + const float depth = rsg.depositDepth().toUnit(Precipitation::Unit::MM).value_or(0.0f); + int extend = 0; + switch (rsg.contaminationExtent()) { + case RunwayStateGroup::Extent::FROM_26_TO_50_PERCENT: + extend = 1; + break; + case RunwayStateGroup::Extent::MORE_THAN_51_PERCENT: + extend = 2; + break; + default: + extend = 0; + } + switch (rsg.deposits()) { + case RunwayStateGroup::Deposits::NOT_REPORTED: + case RunwayStateGroup::Deposits::CLEAR_AND_DRY: + w.runway_friction = 0; + break; + case RunwayStateGroup::Deposits::DAMP: + w.runway_friction = 1; + break; + case RunwayStateGroup::Deposits::WET_AND_WATER_PATCHES: + // depth decides between wet (<2mm) and puddly (>=2mm) + w.runway_friction = (depth < 2 ? 1 : 4) + extend; + break; + case RunwayStateGroup::Deposits::ICE: + case RunwayStateGroup::Deposits::RIME_AND_FROST_COVERED: + case RunwayStateGroup::Deposits::FROZEN_RUTS_OR_RIDGES: + w.runway_friction = 10 + extend; // icy + break; + case RunwayStateGroup::Deposits::DRY_SNOW: + case RunwayStateGroup::Deposits::WET_SNOW: + case RunwayStateGroup::Deposits::SLUSH: + w.runway_friction = 7 + extend; // snowy + break; + case RunwayStateGroup::Deposits::COMPACTED_OR_ROLLED_SNOW: + w.runway_friction = 13 + extend; // snowy/icy + break; + } + } + return false; + } + /// After finishing the processing, perform some cleanup void PostProcessing () { // Runway friction - if (w.runway_friction < 0) // don't yet have a runway friction? - // Rain causes wet status [0..7] - w.runway_friction = (int)std::lround(w.rain_percent * 6.f); - // Rwy Friction: Consider freezing if there is something's on the rwy that is not yet ice - const float t = w.GetInterpolated(w.temperature_altitude_msl_m, - w.temperatures_aloft_deg_c, - float(posField.alt_m())); - if (t <= TEMP_RWY_ICED && - 0 < w.runway_friction && w.runway_friction < 10) - { - // From water - if (1 <= w.runway_friction && w.runway_friction <= 6) - w.runway_friction = (w.runway_friction-1)/2 + 10; // convert from wet/puddly [1..6] to icy [10..12] - else - w.runway_friction += 3; // convert from snowy [7..9] to icy [10..12] + if (!bRwyFrictionDefined) { + if (w.runway_friction < 0) // don't yet have a runway friction? + // Rain causes wet status [0..7] + w.runway_friction = (int)std::lround(w.rain_percent * 6.f); + // Rwy Friction: Consider freezing if there is something's on the rwy that is not yet ice + const float t = w.GetInterpolated(w.temperature_altitude_msl_m, + w.temperatures_aloft_deg_c, + float(posField.alt_m())); + if (t <= TEMP_RWY_ICED && + 0 < w.runway_friction && w.runway_friction < 10) + { + // From water + if (1 <= w.runway_friction && w.runway_friction <= 6) + w.runway_friction = (w.runway_friction-1)/2 + 10; // convert from wet/puddly [1..6] to icy [10..12] + else + w.runway_friction += 3; // convert from snowy [7..9] to icy [10..12] + } } // Cleanup up cloud layers: Anything beyond iCloud, that is lower than the last METAR layer, is to be removed