mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 21:10:04 +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/rpg/ar2v_file.hpp>
|
||||||
|
#include <scwx/wsr88d/rda/message_header.hpp>
|
||||||
#include <scwx/util/rangebuf.hpp>
|
#include <scwx/util/rangebuf.hpp>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
@ -33,18 +34,22 @@ public:
|
||||||
julianDate_ {0},
|
julianDate_ {0},
|
||||||
milliseconds_ {0},
|
milliseconds_ {0},
|
||||||
icao_(),
|
icao_(),
|
||||||
numRecords_ {0} {};
|
numRecords_ {0},
|
||||||
|
rawRecords_() {};
|
||||||
~Ar2vFileImpl() = default;
|
~Ar2vFileImpl() = default;
|
||||||
|
|
||||||
void ParseLDMRecords(std::ifstream& f);
|
void LoadLDMRecords(std::ifstream& f);
|
||||||
|
void ParseLDMRecords();
|
||||||
|
|
||||||
std::string tapeFilename_;
|
std::string tapeFilename_;
|
||||||
std::string extensionNumber_;
|
std::string extensionNumber_;
|
||||||
int32_t julianDate_;
|
uint32_t julianDate_;
|
||||||
int32_t milliseconds_;
|
uint32_t milliseconds_;
|
||||||
std::string icao_;
|
std::string icao_;
|
||||||
|
|
||||||
size_t numRecords_;
|
size_t numRecords_;
|
||||||
|
|
||||||
|
std::list<std::stringstream> rawRecords_;
|
||||||
};
|
};
|
||||||
|
|
||||||
Ar2vFile::Ar2vFile() : p(std::make_unique<Ar2vFileImpl>()) {}
|
Ar2vFile::Ar2vFile() : p(std::make_unique<Ar2vFileImpl>()) {}
|
||||||
|
|
@ -101,13 +106,13 @@ bool Ar2vFile::LoadFile(const std::string& filename)
|
||||||
<< logPrefix_ << "Time: " << p->milliseconds_;
|
<< logPrefix_ << "Time: " << p->milliseconds_;
|
||||||
BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "ICAO: " << p->icao_;
|
BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "ICAO: " << p->icao_;
|
||||||
|
|
||||||
p->ParseLDMRecords(f);
|
p->LoadLDMRecords(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileValid;
|
return fileValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ar2vFileImpl::ParseLDMRecords(std::ifstream& f)
|
void Ar2vFileImpl::LoadLDMRecords(std::ifstream& f)
|
||||||
{
|
{
|
||||||
numRecords_ = 0;
|
numRecords_ = 0;
|
||||||
|
|
||||||
|
|
@ -122,7 +127,7 @@ void Ar2vFileImpl::ParseLDMRecords(std::ifstream& f)
|
||||||
controlWord = htonl(controlWord);
|
controlWord = htonl(controlWord);
|
||||||
recordSize = std::abs(controlWord);
|
recordSize = std::abs(controlWord);
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug)
|
BOOST_LOG_TRIVIAL(trace)
|
||||||
<< logPrefix_ << "LDM Record Found: Size = " << recordSize << " bytes";
|
<< logPrefix_ << "LDM Record Found: Size = " << recordSize << " bytes";
|
||||||
|
|
||||||
boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
|
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(boost::iostreams::bzip2_decompressor());
|
||||||
in.push(r);
|
in.push(r);
|
||||||
|
|
||||||
std::ostringstream of;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::streamsize bytesCopied = boost::iostreams::copy(in, of);
|
std::stringstream ss;
|
||||||
BOOST_LOG_TRIVIAL(debug)
|
std::streamsize bytesCopied = boost::iostreams::copy(in, ss);
|
||||||
|
BOOST_LOG_TRIVIAL(trace)
|
||||||
<< logPrefix_ << "Decompressed record size = " << bytesCopied
|
<< logPrefix_ << "Decompressed record size = " << bytesCopied
|
||||||
<< " bytes";
|
<< " bytes";
|
||||||
|
|
||||||
|
rawRecords_.push_back(std::move(ss));
|
||||||
}
|
}
|
||||||
catch (const boost::iostreams::bzip2_error& ex)
|
catch (const boost::iostreams::bzip2_error& ex)
|
||||||
{
|
{
|
||||||
|
|
@ -151,10 +157,64 @@ void Ar2vFileImpl::ParseLDMRecords(std::ifstream& f)
|
||||||
++numRecords_;
|
++numRecords_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParseLDMRecords();
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug)
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
<< logPrefix_ << "Found " << numRecords_ << " LDM Records";
|
<< 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 rpg
|
||||||
} // namespace wsr88d
|
} // namespace wsr88d
|
||||||
} // namespace scwx
|
} // namespace scwx
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,20 @@ set(HDR_UTIL include/scwx/util/rangebuf.hpp)
|
||||||
set(SRC_UTIL source/scwx/util/rangebuf.cpp)
|
set(SRC_UTIL source/scwx/util/rangebuf.cpp)
|
||||||
set(HDR_WSR88D_RPG include/scwx/wsr88d/rpg/ar2v_file.hpp)
|
set(HDR_WSR88D_RPG include/scwx/wsr88d/rpg/ar2v_file.hpp)
|
||||||
set(SRC_WSR88D_RPG source/scwx/wsr88d/rpg/ar2v_file.cpp)
|
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}
|
add_library(wxdata OBJECT ${HDR_UTIL}
|
||||||
${SRC_UTIL}
|
${SRC_UTIL}
|
||||||
|
${HDR_WSR88D_RDA}
|
||||||
|
${SRC_WSR88D_RDA}
|
||||||
${HDR_WSR88D_RPG}
|
${HDR_WSR88D_RPG}
|
||||||
${SRC_WSR88D_RPG})
|
${SRC_WSR88D_RPG})
|
||||||
|
|
||||||
source_group("Header Files\\util" FILES ${HDR_UTIL})
|
source_group("Header Files\\util" FILES ${HDR_UTIL})
|
||||||
source_group("Source Files\\util" FILES ${SRC_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("Header Files\\wsr88d\\rpg" FILES ${HDR_WSR88D_RPG})
|
||||||
source_group("Source Files\\wsr88d\\rpg" FILES ${SRC_WSR88D_RPG})
|
source_group("Source Files\\wsr88d\\rpg" FILES ${SRC_WSR88D_RPG})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue