Parse Clutter Filter Map (Message 15)

This commit is contained in:
Dan Paulat 2021-06-15 21:59:38 -05:00
parent f36d57b71d
commit 3ab4ec4b9b
15 changed files with 682 additions and 22 deletions

View file

@ -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

View 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

View 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

View 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

View file

@ -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;