mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 19:20:05 +00:00
Parse Clutter Filter Map (Message 15)
This commit is contained in:
parent
f36d57b71d
commit
3ab4ec4b9b
15 changed files with 682 additions and 22 deletions
19
wxdata/source/scwx/util/vectorbuf.cpp
Normal file
19
wxdata/source/scwx/util/vectorbuf.cpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#include <scwx/util/vectorbuf.hpp>
|
||||
|
||||
namespace scwx
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
vectorbuf::vectorbuf(std::vector<char>& v) : v_(v)
|
||||
{
|
||||
update_read_pointers(0);
|
||||
}
|
||||
|
||||
void vectorbuf::update_read_pointers(size_t size)
|
||||
{
|
||||
setg(v_.data(), v_.data(), v_.data() + size);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace scwx
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include <scwx/wsr88d/ar2v_file.hpp>
|
||||
#include <scwx/wsr88d/rda/message_header.hpp>
|
||||
#include <scwx/wsr88d/rda/message_factory.hpp>
|
||||
#include <scwx/util/rangebuf.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
|
@ -54,7 +54,7 @@ Ar2vFile::Ar2vFile() : p(std::make_unique<Ar2vFileImpl>()) {}
|
|||
Ar2vFile::~Ar2vFile() = default;
|
||||
|
||||
Ar2vFile::Ar2vFile(Ar2vFile&&) noexcept = default;
|
||||
Ar2vFile& Ar2vFile::operator=(Ar2vFile&&) = default;
|
||||
Ar2vFile& Ar2vFile::operator=(Ar2vFile&&) noexcept = default;
|
||||
|
||||
bool Ar2vFile::LoadFile(const std::string& filename)
|
||||
{
|
||||
|
|
@ -112,6 +112,8 @@ bool Ar2vFile::LoadFile(const std::string& filename)
|
|||
|
||||
void Ar2vFileImpl::LoadLDMRecords(std::ifstream& f)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Loading LDM Records";
|
||||
|
||||
numRecords_ = 0;
|
||||
|
||||
while (f.peek() != EOF)
|
||||
|
|
@ -163,6 +165,8 @@ void Ar2vFileImpl::LoadLDMRecords(std::ifstream& f)
|
|||
|
||||
void Ar2vFileImpl::ParseLDMRecords()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << logPrefix_ << "Parsing LDM Records";
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
for (auto it = rawRecords_.begin(); it != rawRecords_.end(); it++)
|
||||
|
|
@ -177,18 +181,13 @@ void Ar2vFileImpl::ParseLDMRecords()
|
|||
|
||||
while (!ss.eof())
|
||||
{
|
||||
// TODO: Parse message, not just header
|
||||
rda::MessageHeader header;
|
||||
if (!header.Parse(ss))
|
||||
rda::MessageInfo msgInfo = rda::MessageFactory::Create(ss);
|
||||
if (!msgInfo.headerValid)
|
||||
{
|
||||
// Invalid header
|
||||
// Invalid message
|
||||
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
|
||||
|
|
|
|||
234
wxdata/source/scwx/wsr88d/rda/clutter_filter_map.cpp
Normal file
234
wxdata/source/scwx/wsr88d/rda/clutter_filter_map.cpp
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
#include <scwx/wsr88d/rda/clutter_filter_map.hpp>
|
||||
|
||||
#include <istream>
|
||||
#include <vector>
|
||||
|
||||
#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::clutter_filter_map] ";
|
||||
|
||||
struct RangeZone
|
||||
{
|
||||
uint16_t opCode;
|
||||
uint16_t endRange;
|
||||
};
|
||||
|
||||
class ClutterFilterMapImpl
|
||||
{
|
||||
public:
|
||||
explicit ClutterFilterMapImpl() :
|
||||
mapGenerationDate_(), mapGenerationTime_(), rangeZones_() {};
|
||||
~ClutterFilterMapImpl() = default;
|
||||
|
||||
uint16_t mapGenerationDate_;
|
||||
uint16_t mapGenerationTime_;
|
||||
|
||||
std::vector<std::vector<std::vector<RangeZone>>> rangeZones_;
|
||||
};
|
||||
|
||||
ClutterFilterMap::ClutterFilterMap() :
|
||||
Message(), p(std::make_unique<ClutterFilterMapImpl>())
|
||||
{
|
||||
}
|
||||
ClutterFilterMap::~ClutterFilterMap() = default;
|
||||
|
||||
ClutterFilterMap::ClutterFilterMap(ClutterFilterMap&&) noexcept = default;
|
||||
ClutterFilterMap&
|
||||
ClutterFilterMap::operator=(ClutterFilterMap&&) noexcept = default;
|
||||
|
||||
uint16_t ClutterFilterMap::map_generation_date() const
|
||||
{
|
||||
return p->mapGenerationDate_;
|
||||
}
|
||||
|
||||
uint16_t ClutterFilterMap::map_generation_time() const
|
||||
{
|
||||
return p->mapGenerationTime_;
|
||||
}
|
||||
|
||||
uint16_t ClutterFilterMap::number_of_elevation_segments() const
|
||||
{
|
||||
return static_cast<uint16_t>(p->rangeZones_.size());
|
||||
}
|
||||
|
||||
uint16_t ClutterFilterMap::number_of_range_zones(uint16_t e, uint16_t a) const
|
||||
{
|
||||
return static_cast<uint16_t>(p->rangeZones_[e][a].size());
|
||||
}
|
||||
|
||||
uint16_t ClutterFilterMap::op_code(uint16_t e, uint16_t a, uint16_t z) const
|
||||
{
|
||||
return p->rangeZones_[e][a][z].opCode;
|
||||
}
|
||||
|
||||
uint16_t ClutterFilterMap::end_range(uint16_t e, uint16_t a, uint16_t z) const
|
||||
{
|
||||
return p->rangeZones_[e][a][z].endRange;
|
||||
}
|
||||
|
||||
bool ClutterFilterMap::Parse(std::istream& is)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< logPrefix_ << "Parsing Clutter Filter Map (Message Type 15)";
|
||||
|
||||
bool messageValid = true;
|
||||
size_t bytesRead = 0;
|
||||
uint16_t numElevationSegments = 0;
|
||||
|
||||
is.read(reinterpret_cast<char*>(&p->mapGenerationDate_), 2);
|
||||
is.read(reinterpret_cast<char*>(&p->mapGenerationTime_), 2);
|
||||
is.read(reinterpret_cast<char*>(&numElevationSegments), 2);
|
||||
bytesRead += 6;
|
||||
|
||||
p->mapGenerationDate_ = htons(p->mapGenerationDate_);
|
||||
p->mapGenerationTime_ = htons(p->mapGenerationTime_);
|
||||
numElevationSegments = htons(numElevationSegments);
|
||||
|
||||
if (is.eof())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning) << logPrefix_ << "Reached end of file (1)";
|
||||
messageValid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p->mapGenerationDate_ < 1)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Invalid date: " << p->mapGenerationDate_;
|
||||
messageValid = false;
|
||||
}
|
||||
if (p->mapGenerationTime_ > 1440)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Invalid time: " << p->mapGenerationTime_;
|
||||
messageValid = false;
|
||||
}
|
||||
if (numElevationSegments < 1 || numElevationSegments > 5)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_
|
||||
<< "Invalid number of elevation segments: " << numElevationSegments;
|
||||
messageValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!messageValid)
|
||||
{
|
||||
numElevationSegments = 0;
|
||||
}
|
||||
|
||||
p->rangeZones_.resize(numElevationSegments);
|
||||
|
||||
for (uint16_t e = 0; e < numElevationSegments && messageValid; e++)
|
||||
{
|
||||
p->rangeZones_[e].resize(NUM_AZIMUTH_SEGMENTS);
|
||||
|
||||
for (uint16_t a = 0; a < NUM_AZIMUTH_SEGMENTS && messageValid; a++)
|
||||
{
|
||||
uint16_t numRangeZones;
|
||||
is.read(reinterpret_cast<char*>(&numRangeZones), 2);
|
||||
bytesRead += 2;
|
||||
|
||||
numRangeZones = htons(numRangeZones);
|
||||
|
||||
if (is.eof())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Reached end of file (2)";
|
||||
messageValid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (numRangeZones < 1 || numRangeZones > 20)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_
|
||||
<< "Invalid number of range zones: " << numRangeZones;
|
||||
messageValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!messageValid)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
p->rangeZones_[e][a].resize(numRangeZones);
|
||||
|
||||
for (uint16_t z = 0; z < numRangeZones && messageValid; z++)
|
||||
{
|
||||
RangeZone& zone = p->rangeZones_[e][a][z];
|
||||
|
||||
is.read(reinterpret_cast<char*>(&zone.opCode), 2);
|
||||
is.read(reinterpret_cast<char*>(&zone.endRange), 2);
|
||||
bytesRead += 4;
|
||||
|
||||
zone.opCode = htons(zone.opCode);
|
||||
zone.endRange = htons(zone.endRange);
|
||||
|
||||
if (is.eof())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Reached end of file (3)";
|
||||
messageValid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zone.opCode > 2)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Invalid op code: " << zone.opCode;
|
||||
messageValid = false;
|
||||
}
|
||||
if (zone.endRange > 511)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Invalid end range: " << zone.endRange;
|
||||
messageValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ValidateSize(is, bytesRead))
|
||||
{
|
||||
messageValid = false;
|
||||
}
|
||||
|
||||
if (!messageValid)
|
||||
{
|
||||
p->rangeZones_.resize(0);
|
||||
p->rangeZones_.shrink_to_fit();
|
||||
}
|
||||
|
||||
return messageValid;
|
||||
}
|
||||
|
||||
std::unique_ptr<ClutterFilterMap>
|
||||
ClutterFilterMap::Create(MessageHeader&& header, std::istream& is)
|
||||
{
|
||||
std::unique_ptr<ClutterFilterMap> message =
|
||||
std::make_unique<ClutterFilterMap>();
|
||||
message->set_header(std::move(header));
|
||||
message->Parse(is);
|
||||
return message;
|
||||
}
|
||||
|
||||
} // namespace rda
|
||||
} // namespace wsr88d
|
||||
} // namespace scwx
|
||||
72
wxdata/source/scwx/wsr88d/rda/message.cpp
Normal file
72
wxdata/source/scwx/wsr88d/rda/message.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#include <scwx/wsr88d/rda/message.hpp>
|
||||
|
||||
#include <istream>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace scwx
|
||||
{
|
||||
namespace wsr88d
|
||||
{
|
||||
namespace rda
|
||||
{
|
||||
|
||||
static const std::string logPrefix_ = "[scwx::wsr88d::rda::message] ";
|
||||
|
||||
class MessageImpl
|
||||
{
|
||||
public:
|
||||
explicit MessageImpl() : header_() {};
|
||||
~MessageImpl() = default;
|
||||
|
||||
MessageHeader header_;
|
||||
};
|
||||
|
||||
Message::Message() : p(std::make_unique<MessageImpl>()) {}
|
||||
Message::~Message() = default;
|
||||
|
||||
Message::Message(Message&&) noexcept = default;
|
||||
Message& Message::operator=(Message&&) noexcept = default;
|
||||
|
||||
bool Message::ValidateSize(std::istream& is, size_t bytesRead) const
|
||||
{
|
||||
bool messageValid = true;
|
||||
size_t dataSize = header().message_size() * 2 - header().SIZE;
|
||||
|
||||
if (bytesRead != dataSize)
|
||||
{
|
||||
is.seekg(static_cast<std::streamoff>(dataSize) -
|
||||
static_cast<std::streamoff>(bytesRead),
|
||||
std::ios_base::cur);
|
||||
|
||||
if (bytesRead < dataSize)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< logPrefix_ << "Message contents smaller than size: " << bytesRead
|
||||
<< " < " << dataSize << " bytes";
|
||||
}
|
||||
if (bytesRead > dataSize)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Message contents larger than size: " << bytesRead
|
||||
<< " > " << dataSize << " bytes";
|
||||
messageValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return messageValid;
|
||||
}
|
||||
|
||||
const MessageHeader& Message::header() const
|
||||
{
|
||||
return p->header_;
|
||||
}
|
||||
|
||||
void Message::set_header(MessageHeader&& header)
|
||||
{
|
||||
p->header_ = std::move(header);
|
||||
}
|
||||
|
||||
} // namespace rda
|
||||
} // namespace wsr88d
|
||||
} // namespace scwx
|
||||
130
wxdata/source/scwx/wsr88d/rda/message_factory.cpp
Normal file
130
wxdata/source/scwx/wsr88d/rda/message_factory.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#include <scwx/wsr88d/rda/message_factory.hpp>
|
||||
|
||||
#include <scwx/util/vectorbuf.hpp>
|
||||
#include <scwx/wsr88d/rda/clutter_filter_map.hpp>
|
||||
|
||||
#include <istream>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace scwx
|
||||
{
|
||||
namespace wsr88d
|
||||
{
|
||||
namespace rda
|
||||
{
|
||||
|
||||
static const std::string logPrefix_ = "[scwx::wsr88d::rda::message_factory] ";
|
||||
|
||||
typedef std::function<std::unique_ptr<Message>(MessageHeader&&, std::istream&)>
|
||||
CreateMessageFunction;
|
||||
|
||||
static const std::unordered_map<uint8_t, CreateMessageFunction> create_ {
|
||||
{15, ClutterFilterMap::Create}};
|
||||
|
||||
static std::vector<char> messageData_;
|
||||
static size_t bufferedSize_;
|
||||
static util::vectorbuf messageBuffer_(messageData_);
|
||||
static std::istream messageBufferStream_(&messageBuffer_);
|
||||
|
||||
MessageInfo MessageFactory::Create(std::istream& is)
|
||||
{
|
||||
MessageInfo info;
|
||||
MessageHeader header;
|
||||
info.headerValid = header.Parse(is);
|
||||
info.messageValid = info.headerValid;
|
||||
|
||||
if (info.headerValid && create_.find(header.message_type()) == create_.end())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "Unknown message type: "
|
||||
<< static_cast<unsigned>(header.message_type());
|
||||
info.messageValid = false;
|
||||
}
|
||||
|
||||
if (info.messageValid)
|
||||
{
|
||||
uint16_t segment = header.message_segment_number();
|
||||
uint16_t totalSegments = header.number_of_message_segments();
|
||||
uint8_t messageType = header.message_type();
|
||||
size_t dataSize = header.message_size() * 2 - MessageHeader::SIZE;
|
||||
|
||||
std::istream* messageStream = nullptr;
|
||||
|
||||
if (totalSegments == 1)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << logPrefix_ << "Found Message "
|
||||
<< static_cast<unsigned>(messageType);
|
||||
messageStream = &is;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< logPrefix_ << "Found Message "
|
||||
<< static_cast<unsigned>(messageType) << " Segment " << segment
|
||||
<< "/" << totalSegments;
|
||||
|
||||
if (segment == 1)
|
||||
{
|
||||
// Estimate total message size
|
||||
messageData_.reserve(dataSize * totalSegments);
|
||||
bufferedSize_ = 0;
|
||||
}
|
||||
|
||||
if (messageData_.capacity() < bufferedSize_ + dataSize)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< logPrefix_ << "Bad size estimate, increasing size";
|
||||
|
||||
// Estimate remaining size
|
||||
uint16_t remainingSegments =
|
||||
std::max<uint16_t>(totalSegments - segment + 1, 100u);
|
||||
size_t remainingSize = remainingSegments * dataSize;
|
||||
|
||||
messageData_.reserve(bufferedSize_ + remainingSize);
|
||||
}
|
||||
|
||||
is.read(messageData_.data() + bufferedSize_, dataSize);
|
||||
bufferedSize_ += dataSize;
|
||||
|
||||
if (is.eof())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< logPrefix_ << "End of file reached trying to buffer message";
|
||||
info.messageValid = false;
|
||||
messageData_.shrink_to_fit();
|
||||
bufferedSize_ = 0;
|
||||
}
|
||||
else if (segment == totalSegments)
|
||||
{
|
||||
messageBuffer_.update_read_pointers(bufferedSize_);
|
||||
header.set_message_size(
|
||||
static_cast<uint16_t>(bufferedSize_ / 2 + MessageHeader::SIZE));
|
||||
|
||||
messageStream = &messageBufferStream_;
|
||||
}
|
||||
}
|
||||
|
||||
if (messageStream != nullptr)
|
||||
{
|
||||
info.message =
|
||||
create_.at(messageType)(std::move(header), *messageStream);
|
||||
messageData_.shrink_to_fit();
|
||||
bufferedSize_ = 0;
|
||||
}
|
||||
}
|
||||
else if (info.headerValid)
|
||||
{
|
||||
// Seek to the end of the current message
|
||||
is.seekg(header.message_size() * 2 - rda::MessageHeader::SIZE,
|
||||
std::ios_base::cur);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
} // namespace rda
|
||||
} // namespace wsr88d
|
||||
} // namespace scwx
|
||||
|
|
@ -44,14 +44,11 @@ public:
|
|||
uint16_t messageSegmentNumber_;
|
||||
};
|
||||
|
||||
MessageHeader::MessageHeader() :
|
||||
p(std::make_unique<MessageHeaderImpl>())
|
||||
{
|
||||
}
|
||||
MessageHeader::MessageHeader() : p(std::make_unique<MessageHeaderImpl>()) {}
|
||||
MessageHeader::~MessageHeader() = default;
|
||||
|
||||
MessageHeader::MessageHeader(MessageHeader&&) noexcept = default;
|
||||
MessageHeader& MessageHeader::operator=(MessageHeader&&) = default;
|
||||
MessageHeader& MessageHeader::operator=(MessageHeader&&) noexcept = default;
|
||||
|
||||
uint16_t MessageHeader::message_size() const
|
||||
{
|
||||
|
|
@ -93,6 +90,11 @@ uint16_t MessageHeader::message_segment_number() const
|
|||
return p->messageSegmentNumber_;
|
||||
}
|
||||
|
||||
void MessageHeader::set_message_size(uint16_t messageSize)
|
||||
{
|
||||
p->messageSize_ = messageSize;
|
||||
}
|
||||
|
||||
bool MessageHeader::Parse(std::istream& is)
|
||||
{
|
||||
bool headerValid = true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue