mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 08:50:05 +00:00
Parse RDA message headers
This commit is contained in:
parent
33c114ee9d
commit
85ccd25705
4 changed files with 284 additions and 11 deletions
44
wxdata/include/scwx/wsr88d/rda/message_header.hpp
Normal file
44
wxdata/include/scwx/wsr88d/rda/message_header.hpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace scwx
|
||||
{
|
||||
namespace wsr88d
|
||||
{
|
||||
namespace rda
|
||||
{
|
||||
|
||||
class MessageHeaderImpl;
|
||||
|
||||
class MessageHeader
|
||||
{
|
||||
public:
|
||||
explicit MessageHeader();
|
||||
~MessageHeader();
|
||||
|
||||
MessageHeader(const MessageHeader&) = delete;
|
||||
MessageHeader& operator=(const MessageHeader&) = delete;
|
||||
|
||||
MessageHeader(MessageHeader&&) noexcept;
|
||||
MessageHeader& operator=(MessageHeader&&);
|
||||
|
||||
uint16_t message_size() const;
|
||||
uint8_t rda_redundant_channel() const;
|
||||
uint8_t message_type() const;
|
||||
uint16_t id_sequence_number() const;
|
||||
uint16_t julian_date() const;
|
||||
uint32_t milliseconds_of_day() const;
|
||||
uint16_t number_of_message_segments() const;
|
||||
uint16_t message_segment_number() const;
|
||||
|
||||
bool Parse(std::istream& is);
|
||||
|
||||
static const size_t SIZE = 16u;
|
||||
|
||||
private:
|
||||
std::unique_ptr<MessageHeaderImpl> p;
|
||||
};
|
||||
|
||||
} // namespace rda
|
||||
} // namespace wsr88d
|
||||
} // namespace scwx
|
||||
163
wxdata/source/scwx/wsr88d/rda/message_header.cpp
Normal file
163
wxdata/source/scwx/wsr88d/rda/message_header.cpp
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#include <scwx/wsr88d/rda/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 rda
|
||||
{
|
||||
|
||||
static const std::string logPrefix_ = "[scwx::wsr88d::rda::message_header] ";
|
||||
|
||||
class MessageHeaderImpl
|
||||
{
|
||||
public:
|
||||
explicit MessageHeaderImpl() :
|
||||
messageSize_(),
|
||||
rdaRedundantChannel_(),
|
||||
messageType_(),
|
||||
idSequenceNumber_(),
|
||||
julianDate_(),
|
||||
millisecondsOfDay_(),
|
||||
numberOfMessageSegments_(),
|
||||
messageSegmentNumber_() {};
|
||||
~MessageHeaderImpl() = default;
|
||||
|
||||
uint16_t messageSize_;
|
||||
uint8_t rdaRedundantChannel_;
|
||||
uint8_t messageType_;
|
||||
uint16_t idSequenceNumber_;
|
||||
uint16_t julianDate_;
|
||||
uint32_t millisecondsOfDay_;
|
||||
uint16_t numberOfMessageSegments_;
|
||||
uint16_t messageSegmentNumber_;
|
||||
};
|
||||
|
||||
MessageHeader::MessageHeader() :
|
||||
p(std::make_unique<MessageHeaderImpl>())
|
||||
{
|
||||
}
|
||||
MessageHeader::~MessageHeader() = default;
|
||||
|
||||
MessageHeader::MessageHeader(MessageHeader&&) noexcept = default;
|
||||
MessageHeader& MessageHeader::operator=(MessageHeader&&) = default;
|
||||
|
||||
uint16_t MessageHeader::message_size() const
|
||||
{
|
||||
return p->messageSize_;
|
||||
}
|
||||
|
||||
uint8_t MessageHeader::rda_redundant_channel() const
|
||||
{
|
||||
return p->rdaRedundantChannel_;
|
||||
}
|
||||
|
||||
uint8_t MessageHeader::message_type() const
|
||||
{
|
||||
return p->messageType_;
|
||||
}
|
||||
|
||||
uint16_t MessageHeader::id_sequence_number() const
|
||||
{
|
||||
return p->idSequenceNumber_;
|
||||
}
|
||||
|
||||
uint16_t MessageHeader::julian_date() const
|
||||
{
|
||||
return p->julianDate_;
|
||||
}
|
||||
|
||||
uint32_t MessageHeader::milliseconds_of_day() const
|
||||
{
|
||||
return p->millisecondsOfDay_;
|
||||
}
|
||||
|
||||
uint16_t MessageHeader::number_of_message_segments() const
|
||||
{
|
||||
return p->numberOfMessageSegments_;
|
||||
}
|
||||
|
||||
uint16_t MessageHeader::message_segment_number() const
|
||||
{
|
||||
return p->messageSegmentNumber_;
|
||||
}
|
||||
|
||||
bool MessageHeader::Parse(std::istream& is)
|
||||
{
|
||||
bool headerValid = true;
|
||||
|
||||
is.read(reinterpret_cast<char*>(&p->messageSize_), 2);
|
||||
is.read(reinterpret_cast<char*>(&p->rdaRedundantChannel_), 1);
|
||||
is.read(reinterpret_cast<char*>(&p->messageType_), 1);
|
||||
is.read(reinterpret_cast<char*>(&p->idSequenceNumber_), 2);
|
||||
is.read(reinterpret_cast<char*>(&p->julianDate_), 2);
|
||||
is.read(reinterpret_cast<char*>(&p->millisecondsOfDay_), 4);
|
||||
is.read(reinterpret_cast<char*>(&p->numberOfMessageSegments_), 2);
|
||||
is.read(reinterpret_cast<char*>(&p->messageSegmentNumber_), 2);
|
||||
|
||||
p->messageSize_ = htons(p->messageSize_);
|
||||
p->idSequenceNumber_ = htons(p->idSequenceNumber_);
|
||||
p->julianDate_ = htons(p->julianDate_);
|
||||
p->millisecondsOfDay_ = htonl(p->millisecondsOfDay_);
|
||||
p->numberOfMessageSegments_ = htons(p->numberOfMessageSegments_);
|
||||
p->messageSegmentNumber_ = htons(p->messageSegmentNumber_);
|
||||
|
||||
if (is.eof())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Reached end of file";
|
||||
headerValid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p->messageSize_ < 9)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Invalid message size: " << p->messageSize_;
|
||||
headerValid = false;
|
||||
}
|
||||
if (p->julianDate_ < 1)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Invalid date: " << p->julianDate_;
|
||||
headerValid = false;
|
||||
}
|
||||
if (p->millisecondsOfDay_ > 86'399'999u)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Invalid milliseconds: " << p->millisecondsOfDay_;
|
||||
headerValid = false;
|
||||
}
|
||||
if (p->messageSize_ < 65534 &&
|
||||
p->messageSegmentNumber_ > p->numberOfMessageSegments_)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Invalid segment = " << p->messageSegmentNumber_
|
||||
<< "/" << p->numberOfMessageSegments_;
|
||||
headerValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (headerValid)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< logPrefix_
|
||||
<< "Message type: " << static_cast<unsigned>(p->messageType_);
|
||||
}
|
||||
|
||||
return headerValid;
|
||||
}
|
||||
|
||||
} // namespace rda
|
||||
} // namespace wsr88d
|
||||
} // namespace scwx
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include <scwx/wsr88d/rpg/ar2v_file.hpp>
|
||||
#include <scwx/wsr88d/rda/message_header.hpp>
|
||||
#include <scwx/util/rangebuf.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
|
@ -33,18 +34,22 @@ public:
|
|||
julianDate_ {0},
|
||||
milliseconds_ {0},
|
||||
icao_(),
|
||||
numRecords_ {0} {};
|
||||
numRecords_ {0},
|
||||
rawRecords_() {};
|
||||
~Ar2vFileImpl() = default;
|
||||
|
||||
void ParseLDMRecords(std::ifstream& f);
|
||||
void LoadLDMRecords(std::ifstream& f);
|
||||
void ParseLDMRecords();
|
||||
|
||||
std::string tapeFilename_;
|
||||
std::string extensionNumber_;
|
||||
int32_t julianDate_;
|
||||
int32_t milliseconds_;
|
||||
uint32_t julianDate_;
|
||||
uint32_t milliseconds_;
|
||||
std::string icao_;
|
||||
|
||||
size_t numRecords_;
|
||||
|
||||
std::list<std::stringstream> rawRecords_;
|
||||
};
|
||||
|
||||
Ar2vFile::Ar2vFile() : p(std::make_unique<Ar2vFileImpl>()) {}
|
||||
|
|
@ -101,13 +106,13 @@ bool Ar2vFile::LoadFile(const std::string& filename)
|
|||
<< logPrefix_ << "Time: " << p->milliseconds_;
|
||||
BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "ICAO: " << p->icao_;
|
||||
|
||||
p->ParseLDMRecords(f);
|
||||
p->LoadLDMRecords(f);
|
||||
}
|
||||
|
||||
return fileValid;
|
||||
}
|
||||
|
||||
void Ar2vFileImpl::ParseLDMRecords(std::ifstream& f)
|
||||
void Ar2vFileImpl::LoadLDMRecords(std::ifstream& f)
|
||||
{
|
||||
numRecords_ = 0;
|
||||
|
||||
|
|
@ -122,7 +127,7 @@ void Ar2vFileImpl::ParseLDMRecords(std::ifstream& f)
|
|||
controlWord = htonl(controlWord);
|
||||
recordSize = std::abs(controlWord);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< logPrefix_ << "LDM Record Found: Size = " << recordSize << " bytes";
|
||||
|
||||
boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
|
||||
|
|
@ -130,14 +135,15 @@ void Ar2vFileImpl::ParseLDMRecords(std::ifstream& f)
|
|||
in.push(boost::iostreams::bzip2_decompressor());
|
||||
in.push(r);
|
||||
|
||||
std::ostringstream of;
|
||||
|
||||
try
|
||||
{
|
||||
std::streamsize bytesCopied = boost::iostreams::copy(in, of);
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
std::stringstream ss;
|
||||
std::streamsize bytesCopied = boost::iostreams::copy(in, ss);
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< logPrefix_ << "Decompressed record size = " << bytesCopied
|
||||
<< " bytes";
|
||||
|
||||
rawRecords_.push_back(std::move(ss));
|
||||
}
|
||||
catch (const boost::iostreams::bzip2_error& ex)
|
||||
{
|
||||
|
|
@ -151,10 +157,64 @@ void Ar2vFileImpl::ParseLDMRecords(std::ifstream& f)
|
|||
++numRecords_;
|
||||
}
|
||||
|
||||
ParseLDMRecords();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< logPrefix_ << "Found " << numRecords_ << " LDM Records";
|
||||
}
|
||||
|
||||
void Ar2vFileImpl::ParseLDMRecords()
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
for (auto it = rawRecords_.begin(); it != rawRecords_.end(); it++)
|
||||
{
|
||||
std::stringstream& ss = *it;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << logPrefix_ << "Record " << count++;
|
||||
|
||||
// The communications manager inserts an extra 12 bytes at the beginning
|
||||
// of each record
|
||||
ss.seekg(12);
|
||||
|
||||
while (!ss.eof())
|
||||
{
|
||||
// TODO: Parse message, not just header
|
||||
rda::MessageHeader header;
|
||||
if (!header.Parse(ss))
|
||||
{
|
||||
// Invalid header
|
||||
break;
|
||||
}
|
||||
|
||||
// Seek to the end of the current message
|
||||
ss.seekg(header.message_size() * 2 - rda::MessageHeader::SIZE,
|
||||
std::ios_base::cur);
|
||||
|
||||
off_t offset = 0;
|
||||
uint16_t nextSize = 0u;
|
||||
do
|
||||
{
|
||||
ss.read(reinterpret_cast<char*>(&nextSize), 2);
|
||||
if (nextSize == 0)
|
||||
{
|
||||
offset += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ss.seekg(-2, std::ios_base::cur);
|
||||
}
|
||||
} while (!ss.eof() && nextSize == 0u);
|
||||
|
||||
if (!ss.eof() && offset != 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< logPrefix_ << "Next record offset by " << offset << " bytes";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rpg
|
||||
} // namespace wsr88d
|
||||
} // namespace scwx
|
||||
|
|
|
|||
|
|
@ -6,14 +6,20 @@ set(HDR_UTIL include/scwx/util/rangebuf.hpp)
|
|||
set(SRC_UTIL source/scwx/util/rangebuf.cpp)
|
||||
set(HDR_WSR88D_RPG include/scwx/wsr88d/rpg/ar2v_file.hpp)
|
||||
set(SRC_WSR88D_RPG source/scwx/wsr88d/rpg/ar2v_file.cpp)
|
||||
set(HDR_WSR88D_RDA include/scwx/wsr88d/rda/message_header.hpp)
|
||||
set(SRC_WSR88D_RDA source/scwx/wsr88d/rda/message_header.cpp)
|
||||
|
||||
add_library(wxdata OBJECT ${HDR_UTIL}
|
||||
${SRC_UTIL}
|
||||
${HDR_WSR88D_RDA}
|
||||
${SRC_WSR88D_RDA}
|
||||
${HDR_WSR88D_RPG}
|
||||
${SRC_WSR88D_RPG})
|
||||
|
||||
source_group("Header Files\\util" FILES ${HDR_UTIL})
|
||||
source_group("Source Files\\util" FILES ${SRC_UTIL})
|
||||
source_group("Header Files\\wsr88d\\rda" FILES ${HDR_WSR88D_RDA})
|
||||
source_group("Source Files\\wsr88d\\rda" FILES ${SRC_WSR88D_RDA})
|
||||
source_group("Header Files\\wsr88d\\rpg" FILES ${HDR_WSR88D_RPG})
|
||||
source_group("Source Files\\wsr88d\\rpg" FILES ${SRC_WSR88D_RPG})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue