mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 00:40: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
				
			
		
							
								
								
									
										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