mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 16:40:04 +00:00 
			
		
		
		
	Adding robust date calculation to WMO header
This commit is contained in:
		
							parent
							
								
									c00016cb69
								
							
						
					
					
						commit
						7e9895e002
					
				
					 3 changed files with 178 additions and 72 deletions
				
			
		|  | @ -1,5 +1,6 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <chrono> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| 
 | 
 | ||||||
|  | @ -45,8 +46,41 @@ public: | ||||||
|    std::string product_category() const; |    std::string product_category() const; | ||||||
|    std::string product_designator() const; |    std::string product_designator() const; | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * @brief Get the WMO date/time | ||||||
|  |     * | ||||||
|  |     * Gets the WMO date/time. Uses the optional date hint provided via | ||||||
|  |     * SetDateHint(std::chrono::year_month). If the date hint has not been | ||||||
|  |     * provided, the endTimeHint parameter is required. | ||||||
|  |     * | ||||||
|  |     * @param [in] endTimeHint The optional end time bounds to provide. This is | ||||||
|  |     * ignored if a date hint has been provided to determine an absolute date. | ||||||
|  |     */ | ||||||
|  |    std::chrono::sys_time<std::chrono::minutes> GetDateTime( | ||||||
|  |       std::optional<std::chrono::system_clock::time_point> endTimeHint = | ||||||
|  |          std::nullopt); | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * @brief Parse a WMO header | ||||||
|  |     * | ||||||
|  |     * @param [in] is The input stream to parse | ||||||
|  |     */ | ||||||
|    bool Parse(std::istream& is); |    bool Parse(std::istream& is); | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * @brief Provide a date hint for the WMO parser | ||||||
|  |     * | ||||||
|  |     * The WMO header contains a date/time in the format DDMMSS. The year and | ||||||
|  |     * month must be derived using another source. The date hint provides the | ||||||
|  |     * additional context required to determine the absolute product time. | ||||||
|  |     * | ||||||
|  |     * This function will update any absolute date/time already calculated, or | ||||||
|  |     * affect the calculation of a subsequent absolute date/time. | ||||||
|  |     * | ||||||
|  |     * @param [in] dateHint The date hint to provide the WMO header parser | ||||||
|  |     */ | ||||||
|  |    void SetDateHint(std::chrono::year_month dateHint); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|    std::unique_ptr<WmoHeaderImpl> p; |    std::unique_ptr<WmoHeaderImpl> p; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -119,71 +119,11 @@ std::chrono::system_clock::time_point Segment::event_begin() const | ||||||
|       // If event begin is 000000T0000Z
 |       // If event begin is 000000T0000Z
 | ||||||
|       if (eventBegin == std::chrono::system_clock::time_point {}) |       if (eventBegin == std::chrono::system_clock::time_point {}) | ||||||
|       { |       { | ||||||
|          using namespace std::chrono; |  | ||||||
| 
 |  | ||||||
|          // Determine event end from P-VTEC string
 |          // Determine event end from P-VTEC string
 | ||||||
|          system_clock::time_point eventEnd = |          std::chrono::system_clock::time_point eventEnd = | ||||||
|             header_->vtecString_[0].pVtec_.event_end(); |             header_->vtecString_[0].pVtec_.event_end(); | ||||||
| 
 | 
 | ||||||
|          auto           endDays = floor<days>(eventEnd); |          eventBegin = wmoHeader_->GetDateTime(eventEnd); | ||||||
|          year_month_day endDate {endDays}; |  | ||||||
| 
 |  | ||||||
|          // Determine WMO date/time
 |  | ||||||
|          std::string wmoDateTime = wmoHeader_->date_time(); |  | ||||||
| 
 |  | ||||||
|          bool          wmoDateTimeValid = false; |  | ||||||
|          unsigned int  dayOfMonth       = 0; |  | ||||||
|          unsigned long beginHour        = 0; |  | ||||||
|          unsigned long beginMinute      = 0; |  | ||||||
| 
 |  | ||||||
|          try |  | ||||||
|          { |  | ||||||
|             // WMO date time is in the format DDHHMM
 |  | ||||||
|             dayOfMonth = |  | ||||||
|                static_cast<unsigned int>(std::stoul(wmoDateTime.substr(0, 2))); |  | ||||||
|             beginHour        = std::stoul(wmoDateTime.substr(2, 2)); |  | ||||||
|             beginMinute      = std::stoul(wmoDateTime.substr(4, 2)); |  | ||||||
|             wmoDateTimeValid = true; |  | ||||||
|          } |  | ||||||
|          catch (const std::exception&) |  | ||||||
|          { |  | ||||||
|             logger_->warn("Malformed WMO date/time: {}", wmoDateTime); |  | ||||||
|          } |  | ||||||
| 
 |  | ||||||
|          if (wmoDateTimeValid) |  | ||||||
|          { |  | ||||||
|             // Combine end date year and month with WMO date time
 |  | ||||||
|             eventBegin = |  | ||||||
|                sys_days {endDate.year() / endDate.month() / day {dayOfMonth}} + |  | ||||||
|                hours {beginHour} + minutes {beginMinute}; |  | ||||||
| 
 |  | ||||||
|             // If the begin date is after the end date, assume the start time
 |  | ||||||
|             // was the previous month (give a 1 day grace period for expiring
 |  | ||||||
|             // events in the past)
 |  | ||||||
|             if (eventBegin > eventEnd + 24h) |  | ||||||
|             { |  | ||||||
|                // If the current end month is January
 |  | ||||||
|                if (endDate.month() == January) |  | ||||||
|                { |  | ||||||
|                   // The begin month must be December of last year
 |  | ||||||
|                   eventBegin = |  | ||||||
|                      sys_days { |  | ||||||
|                         year {static_cast<int>((endDate.year() - 1y).count())} / |  | ||||||
|                         December / day {dayOfMonth}} + |  | ||||||
|                      hours {beginHour} + minutes {beginMinute}; |  | ||||||
|                } |  | ||||||
|                else |  | ||||||
|                { |  | ||||||
|                   // Back up one month
 |  | ||||||
|                   eventBegin = |  | ||||||
|                      sys_days {endDate.year() / |  | ||||||
|                                month {static_cast<unsigned int>( |  | ||||||
|                                   (endDate.month() - month {1}).count())} / |  | ||||||
|                                day {dayOfMonth}} + |  | ||||||
|                      hours {beginHour} + minutes {beginMinute}; |  | ||||||
|                } |  | ||||||
|             } |  | ||||||
|          } |  | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -42,17 +42,26 @@ public: | ||||||
|    WmoHeaderImpl(const WmoHeaderImpl&&)            = delete; |    WmoHeaderImpl(const WmoHeaderImpl&&)            = delete; | ||||||
|    WmoHeaderImpl& operator=(const WmoHeaderImpl&&) = delete; |    WmoHeaderImpl& operator=(const WmoHeaderImpl&&) = delete; | ||||||
| 
 | 
 | ||||||
|  |    void CalculateAbsoluteDateTime(); | ||||||
|  |    bool ParseDateTime(unsigned int&  dayOfMonth, | ||||||
|  |                       unsigned long& hour, | ||||||
|  |                       unsigned long& minute); | ||||||
|  | 
 | ||||||
|    bool operator==(const WmoHeaderImpl& o) const; |    bool operator==(const WmoHeaderImpl& o) const; | ||||||
| 
 | 
 | ||||||
|    std::string sequenceNumber_; |    std::string sequenceNumber_ {}; | ||||||
|    std::string dataType_; |    std::string dataType_ {}; | ||||||
|    std::string geographicDesignator_; |    std::string geographicDesignator_ {}; | ||||||
|    std::string bulletinId_; |    std::string bulletinId_ {}; | ||||||
|    std::string icao_; |    std::string icao_ {}; | ||||||
|    std::string dateTime_; |    std::string dateTime_ {}; | ||||||
|    std::string bbbIndicator_; |    std::string bbbIndicator_ {}; | ||||||
|    std::string productCategory_; |    std::string productCategory_ {}; | ||||||
|    std::string productDesignator_; |    std::string productDesignator_ {}; | ||||||
|  | 
 | ||||||
|  |    std::optional<std::chrono::year_month> dateHint_ {}; | ||||||
|  |    std::optional<std::chrono::sys_time<std::chrono::minutes>> | ||||||
|  |       absoluteDateTime_ {}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| WmoHeader::WmoHeader() : p(std::make_unique<WmoHeaderImpl>()) {} | WmoHeader::WmoHeader() : p(std::make_unique<WmoHeaderImpl>()) {} | ||||||
|  | @ -124,6 +133,71 @@ std::string WmoHeader::product_designator() const | ||||||
|    return p->productDesignator_; |    return p->productDesignator_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::chrono::sys_time<std::chrono::minutes> WmoHeader::GetDateTime( | ||||||
|  |    std::optional<std::chrono::system_clock::time_point> endTimeHint) | ||||||
|  | { | ||||||
|  |    std::chrono::sys_time<std::chrono::minutes> wmoDateTime {}; | ||||||
|  | 
 | ||||||
|  |    if (p->absoluteDateTime_.has_value()) | ||||||
|  |    { | ||||||
|  |       wmoDateTime = p->absoluteDateTime_.value(); | ||||||
|  |    } | ||||||
|  |    else if (endTimeHint.has_value()) | ||||||
|  |    { | ||||||
|  |       bool          dateTimeValid = false; | ||||||
|  |       unsigned int  dayOfMonth    = 0; | ||||||
|  |       unsigned long hour          = 0; | ||||||
|  |       unsigned long minute        = 0; | ||||||
|  | 
 | ||||||
|  |       dateTimeValid = p->ParseDateTime(dayOfMonth, hour, minute); | ||||||
|  | 
 | ||||||
|  |       if (dateTimeValid) | ||||||
|  |       { | ||||||
|  |          using namespace std::chrono; | ||||||
|  | 
 | ||||||
|  |          auto           endDays = floor<days>(endTimeHint.value()); | ||||||
|  |          year_month_day endDate {endDays}; | ||||||
|  | 
 | ||||||
|  |          // Combine end date year and month with WMO date time
 | ||||||
|  |          wmoDateTime = | ||||||
|  |             sys_days {endDate.year() / endDate.month() / day {dayOfMonth}} + | ||||||
|  |             hours {hour} + minutes {minute}; | ||||||
|  | 
 | ||||||
|  |          // If the begin date is after the end date, assume the start time
 | ||||||
|  |          // was the previous month (give a 1 day grace period for expiring
 | ||||||
|  |          // events in the past)
 | ||||||
|  |          if (wmoDateTime > endTimeHint.value() + 24h) | ||||||
|  |          { | ||||||
|  |             // If the current end month is January
 | ||||||
|  |             if (endDate.month() == January) | ||||||
|  |             { | ||||||
|  |                year_month x = year {2024} / December; | ||||||
|  |                sys_days   y; | ||||||
|  | 
 | ||||||
|  |                // The begin month must be December of last year
 | ||||||
|  |                wmoDateTime = | ||||||
|  |                   sys_days { | ||||||
|  |                      year {static_cast<int>((endDate.year() - 1y).count())} / | ||||||
|  |                      December / day {dayOfMonth}} + | ||||||
|  |                   hours {hour} + minutes {minute}; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                // Back up one month
 | ||||||
|  |                wmoDateTime = | ||||||
|  |                   sys_days {endDate.year() / | ||||||
|  |                             month {static_cast<unsigned int>( | ||||||
|  |                                (endDate.month() - month {1}).count())} / | ||||||
|  |                             day {dayOfMonth}} + | ||||||
|  |                   hours {hour} + minutes {minute}; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return wmoDateTime; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool WmoHeader::Parse(std::istream& is) | bool WmoHeader::Parse(std::istream& is) | ||||||
| { | { | ||||||
|    bool headerValid = true; |    bool headerValid = true; | ||||||
|  | @ -224,6 +298,8 @@ bool WmoHeader::Parse(std::istream& is) | ||||||
|          p->icao_                 = wmoTokenList[1]; |          p->icao_                 = wmoTokenList[1]; | ||||||
|          p->dateTime_             = wmoTokenList[2]; |          p->dateTime_             = wmoTokenList[2]; | ||||||
| 
 | 
 | ||||||
|  |          p->CalculateAbsoluteDateTime(); | ||||||
|  | 
 | ||||||
|          if (wmoTokenList.size() == 4) |          if (wmoTokenList.size() == 4) | ||||||
|          { |          { | ||||||
|             p->bbbIndicator_ = wmoTokenList[3]; |             p->bbbIndicator_ = wmoTokenList[3]; | ||||||
|  | @ -255,4 +331,60 @@ bool WmoHeader::Parse(std::istream& is) | ||||||
|    return headerValid; |    return headerValid; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void WmoHeader::SetDateHint(std::chrono::year_month dateHint) | ||||||
|  | { | ||||||
|  |    p->dateHint_ = dateHint; | ||||||
|  |    p->CalculateAbsoluteDateTime(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool WmoHeaderImpl::ParseDateTime(unsigned int&  dayOfMonth, | ||||||
|  |                                   unsigned long& hour, | ||||||
|  |                                   unsigned long& minute) | ||||||
|  | { | ||||||
|  |    bool dateTimeValid = false; | ||||||
|  | 
 | ||||||
|  |    try | ||||||
|  |    { | ||||||
|  |       // WMO date time is in the format DDHHMM
 | ||||||
|  |       dayOfMonth = | ||||||
|  |          static_cast<unsigned int>(std::stoul(dateTime_.substr(0, 2))); | ||||||
|  |       hour          = std::stoul(dateTime_.substr(2, 2)); | ||||||
|  |       minute        = std::stoul(dateTime_.substr(4, 2)); | ||||||
|  |       dateTimeValid = true; | ||||||
|  |    } | ||||||
|  |    catch (const std::exception&) | ||||||
|  |    { | ||||||
|  |       logger_->warn("Malformed WMO date/time: {}", dateTime_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return dateTimeValid; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WmoHeaderImpl::CalculateAbsoluteDateTime() | ||||||
|  | { | ||||||
|  |    bool dateTimeValid = false; | ||||||
|  | 
 | ||||||
|  |    if (dateHint_.has_value() && !dateTime_.empty()) | ||||||
|  |    { | ||||||
|  |       unsigned int  dayOfMonth = 0; | ||||||
|  |       unsigned long hour       = 0; | ||||||
|  |       unsigned long minute     = 0; | ||||||
|  | 
 | ||||||
|  |       dateTimeValid = ParseDateTime(dayOfMonth, hour, minute); | ||||||
|  | 
 | ||||||
|  |       if (dateTimeValid) | ||||||
|  |       { | ||||||
|  |          using namespace std::chrono; | ||||||
|  |          absoluteDateTime_ = sys_days {dateHint_->year() / dateHint_->month() / | ||||||
|  |                                        day {dayOfMonth}} + | ||||||
|  |                              hours {hour} + minutes {minute}; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (!dateTimeValid) | ||||||
|  |    { | ||||||
|  |       absoluteDateTime_.reset(); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace scwx::awips
 | } // namespace scwx::awips
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat