mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 01:50:06 +00:00 
			
		
		
		
	Initial Level 3 header and description information
This commit is contained in:
		
							parent
							
								
									a280f37289
								
							
						
					
					
						commit
						0303412519
					
				
					 6 changed files with 855 additions and 0 deletions
				
			
		
							
								
								
									
										45
									
								
								wxdata/include/scwx/wsr88d/rpg/level3_message_header.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								wxdata/include/scwx/wsr88d/rpg/level3_message_header.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <cstdint> | ||||||
|  | #include <memory> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace wsr88d | ||||||
|  | { | ||||||
|  | namespace rpg | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class Level3MessageHeaderImpl; | ||||||
|  | 
 | ||||||
|  | class Level3MessageHeader | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit Level3MessageHeader(); | ||||||
|  |    ~Level3MessageHeader(); | ||||||
|  | 
 | ||||||
|  |    Level3MessageHeader(const Level3MessageHeader&) = delete; | ||||||
|  |    Level3MessageHeader& operator=(const Level3MessageHeader&) = delete; | ||||||
|  | 
 | ||||||
|  |    Level3MessageHeader(Level3MessageHeader&&) noexcept; | ||||||
|  |    Level3MessageHeader& operator=(Level3MessageHeader&&) noexcept; | ||||||
|  | 
 | ||||||
|  |    int16_t  message_code() const; | ||||||
|  |    uint16_t date_of_message() const; | ||||||
|  |    uint32_t time_of_message() const; | ||||||
|  |    uint32_t length_of_message() const; | ||||||
|  |    uint16_t source_id() const; | ||||||
|  |    uint16_t destination_id() const; | ||||||
|  |    uint16_t number_blocks() const; | ||||||
|  | 
 | ||||||
|  |    bool Parse(std::istream& is); | ||||||
|  | 
 | ||||||
|  |    static const size_t SIZE = 18u; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    std::unique_ptr<Level3MessageHeaderImpl> p; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace rpg
 | ||||||
|  | } // namespace wsr88d
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										61
									
								
								wxdata/include/scwx/wsr88d/rpg/product_description_block.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								wxdata/include/scwx/wsr88d/rpg/product_description_block.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <scwx/wsr88d/message.hpp> | ||||||
|  | 
 | ||||||
|  | #include <cstdint> | ||||||
|  | #include <memory> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace wsr88d | ||||||
|  | { | ||||||
|  | namespace rpg | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class ProductDescriptionBlockImpl; | ||||||
|  | 
 | ||||||
|  | class ProductDescriptionBlock : public Message | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit ProductDescriptionBlock(); | ||||||
|  |    ~ProductDescriptionBlock(); | ||||||
|  | 
 | ||||||
|  |    ProductDescriptionBlock(const ProductDescriptionBlock&) = delete; | ||||||
|  |    ProductDescriptionBlock& operator=(const ProductDescriptionBlock&) = delete; | ||||||
|  | 
 | ||||||
|  |    ProductDescriptionBlock(ProductDescriptionBlock&&) noexcept; | ||||||
|  |    ProductDescriptionBlock& operator=(ProductDescriptionBlock&&) noexcept; | ||||||
|  | 
 | ||||||
|  |    int16_t  block_divider() const; | ||||||
|  |    int32_t  latitude_of_radar() const; | ||||||
|  |    int32_t  longitude_of_radar() const; | ||||||
|  |    int16_t  height_of_radar() const; | ||||||
|  |    int16_t  product_code() const; | ||||||
|  |    uint16_t operational_mode() const; | ||||||
|  |    uint16_t volume_coverage_pattern() const; | ||||||
|  |    int16_t  sequence_number() const; | ||||||
|  |    uint16_t volume_scan_number() const; | ||||||
|  |    uint16_t volume_scan_date() const; | ||||||
|  |    uint32_t volume_scan_start_time() const; | ||||||
|  |    uint16_t generation_date_of_product() const; | ||||||
|  |    uint32_t generation_time_of_product() const; | ||||||
|  |    uint16_t elevation_number() const; | ||||||
|  |    uint8_t  version() const; | ||||||
|  |    uint8_t  spot_blank() const; | ||||||
|  |    uint32_t offset_to_symbology() const; | ||||||
|  |    uint32_t offset_to_graphic() const; | ||||||
|  |    uint32_t offset_to_tabular() const; | ||||||
|  | 
 | ||||||
|  |    bool IsCompressionEnabled() const; | ||||||
|  | 
 | ||||||
|  |    bool Parse(std::istream& is); | ||||||
|  | 
 | ||||||
|  |    static const size_t SIZE = 102u; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    std::unique_ptr<ProductDescriptionBlockImpl> p; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace rpg
 | ||||||
|  | } // namespace wsr88d
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										55
									
								
								wxdata/include/scwx/wsr88d/rpg/wmo_header.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								wxdata/include/scwx/wsr88d/rpg/wmo_header.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace wsr88d | ||||||
|  | { | ||||||
|  | namespace rpg | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class WmoHeaderImpl; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief The WMO Header is defined in WMO Manual No. 386, with additional codes | ||||||
|  |  * defined in WMO Codes Manual 306.  The NWS summarizes the relevant | ||||||
|  |  * information. | ||||||
|  |  * | ||||||
|  |  * <https://www.roc.noaa.gov/WSR88D/Level_III/Level3Info.aspx>
 | ||||||
|  |  * <https://www.weather.gov/tg/head>
 | ||||||
|  |  * <https://www.weather.gov/tg/headef>
 | ||||||
|  |  * <https://www.weather.gov/tg/bbb>
 | ||||||
|  |  * <https://www.weather.gov/tg/awips>
 | ||||||
|  |  */ | ||||||
|  | class WmoHeader | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit WmoHeader(); | ||||||
|  |    ~WmoHeader(); | ||||||
|  | 
 | ||||||
|  |    WmoHeader(const WmoHeader&) = delete; | ||||||
|  |    WmoHeader& operator=(const WmoHeader&) = delete; | ||||||
|  | 
 | ||||||
|  |    WmoHeader(WmoHeader&&) noexcept; | ||||||
|  |    WmoHeader& operator=(WmoHeader&&) noexcept; | ||||||
|  | 
 | ||||||
|  |    const std::string& data_type() const; | ||||||
|  |    const std::string& geographic_designator() const; | ||||||
|  |    const std::string& bulletin_id() const; | ||||||
|  |    const std::string& icao() const; | ||||||
|  |    const std::string& date_time() const; | ||||||
|  |    const std::string& bbb_indicator() const; | ||||||
|  |    const std::string& product_category() const; | ||||||
|  |    const std::string& product_designator() const; | ||||||
|  | 
 | ||||||
|  |    bool Parse(std::istream& is); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    std::unique_ptr<WmoHeaderImpl> p; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace rpg
 | ||||||
|  | } // namespace wsr88d
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										176
									
								
								wxdata/source/scwx/wsr88d/rpg/level3_message_header.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								wxdata/source/scwx/wsr88d/rpg/level3_message_header.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,176 @@ | ||||||
|  | #include <scwx/wsr88d/rpg/level3_message_header.hpp> | ||||||
|  | 
 | ||||||
|  | #include <istream> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include <boost/log/trivial.hpp> | ||||||
|  | 
 | ||||||
|  | #ifdef WIN32 | ||||||
|  | #   include <WinSock2.h> | ||||||
|  | #else | ||||||
|  | #   include <arpa/inet.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace wsr88d | ||||||
|  | { | ||||||
|  | namespace rpg | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const std::string logPrefix_ = | ||||||
|  |    "[scwx::wsr88d::rpg::level3_message_header] "; | ||||||
|  | 
 | ||||||
|  | class Level3MessageHeaderImpl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit Level3MessageHeaderImpl() : | ||||||
|  |        messageCode_ {}, | ||||||
|  |        dateOfMessage_ {}, | ||||||
|  |        timeOfMessage_ {}, | ||||||
|  |        lengthOfMessage_ {}, | ||||||
|  |        sourceId_ {}, | ||||||
|  |        destinationId_ {}, | ||||||
|  |        numberBlocks_ {} {}; | ||||||
|  |    ~Level3MessageHeaderImpl() = default; | ||||||
|  | 
 | ||||||
|  |    int16_t  messageCode_; | ||||||
|  |    uint16_t dateOfMessage_; | ||||||
|  |    uint32_t timeOfMessage_; | ||||||
|  |    uint32_t lengthOfMessage_; | ||||||
|  |    uint16_t sourceId_; | ||||||
|  |    uint16_t destinationId_; | ||||||
|  |    uint16_t numberBlocks_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Level3MessageHeader::Level3MessageHeader() : | ||||||
|  |     p(std::make_unique<Level3MessageHeaderImpl>()) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | Level3MessageHeader::~Level3MessageHeader() = default; | ||||||
|  | 
 | ||||||
|  | Level3MessageHeader::Level3MessageHeader(Level3MessageHeader&&) noexcept = | ||||||
|  |    default; | ||||||
|  | Level3MessageHeader& | ||||||
|  | Level3MessageHeader::operator=(Level3MessageHeader&&) noexcept = default; | ||||||
|  | 
 | ||||||
|  | int16_t Level3MessageHeader::message_code() const | ||||||
|  | { | ||||||
|  |    return p->messageCode_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t Level3MessageHeader::date_of_message() const | ||||||
|  | { | ||||||
|  |    return p->dateOfMessage_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t Level3MessageHeader::time_of_message() const | ||||||
|  | { | ||||||
|  |    return p->timeOfMessage_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t Level3MessageHeader::length_of_message() const | ||||||
|  | { | ||||||
|  |    return p->lengthOfMessage_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t Level3MessageHeader::source_id() const | ||||||
|  | { | ||||||
|  |    return p->sourceId_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t Level3MessageHeader::destination_id() const | ||||||
|  | { | ||||||
|  |    return p->destinationId_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t Level3MessageHeader::number_blocks() const | ||||||
|  | { | ||||||
|  |    return p->numberBlocks_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Level3MessageHeader::Parse(std::istream& is) | ||||||
|  | { | ||||||
|  |    bool headerValid = true; | ||||||
|  | 
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->messageCode_), 2); | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->dateOfMessage_), 2); | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->timeOfMessage_), 4); | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->lengthOfMessage_), 4); | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->sourceId_), 2); | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->destinationId_), 2); | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->numberBlocks_), 2); | ||||||
|  | 
 | ||||||
|  |    p->messageCode_     = ntohs(p->messageCode_); | ||||||
|  |    p->dateOfMessage_   = ntohs(p->dateOfMessage_); | ||||||
|  |    p->timeOfMessage_   = ntohl(p->timeOfMessage_); | ||||||
|  |    p->lengthOfMessage_ = ntohl(p->lengthOfMessage_); | ||||||
|  |    p->sourceId_        = ntohs(p->sourceId_); | ||||||
|  |    p->destinationId_   = ntohs(p->destinationId_); | ||||||
|  |    p->numberBlocks_    = ntohs(p->numberBlocks_); | ||||||
|  | 
 | ||||||
|  |    if (is.eof()) | ||||||
|  |    { | ||||||
|  |       BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Reached end of file"; | ||||||
|  |       headerValid = false; | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       if (p->messageCode_ < -131 || | ||||||
|  |           (p->messageCode_ > -16 && p->messageCode_ < 0) || | ||||||
|  |           p->messageCode_ > 211) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(warning) | ||||||
|  |             << logPrefix_ << "Invalid message code: " << p->messageCode_; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       if (p->dateOfMessage_ < 1u || p->dateOfMessage_ > 32'767u) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(warning) | ||||||
|  |             << logPrefix_ << "Invalid date: " << p->dateOfMessage_; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       if (p->timeOfMessage_ > 86'399u) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(warning) | ||||||
|  |             << logPrefix_ << "Invalid time: " << p->timeOfMessage_; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       if (p->lengthOfMessage_ < 18 || p->lengthOfMessage_ > 1'329'270u) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(warning) | ||||||
|  |             << logPrefix_ << "Invalid length: " << p->lengthOfMessage_; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       if (p->sourceId_ > 999u) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(warning) | ||||||
|  |             << logPrefix_ << "Invalid source ID: " << p->sourceId_; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       if (p->destinationId_ > 999u) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(warning) | ||||||
|  |             << logPrefix_ << "Invalid destination ID: " << p->destinationId_; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       if (p->numberBlocks_ < 1u || p->numberBlocks_ > 51u) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(warning) | ||||||
|  |             << logPrefix_ << "Invalid block count: " << p->numberBlocks_; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (headerValid) | ||||||
|  |    { | ||||||
|  |       BOOST_LOG_TRIVIAL(trace) | ||||||
|  |          << logPrefix_ << "Message code: " << p->messageCode_; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return headerValid; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace rpg
 | ||||||
|  | } // namespace wsr88d
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										306
									
								
								wxdata/source/scwx/wsr88d/rpg/product_description_block.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								wxdata/source/scwx/wsr88d/rpg/product_description_block.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,306 @@ | ||||||
|  | #include <scwx/wsr88d/rpg/product_description_block.hpp> | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <istream> | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include <boost/log/trivial.hpp> | ||||||
|  | 
 | ||||||
|  | #ifdef WIN32 | ||||||
|  | #   include <WinSock2.h> | ||||||
|  | #else | ||||||
|  | #   include <arpa/inet.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace wsr88d | ||||||
|  | { | ||||||
|  | namespace rpg | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const std::string logPrefix_ = | ||||||
|  |    "[scwx::wsr88d::rpg::product_description_block] "; | ||||||
|  | 
 | ||||||
|  | static const std::set<int16_t> compressedProducts_ = { | ||||||
|  |    32,  94,  99,  134, 135, 138, 149, 152, 153, 154, 155, 159, 161, 163, 165, | ||||||
|  |    167, 168, 170, 172, 173, 174, 175, 176, 177, 178, 179, 193, 195, 202}; | ||||||
|  | 
 | ||||||
|  | class ProductDescriptionBlockImpl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit ProductDescriptionBlockImpl() : | ||||||
|  |        blockDivider_ {}, | ||||||
|  |        latitudeOfRadar_ {}, | ||||||
|  |        longitudeOfRadar_ {}, | ||||||
|  |        heightOfRadar_ {}, | ||||||
|  |        productCode_ {}, | ||||||
|  |        operationalMode_ {}, | ||||||
|  |        volumeCoveragePattern_ {}, | ||||||
|  |        sequenceNumber_ {}, | ||||||
|  |        volumeScanNumber_ {}, | ||||||
|  |        volumeScanDate_ {}, | ||||||
|  |        volumeScanStartTime_ {}, | ||||||
|  |        generationDateOfProduct_ {}, | ||||||
|  |        generationTimeOfProduct_ {}, | ||||||
|  |        elevationNumber_ {}, | ||||||
|  |        halfword31_ {}, | ||||||
|  |        halfword32_ {}, | ||||||
|  |        halfword33_ {}, | ||||||
|  |        halfword34_ {}, | ||||||
|  |        halfword35_ {}, | ||||||
|  |        version_ {}, | ||||||
|  |        spotBlank_ {}, | ||||||
|  |        offsetToSymbology_ {}, | ||||||
|  |        offsetToGraphic_ {}, | ||||||
|  |        offsetToTabular_ {}, | ||||||
|  |        parameters_ {} {}; | ||||||
|  |    ~ProductDescriptionBlockImpl() = default; | ||||||
|  | 
 | ||||||
|  |    int16_t  blockDivider_; | ||||||
|  |    int32_t  latitudeOfRadar_; | ||||||
|  |    int32_t  longitudeOfRadar_; | ||||||
|  |    int16_t  heightOfRadar_; | ||||||
|  |    int16_t  productCode_; | ||||||
|  |    uint16_t operationalMode_; | ||||||
|  |    uint16_t volumeCoveragePattern_; | ||||||
|  |    int16_t  sequenceNumber_; | ||||||
|  |    uint16_t volumeScanNumber_; | ||||||
|  |    uint16_t volumeScanDate_; | ||||||
|  |    uint32_t volumeScanStartTime_; | ||||||
|  |    uint16_t generationDateOfProduct_; | ||||||
|  |    uint32_t generationTimeOfProduct_; | ||||||
|  |    // 27-28: Product dependent parameters 1 and 2 (Table V)
 | ||||||
|  |    uint16_t elevationNumber_; | ||||||
|  |    // 30:    Product dependent parameter 3 (Table V)
 | ||||||
|  |    // 31-46: Product dependent (Note 1)
 | ||||||
|  |    uint16_t halfword31_; | ||||||
|  |    uint16_t halfword32_; | ||||||
|  |    uint16_t halfword33_; | ||||||
|  |    uint16_t halfword34_; | ||||||
|  |    uint16_t halfword35_; | ||||||
|  |    // Halfwords 36-46 are unused
 | ||||||
|  |    // 47-53: Product dependent parameters 4-10 (Table V, Note 3)
 | ||||||
|  |    uint8_t  version_; | ||||||
|  |    uint8_t  spotBlank_; | ||||||
|  |    uint32_t offsetToSymbology_; | ||||||
|  |    uint32_t offsetToGraphic_; | ||||||
|  |    uint32_t offsetToTabular_; | ||||||
|  | 
 | ||||||
|  |    std::array<uint16_t, 10> parameters_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ProductDescriptionBlock::ProductDescriptionBlock() : | ||||||
|  |     p(std::make_unique<ProductDescriptionBlockImpl>()) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | ProductDescriptionBlock::~ProductDescriptionBlock() = default; | ||||||
|  | 
 | ||||||
|  | ProductDescriptionBlock::ProductDescriptionBlock( | ||||||
|  |    ProductDescriptionBlock&&) noexcept                    = default; | ||||||
|  | ProductDescriptionBlock& ProductDescriptionBlock::operator=( | ||||||
|  |    ProductDescriptionBlock&&) noexcept = default; | ||||||
|  | 
 | ||||||
|  | int16_t ProductDescriptionBlock::block_divider() const | ||||||
|  | { | ||||||
|  |    return p->blockDivider_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t ProductDescriptionBlock::latitude_of_radar() const | ||||||
|  | { | ||||||
|  |    return p->latitudeOfRadar_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t ProductDescriptionBlock::longitude_of_radar() const | ||||||
|  | { | ||||||
|  |    return p->longitudeOfRadar_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int16_t ProductDescriptionBlock::height_of_radar() const | ||||||
|  | { | ||||||
|  |    return p->heightOfRadar_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int16_t ProductDescriptionBlock::product_code() const | ||||||
|  | { | ||||||
|  |    return p->productCode_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t ProductDescriptionBlock::operational_mode() const | ||||||
|  | { | ||||||
|  |    return p->operationalMode_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t ProductDescriptionBlock::volume_coverage_pattern() const | ||||||
|  | { | ||||||
|  |    return p->volumeCoveragePattern_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int16_t ProductDescriptionBlock::sequence_number() const | ||||||
|  | { | ||||||
|  |    return p->sequenceNumber_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t ProductDescriptionBlock::volume_scan_number() const | ||||||
|  | { | ||||||
|  |    return p->volumeScanNumber_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t ProductDescriptionBlock::volume_scan_date() const | ||||||
|  | { | ||||||
|  |    return p->volumeScanDate_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t ProductDescriptionBlock::volume_scan_start_time() const | ||||||
|  | { | ||||||
|  |    return p->volumeScanStartTime_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t ProductDescriptionBlock::generation_date_of_product() const | ||||||
|  | { | ||||||
|  |    return p->generationDateOfProduct_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t ProductDescriptionBlock::generation_time_of_product() const | ||||||
|  | { | ||||||
|  |    return p->generationTimeOfProduct_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint16_t ProductDescriptionBlock::elevation_number() const | ||||||
|  | { | ||||||
|  |    return p->elevationNumber_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ProductDescriptionBlock::version() const | ||||||
|  | { | ||||||
|  |    return p->version_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ProductDescriptionBlock::spot_blank() const | ||||||
|  | { | ||||||
|  |    return p->spotBlank_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t ProductDescriptionBlock::offset_to_symbology() const | ||||||
|  | { | ||||||
|  |    return p->offsetToSymbology_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t ProductDescriptionBlock::offset_to_graphic() const | ||||||
|  | { | ||||||
|  |    return p->offsetToGraphic_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t ProductDescriptionBlock::offset_to_tabular() const | ||||||
|  | { | ||||||
|  |    return p->offsetToTabular_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ProductDescriptionBlock::IsCompressionEnabled() const | ||||||
|  | { | ||||||
|  |    bool isCompressed = false; | ||||||
|  | 
 | ||||||
|  |    if (compressedProducts_.contains(p->productCode_)) | ||||||
|  |    { | ||||||
|  |       isCompressed = (p->parameters_[7] == 1u); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return isCompressed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ProductDescriptionBlock::Parse(std::istream& is) | ||||||
|  | { | ||||||
|  |    bool blockValid = true; | ||||||
|  | 
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->blockDivider_), 2);            // 10
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->latitudeOfRadar_), 4);         // 11-12
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->longitudeOfRadar_), 4);        // 13-14
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->heightOfRadar_), 2);           // 15
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->productCode_), 2);             // 16
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->operationalMode_), 2);         // 17
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->volumeCoveragePattern_), 2);   // 18
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->sequenceNumber_), 2);          // 19
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->volumeScanNumber_), 2);        // 20
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->volumeScanDate_), 2);          // 21
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->volumeScanStartTime_), 4);     // 22-23
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->generationDateOfProduct_), 2); // 24
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->generationTimeOfProduct_), 4); // 25-26
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->parameters_[0]), 2 * 2);       // 27-28
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->elevationNumber_), 2);         // 29
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->parameters_[2]), 2);           // 30
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->halfword31_), 2);              // 31
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->halfword32_), 2);              // 32
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->halfword33_), 2);              // 33
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->halfword34_), 2);              // 34
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->halfword35_), 2);              // 35
 | ||||||
|  | 
 | ||||||
|  |    is.seekg(11 * 2, std::ios_base::cur); // 36-46
 | ||||||
|  | 
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->parameters_[3]), 7 * 2); // 47-53
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->version_), 1);           // 54
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->spotBlank_), 1);         // 54
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->offsetToSymbology_), 4); // 55-56
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->offsetToGraphic_), 4);   // 57-58
 | ||||||
|  |    is.read(reinterpret_cast<char*>(&p->offsetToTabular_), 4);   // 59-60
 | ||||||
|  | 
 | ||||||
|  |    p->blockDivider_            = ntohs(p->blockDivider_); | ||||||
|  |    p->latitudeOfRadar_         = ntohl(p->latitudeOfRadar_); | ||||||
|  |    p->longitudeOfRadar_        = ntohl(p->longitudeOfRadar_); | ||||||
|  |    p->heightOfRadar_           = ntohs(p->heightOfRadar_); | ||||||
|  |    p->productCode_             = ntohs(p->productCode_); | ||||||
|  |    p->operationalMode_         = ntohs(p->operationalMode_); | ||||||
|  |    p->volumeCoveragePattern_   = ntohs(p->volumeCoveragePattern_); | ||||||
|  |    p->sequenceNumber_          = ntohs(p->sequenceNumber_); | ||||||
|  |    p->volumeScanNumber_        = ntohs(p->volumeScanNumber_); | ||||||
|  |    p->volumeScanDate_          = ntohs(p->volumeScanDate_); | ||||||
|  |    p->volumeScanStartTime_     = ntohl(p->volumeScanStartTime_); | ||||||
|  |    p->generationDateOfProduct_ = ntohs(p->generationDateOfProduct_); | ||||||
|  |    p->generationTimeOfProduct_ = ntohl(p->generationTimeOfProduct_); | ||||||
|  |    p->elevationNumber_         = ntohs(p->elevationNumber_); | ||||||
|  |    p->halfword31_              = ntohs(p->halfword31_); | ||||||
|  |    p->halfword32_              = ntohs(p->halfword32_); | ||||||
|  |    p->halfword33_              = ntohs(p->halfword33_); | ||||||
|  |    p->halfword34_              = ntohs(p->halfword34_); | ||||||
|  |    p->halfword35_              = ntohs(p->halfword35_); | ||||||
|  |    p->offsetToSymbology_       = ntohl(p->offsetToSymbology_); | ||||||
|  |    p->offsetToGraphic_         = ntohl(p->offsetToGraphic_); | ||||||
|  |    p->offsetToTabular_         = ntohl(p->offsetToTabular_); | ||||||
|  | 
 | ||||||
|  |    SwapArray(p->parameters_); | ||||||
|  | 
 | ||||||
|  |    if (is.eof()) | ||||||
|  |    { | ||||||
|  |       BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Reached end of file"; | ||||||
|  |       blockValid = false; | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       if (p->blockDivider_ != -1) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(warning) | ||||||
|  |             << logPrefix_ << "Invalid block divider: " << p->blockDivider_; | ||||||
|  |          blockValid = false; | ||||||
|  |       } | ||||||
|  |       if (p->productCode_ < -299 || | ||||||
|  |           (p->productCode_ > -16 && p->productCode_ < 16) || | ||||||
|  |           p->productCode_ > 299) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(warning) | ||||||
|  |             << logPrefix_ << "Invalid product code: " << p->productCode_; | ||||||
|  |          blockValid = false; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (blockValid) | ||||||
|  |    { | ||||||
|  |       BOOST_LOG_TRIVIAL(trace) | ||||||
|  |          << logPrefix_ << "Product code: " << p->productCode_; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return blockValid; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace rpg
 | ||||||
|  | } // namespace wsr88d
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										212
									
								
								wxdata/source/scwx/wsr88d/rpg/wmo_header.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								wxdata/source/scwx/wsr88d/rpg/wmo_header.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,212 @@ | ||||||
|  | #include <scwx/wsr88d/rpg/wmo_header.hpp> | ||||||
|  | #include <scwx/util/streams.hpp> | ||||||
|  | 
 | ||||||
|  | #include <istream> | ||||||
|  | #include <sstream> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include <boost/log/trivial.hpp> | ||||||
|  | 
 | ||||||
|  | #ifdef WIN32 | ||||||
|  | #   include <WinSock2.h> | ||||||
|  | #else | ||||||
|  | #   include <arpa/inet.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace wsr88d | ||||||
|  | { | ||||||
|  | namespace rpg | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const std::string logPrefix_ = "[scwx::wsr88d::rpg::wmo_header] "; | ||||||
|  | 
 | ||||||
|  | class WmoHeaderImpl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit WmoHeaderImpl() : | ||||||
|  |        dataType_ {}, | ||||||
|  |        geographicDesignator_ {}, | ||||||
|  |        bulletinId_ {}, | ||||||
|  |        icao_ {}, | ||||||
|  |        dateTime_ {}, | ||||||
|  |        bbbIndicator_ {}, | ||||||
|  |        productCategory_ {}, | ||||||
|  |        productDesignator_ {} {}; | ||||||
|  |    ~WmoHeaderImpl() = default; | ||||||
|  | 
 | ||||||
|  |    std::string dataType_; | ||||||
|  |    std::string geographicDesignator_; | ||||||
|  |    std::string bulletinId_; | ||||||
|  |    std::string icao_; | ||||||
|  |    std::string dateTime_; | ||||||
|  |    std::string bbbIndicator_; | ||||||
|  |    std::string productCategory_; | ||||||
|  |    std::string productDesignator_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | WmoHeader::WmoHeader() : p(std::make_unique<WmoHeaderImpl>()) {} | ||||||
|  | WmoHeader::~WmoHeader() = default; | ||||||
|  | 
 | ||||||
|  | WmoHeader::WmoHeader(WmoHeader&&) noexcept = default; | ||||||
|  | WmoHeader& WmoHeader::operator=(WmoHeader&&) noexcept = default; | ||||||
|  | 
 | ||||||
|  | const std::string& WmoHeader::data_type() const | ||||||
|  | { | ||||||
|  |    return p->dataType_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const std::string& WmoHeader::geographic_designator() const | ||||||
|  | { | ||||||
|  |    return p->geographicDesignator_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const std::string& WmoHeader::bulletin_id() const | ||||||
|  | { | ||||||
|  |    return p->bulletinId_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const std::string& WmoHeader::icao() const | ||||||
|  | { | ||||||
|  |    return p->icao_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const std::string& WmoHeader::date_time() const | ||||||
|  | { | ||||||
|  |    return p->dateTime_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const std::string& WmoHeader::bbb_indicator() const | ||||||
|  | { | ||||||
|  |    return p->bbbIndicator_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const std::string& WmoHeader::product_category() const | ||||||
|  | { | ||||||
|  |    return p->productCategory_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const std::string& WmoHeader::product_designator() const | ||||||
|  | { | ||||||
|  |    return p->productDesignator_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool WmoHeader::Parse(std::istream& is) | ||||||
|  | { | ||||||
|  |    bool headerValid = true; | ||||||
|  | 
 | ||||||
|  |    std::string wmoLine; | ||||||
|  |    std::string awipsLine; | ||||||
|  | 
 | ||||||
|  |    std::getline(is, wmoLine); | ||||||
|  |    std::getline(is, awipsLine); | ||||||
|  | 
 | ||||||
|  |    if (is.eof()) | ||||||
|  |    { | ||||||
|  |       BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Reached end of file"; | ||||||
|  |       headerValid = false; | ||||||
|  |    } | ||||||
|  |    else if (!wmoLine.ends_with("\r\r")) | ||||||
|  |    { | ||||||
|  |       BOOST_LOG_TRIVIAL(debug) | ||||||
|  |          << logPrefix_ << "WMO Abbreviated Heading Line is malformed"; | ||||||
|  |       headerValid = false; | ||||||
|  |    } | ||||||
|  |    else if (!awipsLine.ends_with("\r\r")) | ||||||
|  |    { | ||||||
|  |       BOOST_LOG_TRIVIAL(debug) | ||||||
|  |          << logPrefix_ << "AWIPS Identifier Line is malformed"; | ||||||
|  |       headerValid = false; | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       // Remove delimiters from the end of the line
 | ||||||
|  |       wmoLine.erase(wmoLine.end() - 2); | ||||||
|  |       awipsLine.erase(awipsLine.end() - 2); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // WMO Abbreviated Heading Line:
 | ||||||
|  |    // T1T2A1A2ii CCCC YYGGgg (BBB)
 | ||||||
|  | 
 | ||||||
|  |    if (headerValid) | ||||||
|  |    { | ||||||
|  |       std::string              token; | ||||||
|  |       std::istringstream       wmoTokens(wmoLine); | ||||||
|  |       std::vector<std::string> wmoTokenList; | ||||||
|  | 
 | ||||||
|  |       while (wmoTokens >> token) | ||||||
|  |       { | ||||||
|  |          wmoTokenList.push_back(std::move(token)); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (wmoTokenList.size() < 3 || wmoTokenList.size() > 4) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(debug) | ||||||
|  |             << logPrefix_ << "Invalid number of WMO tokens"; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       else if (wmoTokenList[0].size() != 6) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "WMO identifier malformed"; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       else if (wmoTokenList[1].size() != 4) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "ICAO malformed"; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       else if (wmoTokenList[2].size() != 6) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Date/time malformed"; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       else if (wmoTokenList.size() == 4 && wmoTokenList[3].size() != 3) | ||||||
|  |       { | ||||||
|  |          // BBB indicator is optional
 | ||||||
|  |          BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "BBB indicator malformed"; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |       { | ||||||
|  |          p->dataType_             = wmoTokenList[0].substr(0, 2); | ||||||
|  |          p->geographicDesignator_ = wmoTokenList[0].substr(2, 2); | ||||||
|  |          p->bulletinId_           = wmoTokenList[0].substr(4, 2); | ||||||
|  |          p->icao_                 = wmoTokenList[1]; | ||||||
|  |          p->dateTime_             = wmoTokenList[2]; | ||||||
|  | 
 | ||||||
|  |          if (wmoTokenList.size() == 4) | ||||||
|  |          { | ||||||
|  |             p->bbbIndicator_ = wmoTokenList[3]; | ||||||
|  |          } | ||||||
|  |          else | ||||||
|  |          { | ||||||
|  |             p->bbbIndicator_ = ""; | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // AWIPS Identifer Line:
 | ||||||
|  |    // NNNxxx
 | ||||||
|  | 
 | ||||||
|  |    if (headerValid) | ||||||
|  |    { | ||||||
|  |       if (awipsLine.size() != 6) | ||||||
|  |       { | ||||||
|  |          BOOST_LOG_TRIVIAL(debug) | ||||||
|  |             << logPrefix_ << "AWIPS Identifier Line bad size"; | ||||||
|  |          headerValid = false; | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |       { | ||||||
|  |          p->productCategory_   = awipsLine.substr(0, 3); | ||||||
|  |          p->productDesignator_ = awipsLine.substr(3, 3); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return headerValid; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace rpg
 | ||||||
|  | } // namespace wsr88d
 | ||||||
|  | } // namespace scwx
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat