mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 23:20:05 +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