Program Listing for File sbf_blocks.hpp
↰ Return to documentation for file (/tmp/ws/src/septentrio_gnss_driver/include/septentrio_gnss_driver/parsers/sbf_blocks.hpp
)
// *****************************************************************************
//
// © Copyright 2020, Septentrio NV/SA.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// *****************************************************************************
#pragma once
#include <cstdint>
static const uint8_t NR_OF_LOGICALCHANNELS = 80;
static const uint8_t MAX_NB_INMARSATCHANNELS = 1;
static const uint8_t MAX_NR_OF_SIGNALS_PER_SATELLITE = 7;
static const uint8_t NR_OF_ANTENNAS = 3;
static const uint8_t MAXSB_NBRANTENNA = 4;
static const uint8_t MAXSB_CHANNELSATINFO =
(NR_OF_LOGICALCHANNELS + MAX_NB_INMARSATCHANNELS);
static const uint16_t MAXSB_CHANNELSTATEINFO =
(MAXSB_CHANNELSATINFO * MAXSB_NBRANTENNA);
static const uint8_t MAXSB_MEASEPOCH_T1 =
(NR_OF_LOGICALCHANNELS + MAX_NB_INMARSATCHANNELS);
static const uint16_t MAXSB_MEASEPOCH_T2 =
((MAXSB_MEASEPOCH_T1) *
(((MAX_NR_OF_SIGNALS_PER_SATELLITE) * (NR_OF_ANTENNAS)) - 1));
static const uint8_t MAXSB_NBVECTORINFO = 30;
static const uint8_t SBF_SYNC_1 = 0x24;
static const uint8_t SBF_SYNC_2 = 0x40;
// C++
#include <algorithm>
// Boost
#include <boost/spirit/include/qi.hpp>
// ROSaic
#include <septentrio_gnss_driver/abstraction/typedefs.hpp>
#include <septentrio_gnss_driver/parsers/parsing_utilities.hpp>
struct BlockHeader
{
uint8_t sync_1;
uint8_t sync_2;
uint16_t crc;
uint16_t id;
uint8_t revision;
uint16_t length;
uint32_t tow;
uint16_t wnc;
};
struct ChannelStateInfo
{
uint8_t antenna;
uint16_t tracking_status;
uint16_t pvt_status;
uint16_t pvt_info;
};
struct ChannelSatInfo
{
uint8_t sv_id;
uint8_t freq_nr;
uint16_t az_rise_set;
uint16_t health_status;
int8_t elev;
uint8_t n2;
uint8_t rx_channel;
std::vector<ChannelStateInfo> stateInfo;
};
struct ChannelStatus
{
BlockHeader block_header;
uint8_t n;
uint8_t sb1_length;
uint8_t sb2_length;
std::vector<ChannelSatInfo> satInfo;
};
struct Dop
{
BlockHeader block_header;
uint8_t nr_sv;
double pdop;
double tdop;
double hdop;
double vdop;
float hpl;
float vpl;
};
struct ReceiverSetup
{
BlockHeader block_header;
std::string marker_name;
std::string marker_number;
std::string observer;
std::string agency;
std::string rx_serial_number;
std::string rx_name;
std::string rx_version;
std::string ant_serial_nbr;
std::string ant_type;
float delta_h; /* [m] */
float delta_e; /* [m] */
float delta_n; /* [m] */
std::string marker_type;
std::string gnss_fw_version;
std::string product_name;
double latitude;
double longitude;
float height;
std::string station_code;
uint8_t monument_idx;
uint8_t receiver_idx;
std::string country_code;
};
struct QualityInd
{
BlockHeader block_header;
uint8_t n = 0;
std::vector<uint16_t> indicators;
};
struct AgcState
{
uint8_t frontend_id;
int8_t gain;
uint8_t sample_var;
uint8_t blanking_stat;
};
struct ReceiverStatus
{
BlockHeader block_header;
uint8_t cpu_load;
uint8_t ext_error;
uint32_t up_time;
uint32_t rx_status;
uint32_t rx_error;
uint8_t n;
uint8_t sb_length;
uint8_t cmd_count;
uint8_t temperature;
std::vector<AgcState> agc_state;
};
static const std::array<uint16_t, 256> CRC_LOOK_UP = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129,
0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252,
0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c,
0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672,
0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738,
0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861,
0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc,
0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5,
0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b,
0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9,
0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3,
0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c,
0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3,
0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676,
0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16,
0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b,
0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36,
0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0};
namespace qi = boost::spirit::qi;
template <typename T>
[[nodiscard]] bool validValue(T s)
{
static_assert(std::is_same<uint16_t, T>::value ||
std::is_same<uint32_t, T>::value ||
std::is_same<float, T>::value || std::is_same<double, T>::value);
if (std::is_same<uint16_t, T>::value)
{
return (s != static_cast<uint16_t>(65535));
} else if (std::is_same<uint32_t, T>::value)
{
return (s != 4294967295u);
} else if (std::is_same<float, T>::value)
{
return (s != -2e10f);
} else if (std::is_same<double, T>::value)
{
return (s != -2e10);
}
}
template <typename Val>
void setDoNotUse(Val& s)
{
static_assert(
std::is_same<uint16_t, Val>::value || std::is_same<uint32_t, Val>::value ||
std::is_same<uint64_t, Val>::value || std::is_same<float, Val>::value ||
std::is_same<double, Val>::value);
if (std::is_same<uint16_t, Val>::value)
{
s = 65535;
} else if (std::is_same<uint32_t, Val>::value)
{
s = 4294967295ul;
} else if (std::is_same<float, Val>::value)
{
s = -2e10f;
} else if (std::is_same<double, Val>::value)
{
s = -2e10;
}
// TODO add more
}
template <typename It, typename Val>
void qiLittleEndianParser(It& it, Val& val)
{
static_assert(
std::is_same<int8_t, Val>::value || std::is_same<uint8_t, Val>::value ||
std::is_same<int16_t, Val>::value || std::is_same<uint16_t, Val>::value ||
std::is_same<int32_t, Val>::value || std::is_same<uint32_t, Val>::value ||
std::is_same<int64_t, Val>::value || std::is_same<uint64_t, Val>::value ||
std::is_same<float, Val>::value || std::is_same<double, Val>::value);
if (std::is_same<int8_t, Val>::value)
{
qi::parse(it, it + 1, qi::char_, val);
} else if (std::is_same<uint8_t, Val>::value)
{
qi::parse(it, it + 1, qi::byte_, val);
} else if ((std::is_same<int16_t, Val>::value) ||
(std::is_same<uint16_t, Val>::value))
{
qi::parse(it, it + 2, qi::little_word, val);
} else if ((std::is_same<int32_t, Val>::value) ||
(std::is_same<uint32_t, Val>::value))
{
qi::parse(it, it + 4, qi::little_dword, val);
} else if ((std::is_same<int64_t, Val>::value) ||
(std::is_same<uint64_t, Val>::value))
{
qi::parse(it, it + 8, qi::little_qword, val);
} else if (std::is_same<float, Val>::value)
{
qi::parse(it, it + 4, qi::little_bin_float, val);
} else if (std::is_same<double, Val>::value)
{
qi::parse(it, it + 8, qi::little_bin_double, val);
}
}
template <typename It>
void qiCharsToStringParser(It& it, std::string& val, std::size_t num)
{
val.clear();
qi::parse(it, it + num, qi::repeat(num)[qi::char_], val);
// remove string termination characters '\0'
val.erase(std::remove(val.begin(), val.end(), '\0'), val.end());
}
template <typename It, typename Hdr>
[[nodiscard]] bool BlockHeaderParser(ROSaicNodeBase* node, It& it, Hdr& block_header)
{
qiLittleEndianParser(it, block_header.sync_1);
if (block_header.sync_1 != SBF_SYNC_1)
{
node->log(log_level::ERROR, "Parse error: Wrong sync byte 1.");
return false;
}
qiLittleEndianParser(it, block_header.sync_2);
if (block_header.sync_2 != SBF_SYNC_2)
{
node->log(log_level::ERROR, "Parse error: Wrong sync byte 2.");
return false;
}
qiLittleEndianParser(it, block_header.crc);
uint16_t ID;
qiLittleEndianParser(it, ID);
block_header.id = ID & 8191; // lower 13 bits are id
block_header.revision = ID >> 13; // upper 3 bits are revision
qiLittleEndianParser(it, block_header.length);
qiLittleEndianParser(it, block_header.tow);
qiLittleEndianParser(it, block_header.wnc);
return true;
}
template <typename It>
void ChannelStateInfoParser(It& it, ChannelStateInfo& msg, uint8_t sb2_length)
{
qiLittleEndianParser(it, msg.antenna);
++it; // reserved
qiLittleEndianParser(it, msg.tracking_status);
qiLittleEndianParser(it, msg.pvt_status);
qiLittleEndianParser(it, msg.pvt_info);
std::advance(it, sb2_length - 8); // skip padding
};
template <typename It>
[[nodiscard]] bool ChannelSatInfoParser(ROSaicNodeBase* node, It& it,
ChannelSatInfo& msg, uint8_t sb1_length,
uint8_t sb2_length)
{
qiLittleEndianParser(it, msg.sv_id);
qiLittleEndianParser(it, msg.freq_nr);
std::advance(it, 2); // reserved
qiLittleEndianParser(it, msg.az_rise_set);
qiLittleEndianParser(it, msg.health_status);
qiLittleEndianParser(it, msg.elev);
qiLittleEndianParser(it, msg.n2);
if (msg.n2 > MAXSB_CHANNELSTATEINFO)
{
node->log(log_level::ERROR, "Parse error: Too many ChannelStateInfo " +
std::to_string(msg.n2));
return false;
}
qiLittleEndianParser(it, msg.rx_channel);
++it; // reserved
std::advance(it, sb1_length - 12); // skip padding
msg.stateInfo.resize(msg.n2);
for (auto& stateInfo : msg.stateInfo)
{
ChannelStateInfoParser(it, stateInfo, sb2_length);
}
return true;
};
template <typename It>
[[nodiscard]] bool ChannelStatusParser(ROSaicNodeBase* node, It it, It itEnd,
ChannelStatus& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4013)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.n);
if (msg.n > MAXSB_CHANNELSATINFO)
{
node->log(log_level::ERROR,
"Parse error: Too many ChannelSatInfo " + std::to_string(msg.n));
return false;
}
qiLittleEndianParser(it, msg.sb1_length);
qiLittleEndianParser(it, msg.sb2_length);
std::advance(it, 3); // reserved
msg.satInfo.resize(msg.n);
for (auto& satInfo : msg.satInfo)
{
if (!ChannelSatInfoParser(node, it, satInfo, msg.sb1_length, msg.sb2_length))
return false;
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool DOPParser(ROSaicNodeBase* node, It it, It itEnd, Dop& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4001)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.nr_sv);
++it; // reserved
uint16_t temp;
qiLittleEndianParser(it, temp);
msg.pdop = temp / 100.0;
qiLittleEndianParser(it, temp);
msg.tdop = temp / 100.0;
qiLittleEndianParser(it, temp);
msg.hdop = temp / 100.0;
qiLittleEndianParser(it, temp);
msg.vdop = temp / 100.0;
qiLittleEndianParser(it, msg.hpl);
qiLittleEndianParser(it, msg.vpl);
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
void MeasEpochChannelType2Parser(It& it, MeasEpochChannelType2Msg& msg,
uint8_t sb2_length)
{
qiLittleEndianParser(it, msg.type);
qiLittleEndianParser(it, msg.lock_time);
qiLittleEndianParser(it, msg.cn0);
qiLittleEndianParser(it, msg.offsets_msb);
qiLittleEndianParser(it, msg.carrier_msb);
qiLittleEndianParser(it, msg.obs_info);
qiLittleEndianParser(it, msg.code_offset_lsb);
qiLittleEndianParser(it, msg.carrier_lsb);
qiLittleEndianParser(it, msg.doppler_offset_lsb);
std::advance(it, sb2_length - 12); // skip padding
};
template <typename It>
[[nodiscard]] bool MeasEpochChannelType1Parser(ROSaicNodeBase* node, It& it,
MeasEpochChannelType1Msg& msg,
uint8_t sb1_length,
uint8_t sb2_length)
{
qiLittleEndianParser(it, msg.rx_channel);
qiLittleEndianParser(it, msg.type);
qiLittleEndianParser(it, msg.sv_id);
qiLittleEndianParser(it, msg.misc);
qiLittleEndianParser(it, msg.code_lsb);
qiLittleEndianParser(it, msg.doppler);
qiLittleEndianParser(it, msg.carrier_lsb);
qiLittleEndianParser(it, msg.carrier_msb);
qiLittleEndianParser(it, msg.cn0);
qiLittleEndianParser(it, msg.lock_time);
qiLittleEndianParser(it, msg.obs_info);
qiLittleEndianParser(it, msg.n2);
std::advance(it, sb1_length - 20); // skip padding
if (msg.n2 > MAXSB_MEASEPOCH_T2)
{
node->log(log_level::ERROR, "Parse error: Too many MeasEpochChannelType2 " +
std::to_string(msg.n2));
return false;
}
msg.type2.resize(msg.n2);
for (auto& type2 : msg.type2)
{
MeasEpochChannelType2Parser(it, type2, sb2_length);
}
return true;
};
template <typename It>
[[nodiscard]] bool MeasEpochParser(ROSaicNodeBase* node, It it, It itEnd,
MeasEpochMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4027)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.n);
if (msg.n > MAXSB_MEASEPOCH_T1)
{
node->log(log_level::ERROR, "Parse error: Too many MeasEpochChannelType1 " +
std::to_string(msg.n));
return false;
}
qiLittleEndianParser(it, msg.sb1_length);
qiLittleEndianParser(it, msg.sb2_length);
qiLittleEndianParser(it, msg.common_flags);
if (msg.block_header.revision > 0)
qiLittleEndianParser(it, msg.cum_clk_jumps);
++it; // reserved
msg.type1.resize(msg.n);
for (auto& type1 : msg.type1)
{
if (!MeasEpochChannelType1Parser(node, it, type1, msg.sb1_length,
msg.sb2_length))
return false;
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool GalAuthStatusParser(ROSaicNodeBase* node, It it, It itEnd,
GalAuthStatusMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4245)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.osnma_status);
qiLittleEndianParser(it, msg.trusted_time_delta);
qiLittleEndianParser(it, msg.gal_active_mask);
qiLittleEndianParser(it, msg.gal_authentic_mask);
qiLittleEndianParser(it, msg.gps_active_mask);
qiLittleEndianParser(it, msg.gps_authentic_mask);
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
void RfBandParser(It& it, RfBandMsg& msg, uint8_t sb_length)
{
qiLittleEndianParser(it, msg.frequency);
qiLittleEndianParser(it, msg.bandwidth);
qiLittleEndianParser(it, msg.info);
std::advance(it, sb_length - 7); // skip padding
};
template <typename It>
[[nodiscard]] bool RfStatusParser(ROSaicNodeBase* node, It it, It itEnd,
RfStatusMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4092)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.n);
qiLittleEndianParser(it, msg.sb_length);
qiLittleEndianParser(it, msg.flags);
std::advance(it, 3); // reserved
msg.rfband.resize(msg.n);
for (auto& rfband : msg.rfband)
{
RfBandParser(it, rfband, msg.sb_length);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool ReceiverSetupParser(ROSaicNodeBase* node, It it, It itEnd,
ReceiverSetup& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 5902)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
std::advance(it, 2); // reserved
qiCharsToStringParser(it, msg.marker_name, 60);
qiCharsToStringParser(it, msg.marker_number, 20);
qiCharsToStringParser(it, msg.observer, 20);
qiCharsToStringParser(it, msg.agency, 40);
qiCharsToStringParser(it, msg.rx_serial_number, 20);
qiCharsToStringParser(it, msg.rx_name, 20);
qiCharsToStringParser(it, msg.rx_version, 20);
qiCharsToStringParser(it, msg.ant_serial_nbr, 20);
qiCharsToStringParser(it, msg.ant_type, 20);
qiLittleEndianParser(it, msg.delta_h);
qiLittleEndianParser(it, msg.delta_e);
qiLittleEndianParser(it, msg.delta_n);
if (msg.block_header.revision > 0)
qiCharsToStringParser(it, msg.marker_type, 20);
if (msg.block_header.revision > 1)
qiCharsToStringParser(it, msg.gnss_fw_version, 40);
if (msg.block_header.revision > 2)
qiCharsToStringParser(it, msg.product_name, 40);
if (msg.block_header.revision > 3)
{
qiLittleEndianParser(it, msg.latitude);
qiLittleEndianParser(it, msg.longitude);
qiLittleEndianParser(it, msg.height);
qiCharsToStringParser(it, msg.station_code, 10);
qiLittleEndianParser(it, msg.monument_idx);
qiLittleEndianParser(it, msg.receiver_idx);
qiCharsToStringParser(it, msg.country_code, 3);
} else
{
setDoNotUse(msg.latitude);
setDoNotUse(msg.longitude);
setDoNotUse(msg.height);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool ReceiverTimesParser(ROSaicNodeBase* node, It it, It itEnd,
ReceiverTimeMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 5914)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.utc_year);
qiLittleEndianParser(it, msg.utc_month);
qiLittleEndianParser(it, msg.utc_day);
qiLittleEndianParser(it, msg.utc_hour);
qiLittleEndianParser(it, msg.utc_min);
qiLittleEndianParser(it, msg.utc_second);
qiLittleEndianParser(it, msg.delta_ls);
qiLittleEndianParser(it, msg.sync_level);
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool PVTCartesianParser(ROSaicNodeBase* node, It it, It itEnd,
PVTCartesianMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4006)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.mode);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.x);
qiLittleEndianParser(it, msg.y);
qiLittleEndianParser(it, msg.z);
qiLittleEndianParser(it, msg.undulation);
qiLittleEndianParser(it, msg.vx);
qiLittleEndianParser(it, msg.vy);
qiLittleEndianParser(it, msg.vz);
qiLittleEndianParser(it, msg.cog);
qiLittleEndianParser(it, msg.rx_clk_bias);
qiLittleEndianParser(it, msg.rx_clk_drift);
qiLittleEndianParser(it, msg.time_system);
qiLittleEndianParser(it, msg.datum);
qiLittleEndianParser(it, msg.nr_sv);
qiLittleEndianParser(it, msg.wa_corr_info);
qiLittleEndianParser(it, msg.reference_id);
qiLittleEndianParser(it, msg.mean_corr_age);
qiLittleEndianParser(it, msg.signal_info);
qiLittleEndianParser(it, msg.alert_flag);
if (msg.block_header.revision > 0)
{
qiLittleEndianParser(it, msg.nr_bases);
qiLittleEndianParser(it, msg.ppp_info);
}
if (msg.block_header.revision > 1)
{
qiLittleEndianParser(it, msg.latency);
qiLittleEndianParser(it, msg.h_accuracy);
qiLittleEndianParser(it, msg.v_accuracy);
qiLittleEndianParser(it, msg.misc);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
}
template <typename It>
[[nodiscard]] bool PVTGeodeticParser(ROSaicNodeBase* node, It it, It itEnd,
PVTGeodeticMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4007)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.mode);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.latitude);
qiLittleEndianParser(it, msg.longitude);
qiLittleEndianParser(it, msg.height);
qiLittleEndianParser(it, msg.undulation);
qiLittleEndianParser(it, msg.vn);
qiLittleEndianParser(it, msg.ve);
qiLittleEndianParser(it, msg.vu);
qiLittleEndianParser(it, msg.cog);
qiLittleEndianParser(it, msg.rx_clk_bias);
qiLittleEndianParser(it, msg.rx_clk_drift);
qiLittleEndianParser(it, msg.time_system);
qiLittleEndianParser(it, msg.datum);
qiLittleEndianParser(it, msg.nr_sv);
qiLittleEndianParser(it, msg.wa_corr_info);
qiLittleEndianParser(it, msg.reference_id);
qiLittleEndianParser(it, msg.mean_corr_age);
qiLittleEndianParser(it, msg.signal_info);
qiLittleEndianParser(it, msg.alert_flag);
if (msg.block_header.revision > 0)
{
qiLittleEndianParser(it, msg.nr_bases);
qiLittleEndianParser(it, msg.ppp_info);
}
if (msg.block_header.revision > 1)
{
qiLittleEndianParser(it, msg.latency);
qiLittleEndianParser(it, msg.h_accuracy);
qiLittleEndianParser(it, msg.v_accuracy);
qiLittleEndianParser(it, msg.misc);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
}
template <typename It>
[[nodiscard]] bool AttEulerParser(ROSaicNodeBase* node, It it, It itEnd,
AttEulerMsg& msg, bool use_ros_axis_orientation)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 5938)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.nr_sv);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.mode);
std::advance(it, 2); // reserved
qiLittleEndianParser(it, msg.heading);
qiLittleEndianParser(it, msg.pitch);
qiLittleEndianParser(it, msg.roll);
qiLittleEndianParser(it, msg.pitch_dot);
qiLittleEndianParser(it, msg.roll_dot);
qiLittleEndianParser(it, msg.heading_dot);
if (use_ros_axis_orientation)
{
if (validValue(msg.heading))
msg.heading = -msg.heading + 90;
if (validValue(msg.pitch))
msg.pitch = -msg.pitch;
if (validValue(msg.pitch_dot))
msg.pitch_dot = -msg.pitch_dot;
if (validValue(msg.heading_dot))
msg.heading_dot = -msg.heading_dot;
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool AttCovEulerParser(ROSaicNodeBase* node, It it, It itEnd,
AttCovEulerMsg& msg,
bool use_ros_axis_orientation)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 5939)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
++it; // reserved
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.cov_headhead);
qiLittleEndianParser(it, msg.cov_pitchpitch);
qiLittleEndianParser(it, msg.cov_rollroll);
qiLittleEndianParser(it, msg.cov_headpitch);
qiLittleEndianParser(it, msg.cov_headroll);
qiLittleEndianParser(it, msg.cov_pitchroll);
if (use_ros_axis_orientation)
{
if (validValue(msg.cov_headroll))
msg.cov_headroll = -msg.cov_headroll;
if (validValue(msg.cov_pitchroll))
msg.cov_pitchroll = -msg.cov_pitchroll;
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
void VectorInfoCartParser(It& it, VectorInfoCartMsg& msg, uint8_t sb_length)
{
qiLittleEndianParser(it, msg.nr_sv);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.mode);
qiLittleEndianParser(it, msg.misc);
qiLittleEndianParser(it, msg.delta_x);
qiLittleEndianParser(it, msg.delta_y);
qiLittleEndianParser(it, msg.delta_z);
qiLittleEndianParser(it, msg.delta_vx);
qiLittleEndianParser(it, msg.delta_vy);
qiLittleEndianParser(it, msg.delta_vz);
qiLittleEndianParser(it, msg.azimuth);
qiLittleEndianParser(it, msg.elevation);
qiLittleEndianParser(it, msg.reference_id);
qiLittleEndianParser(it, msg.corr_age);
qiLittleEndianParser(it, msg.signal_info);
std::advance(it, sb_length - 52); // skip padding
};
template <typename It>
[[nodiscard]] bool BaseVectorCartParser(ROSaicNodeBase* node, It it, It itEnd,
BaseVectorCartMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4043)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.n);
if (msg.n > MAXSB_NBVECTORINFO)
{
node->log(log_level::ERROR,
"Parse error: Too many VectorInfoCart " + std::to_string(msg.n));
return false;
}
qiLittleEndianParser(it, msg.sb_length);
msg.vector_info_cart.resize(msg.n);
for (auto& vector_info_cart : msg.vector_info_cart)
{
VectorInfoCartParser(it, vector_info_cart, msg.sb_length);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
void VectorInfoGeodParser(It& it, VectorInfoGeodMsg& msg, uint8_t sb_length)
{
qiLittleEndianParser(it, msg.nr_sv);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.mode);
qiLittleEndianParser(it, msg.misc);
qiLittleEndianParser(it, msg.delta_east);
qiLittleEndianParser(it, msg.delta_north);
qiLittleEndianParser(it, msg.delta_up);
qiLittleEndianParser(it, msg.delta_ve);
qiLittleEndianParser(it, msg.delta_vn);
qiLittleEndianParser(it, msg.delta_vu);
qiLittleEndianParser(it, msg.azimuth);
qiLittleEndianParser(it, msg.elevation);
qiLittleEndianParser(it, msg.reference_id);
qiLittleEndianParser(it, msg.corr_age);
qiLittleEndianParser(it, msg.signal_info);
std::advance(it, sb_length - 52); // skip padding
};
template <typename It>
[[nodiscard]] bool BaseVectorGeodParser(ROSaicNodeBase* node, It it, It itEnd,
BaseVectorGeodMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4028)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.n);
if (msg.n > MAXSB_NBVECTORINFO)
{
node->log(log_level::ERROR,
"Parse error: Too many VectorInfoGeod " + std::to_string(msg.n));
return false;
}
qiLittleEndianParser(it, msg.sb_length);
msg.vector_info_geod.resize(msg.n);
for (auto& vector_info_geod : msg.vector_info_geod)
{
VectorInfoGeodParser(it, vector_info_geod, msg.sb_length);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool INSNavCartParser(ROSaicNodeBase* node, It it, It itEnd,
INSNavCartMsg& msg,
bool use_ros_axis_orientation)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if ((msg.block_header.id != 4225) && (msg.block_header.id != 4229))
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.gnss_mode);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.info);
qiLittleEndianParser(it, msg.gnss_age);
qiLittleEndianParser(it, msg.x);
qiLittleEndianParser(it, msg.y);
qiLittleEndianParser(it, msg.z);
qiLittleEndianParser(it, msg.accuracy);
qiLittleEndianParser(it, msg.latency);
qiLittleEndianParser(it, msg.datum);
++it; // reserved
qiLittleEndianParser(it, msg.sb_list);
if ((msg.sb_list & 1) != 0)
{
qiLittleEndianParser(it, msg.x_std_dev);
qiLittleEndianParser(it, msg.y_std_dev);
qiLittleEndianParser(it, msg.z_std_dev);
} else
{
setDoNotUse(msg.x_std_dev);
setDoNotUse(msg.y_std_dev);
setDoNotUse(msg.z_std_dev);
}
if ((msg.sb_list & 2) != 0)
{
qiLittleEndianParser(it, msg.heading);
qiLittleEndianParser(it, msg.pitch);
qiLittleEndianParser(it, msg.roll);
if (use_ros_axis_orientation)
{
if (validValue(msg.heading))
msg.heading = -msg.heading + 90;
if (validValue(msg.pitch))
msg.pitch = -msg.pitch;
}
} else
{
setDoNotUse(msg.heading);
setDoNotUse(msg.pitch);
setDoNotUse(msg.roll);
}
if ((msg.sb_list & 4) != 0)
{
qiLittleEndianParser(it, msg.heading_std_dev);
qiLittleEndianParser(it, msg.pitch_std_dev);
qiLittleEndianParser(it, msg.roll_std_dev);
} else
{
setDoNotUse(msg.heading_std_dev);
setDoNotUse(msg.pitch_std_dev);
setDoNotUse(msg.roll_std_dev);
}
if ((msg.sb_list & 8) != 0)
{
qiLittleEndianParser(it, msg.vx);
qiLittleEndianParser(it, msg.vy);
qiLittleEndianParser(it, msg.vz);
} else
{
setDoNotUse(msg.vx);
setDoNotUse(msg.vy);
setDoNotUse(msg.vz);
}
if ((msg.sb_list & 16) != 0)
{
qiLittleEndianParser(it, msg.vx_std_dev);
qiLittleEndianParser(it, msg.vy_std_dev);
qiLittleEndianParser(it, msg.vz_std_dev);
} else
{
setDoNotUse(msg.vx_std_dev);
setDoNotUse(msg.vy_std_dev);
setDoNotUse(msg.vz_std_dev);
}
if ((msg.sb_list & 32) != 0)
{
qiLittleEndianParser(it, msg.xy_cov);
qiLittleEndianParser(it, msg.xz_cov);
qiLittleEndianParser(it, msg.yz_cov);
} else
{
setDoNotUse(msg.xy_cov);
setDoNotUse(msg.xz_cov);
setDoNotUse(msg.yz_cov);
}
if ((msg.sb_list & 64) != 0)
{
qiLittleEndianParser(it, msg.heading_pitch_cov);
qiLittleEndianParser(it, msg.heading_roll_cov);
qiLittleEndianParser(it, msg.pitch_roll_cov);
if (use_ros_axis_orientation)
{
if (validValue(msg.heading_roll_cov))
msg.heading_roll_cov = -msg.heading_roll_cov;
if (validValue(msg.pitch_roll_cov))
msg.pitch_roll_cov = -msg.pitch_roll_cov;
}
} else
{
setDoNotUse(msg.heading_pitch_cov);
setDoNotUse(msg.heading_roll_cov);
setDoNotUse(msg.pitch_roll_cov);
}
if ((msg.sb_list & 128) != 0)
{
qiLittleEndianParser(it, msg.vx_vy_cov);
qiLittleEndianParser(it, msg.vx_vz_cov);
qiLittleEndianParser(it, msg.vy_vz_cov);
} else
{
setDoNotUse(msg.vx_vy_cov);
setDoNotUse(msg.vx_vz_cov);
setDoNotUse(msg.vy_vz_cov);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool PosCovCartesianParser(ROSaicNodeBase* node, It it, It itEnd,
PosCovCartesianMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 5905)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.mode);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.cov_xx);
qiLittleEndianParser(it, msg.cov_yy);
qiLittleEndianParser(it, msg.cov_zz);
qiLittleEndianParser(it, msg.cov_bb);
qiLittleEndianParser(it, msg.cov_xy);
qiLittleEndianParser(it, msg.cov_xz);
qiLittleEndianParser(it, msg.cov_xb);
qiLittleEndianParser(it, msg.cov_yz);
qiLittleEndianParser(it, msg.cov_yb);
qiLittleEndianParser(it, msg.cov_zb);
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool PosCovGeodeticParser(ROSaicNodeBase* node, It it, It itEnd,
PosCovGeodeticMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 5906)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.mode);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.cov_latlat);
qiLittleEndianParser(it, msg.cov_lonlon);
qiLittleEndianParser(it, msg.cov_hgthgt);
qiLittleEndianParser(it, msg.cov_bb);
qiLittleEndianParser(it, msg.cov_latlon);
qiLittleEndianParser(it, msg.cov_lathgt);
qiLittleEndianParser(it, msg.cov_latb);
qiLittleEndianParser(it, msg.cov_lonhgt);
qiLittleEndianParser(it, msg.cov_lonb);
qiLittleEndianParser(it, msg.cov_hb);
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool VelCovCartesianParser(ROSaicNodeBase* node, It it, It itEnd,
VelCovCartesianMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 5907)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.mode);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.cov_vxvx);
qiLittleEndianParser(it, msg.cov_vyvy);
qiLittleEndianParser(it, msg.cov_vzvz);
qiLittleEndianParser(it, msg.cov_dtdt);
qiLittleEndianParser(it, msg.cov_vxvy);
qiLittleEndianParser(it, msg.cov_vxvz);
qiLittleEndianParser(it, msg.cov_vxdt);
qiLittleEndianParser(it, msg.cov_vyvz);
qiLittleEndianParser(it, msg.cov_vydt);
qiLittleEndianParser(it, msg.cov_vzdt);
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool VelCovGeodeticParser(ROSaicNodeBase* node, It it, It itEnd,
VelCovGeodeticMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 5908)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.mode);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.cov_vnvn);
qiLittleEndianParser(it, msg.cov_veve);
qiLittleEndianParser(it, msg.cov_vuvu);
qiLittleEndianParser(it, msg.cov_dtdt);
qiLittleEndianParser(it, msg.cov_vnve);
qiLittleEndianParser(it, msg.cov_vnvu);
qiLittleEndianParser(it, msg.cov_vndt);
qiLittleEndianParser(it, msg.cov_vevu);
qiLittleEndianParser(it, msg.cov_vedt);
qiLittleEndianParser(it, msg.cov_vudt);
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool QualityIndParser(ROSaicNodeBase* node, It it, It itEnd,
QualityInd& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4082)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.n);
if (msg.n > 40)
{
node->log(log_level::ERROR,
"Parse error: Too many indicators " + std::to_string(msg.n));
return false;
}
++it; // reserved
msg.indicators.resize(msg.n);
std::vector<uint16_t> indicators;
for (auto& indicators : msg.indicators)
{
qiLittleEndianParser(it, indicators);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
void AgcStateParser(It it, AgcState& msg, uint8_t sb_length)
{
qiLittleEndianParser(it, msg.frontend_id);
qiLittleEndianParser(it, msg.gain);
qiLittleEndianParser(it, msg.sample_var);
qiLittleEndianParser(it, msg.blanking_stat);
std::advance(it, sb_length - 4); // skip padding
};
template <typename It>
[[nodiscard]] bool ReceiverStatusParser(ROSaicNodeBase* node, It it, It itEnd,
ReceiverStatus& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4014)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.cpu_load);
qiLittleEndianParser(it, msg.ext_error);
qiLittleEndianParser(it, msg.up_time);
qiLittleEndianParser(it, msg.rx_status);
qiLittleEndianParser(it, msg.rx_error);
qiLittleEndianParser(it, msg.n);
if (msg.n > 18)
{
node->log(log_level::ERROR,
"Parse error: Too many AGCState " + std::to_string(msg.n));
return false;
}
qiLittleEndianParser(it, msg.sb_length);
qiLittleEndianParser(it, msg.cmd_count);
qiLittleEndianParser(it, msg.temperature);
msg.agc_state.resize(msg.n);
for (auto& agc_state : msg.agc_state)
{
AgcStateParser(it, agc_state, msg.sb_length);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool ReceiverTimeParser(ROSaicNodeBase* node, It it, It itEnd,
ReceiverTimeMsg& msg)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 5914)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.utc_year);
qiLittleEndianParser(it, msg.utc_month);
qiLittleEndianParser(it, msg.utc_day);
qiLittleEndianParser(it, msg.utc_hour);
qiLittleEndianParser(it, msg.utc_min);
qiLittleEndianParser(it, msg.utc_second);
qiLittleEndianParser(it, msg.delta_ls);
qiLittleEndianParser(it, msg.sync_level);
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool INSNavGeodParser(ROSaicNodeBase* node, It it, It itEnd,
INSNavGeodMsg& msg,
bool use_ros_axis_orientation)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if ((msg.block_header.id != 4226) && (msg.block_header.id != 4230))
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.gnss_mode);
qiLittleEndianParser(it, msg.error);
qiLittleEndianParser(it, msg.info);
qiLittleEndianParser(it, msg.gnss_age);
qiLittleEndianParser(it, msg.latitude);
qiLittleEndianParser(it, msg.longitude);
qiLittleEndianParser(it, msg.height);
qiLittleEndianParser(it, msg.undulation);
qiLittleEndianParser(it, msg.accuracy);
qiLittleEndianParser(it, msg.latency);
qiLittleEndianParser(it, msg.datum);
++it; // reserved
qiLittleEndianParser(it, msg.sb_list);
if ((msg.sb_list & 1) != 0)
{
qiLittleEndianParser(it, msg.latitude_std_dev);
qiLittleEndianParser(it, msg.longitude_std_dev);
qiLittleEndianParser(it, msg.height_std_dev);
} else
{
setDoNotUse(msg.latitude_std_dev);
setDoNotUse(msg.longitude_std_dev);
setDoNotUse(msg.height_std_dev);
}
if ((msg.sb_list & 2) != 0)
{
qiLittleEndianParser(it, msg.heading);
qiLittleEndianParser(it, msg.pitch);
qiLittleEndianParser(it, msg.roll);
if (use_ros_axis_orientation)
{
if (validValue(msg.heading))
msg.heading = -msg.heading + 90;
if (validValue(msg.pitch))
msg.pitch = -msg.pitch;
}
} else
{
setDoNotUse(msg.heading);
setDoNotUse(msg.pitch);
setDoNotUse(msg.roll);
}
if ((msg.sb_list & 4) != 0)
{
qiLittleEndianParser(it, msg.heading_std_dev);
qiLittleEndianParser(it, msg.pitch_std_dev);
qiLittleEndianParser(it, msg.roll_std_dev);
} else
{
setDoNotUse(msg.heading_std_dev);
setDoNotUse(msg.pitch_std_dev);
setDoNotUse(msg.roll_std_dev);
}
if ((msg.sb_list & 8) != 0)
{
qiLittleEndianParser(it, msg.ve);
qiLittleEndianParser(it, msg.vn);
qiLittleEndianParser(it, msg.vu);
} else
{
setDoNotUse(msg.ve);
setDoNotUse(msg.vn);
setDoNotUse(msg.vu);
}
if ((msg.sb_list & 16) != 0)
{
qiLittleEndianParser(it, msg.ve_std_dev);
qiLittleEndianParser(it, msg.vn_std_dev);
qiLittleEndianParser(it, msg.vu_std_dev);
} else
{
setDoNotUse(msg.ve_std_dev);
setDoNotUse(msg.vn_std_dev);
setDoNotUse(msg.vu_std_dev);
}
if ((msg.sb_list & 32) != 0)
{
qiLittleEndianParser(it, msg.latitude_longitude_cov);
qiLittleEndianParser(it, msg.latitude_height_cov);
qiLittleEndianParser(it, msg.longitude_height_cov);
} else
{
setDoNotUse(msg.latitude_longitude_cov);
setDoNotUse(msg.latitude_height_cov);
setDoNotUse(msg.longitude_height_cov);
}
if ((msg.sb_list & 64) != 0)
{
qiLittleEndianParser(it, msg.heading_pitch_cov);
qiLittleEndianParser(it, msg.heading_roll_cov);
qiLittleEndianParser(it, msg.pitch_roll_cov);
if (use_ros_axis_orientation)
{
if (validValue(msg.heading_roll_cov))
msg.heading_roll_cov = -msg.heading_roll_cov;
if (validValue(msg.pitch_roll_cov))
msg.pitch_roll_cov = -msg.pitch_roll_cov;
}
} else
{
setDoNotUse(msg.heading_pitch_cov);
setDoNotUse(msg.heading_roll_cov);
setDoNotUse(msg.pitch_roll_cov);
}
if ((msg.sb_list & 128) != 0)
{
qiLittleEndianParser(it, msg.ve_vn_cov);
qiLittleEndianParser(it, msg.ve_vu_cov);
qiLittleEndianParser(it, msg.vn_vu_cov);
} else
{
setDoNotUse(msg.ve_vn_cov);
setDoNotUse(msg.ve_vu_cov);
setDoNotUse(msg.vn_vu_cov);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool IMUSetupParser(ROSaicNodeBase* node, It it, It itEnd,
IMUSetupMsg& msg, bool use_ros_axis_orientation)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4224)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
++it; // reserved
qiLittleEndianParser(it, msg.serial_port);
qiLittleEndianParser(it, msg.ant_lever_arm_x);
qiLittleEndianParser(it, msg.ant_lever_arm_y);
qiLittleEndianParser(it, msg.ant_lever_arm_z);
qiLittleEndianParser(it, msg.theta_x);
qiLittleEndianParser(it, msg.theta_y);
qiLittleEndianParser(it, msg.theta_z);
if (use_ros_axis_orientation)
{
msg.ant_lever_arm_y = -msg.ant_lever_arm_y;
msg.ant_lever_arm_z = -msg.ant_lever_arm_z;
msg.theta_x = parsing_utilities::wrapAngle180to180(msg.theta_x - 180.0f);
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool VelSensorSetupParser(ROSaicNodeBase* node, It it, It itEnd,
VelSensorSetupMsg& msg,
bool use_ros_axis_orientation)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4244)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
++it; // reserved
qiLittleEndianParser(it, msg.port);
qiLittleEndianParser(it, msg.lever_arm_x);
qiLittleEndianParser(it, msg.lever_arm_y);
qiLittleEndianParser(it, msg.lever_arm_z);
if (use_ros_axis_orientation)
{
msg.lever_arm_y = -msg.lever_arm_y;
msg.lever_arm_z = -msg.lever_arm_z;
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
return true;
};
template <typename It>
[[nodiscard]] bool
ExtSensorMeasParser(ROSaicNodeBase* node, It it, It itEnd, ExtSensorMeasMsg& msg,
bool use_ros_axis_orientation, bool& hasImuMeas)
{
if (!BlockHeaderParser(node, it, msg.block_header))
return false;
if (msg.block_header.id != 4050)
{
node->log(log_level::ERROR, "Parse error: Wrong header ID " +
std::to_string(msg.block_header.id));
return false;
}
qiLittleEndianParser(it, msg.n);
qiLittleEndianParser(it, msg.sb_length);
if (msg.sb_length != 28)
{
node->log(log_level::ERROR,
"Parse error: Wrong sb_length " + std::to_string(msg.sb_length));
return false;
}
msg.acceleration_x = std::numeric_limits<double>::quiet_NaN();
msg.acceleration_y = std::numeric_limits<double>::quiet_NaN();
msg.acceleration_z = std::numeric_limits<double>::quiet_NaN();
msg.angular_rate_x = std::numeric_limits<double>::quiet_NaN();
msg.angular_rate_y = std::numeric_limits<double>::quiet_NaN();
msg.angular_rate_z = std::numeric_limits<double>::quiet_NaN();
msg.velocity_x = std::numeric_limits<double>::quiet_NaN();
msg.velocity_y = std::numeric_limits<double>::quiet_NaN();
msg.velocity_z = std::numeric_limits<double>::quiet_NaN();
msg.std_dev_x = std::numeric_limits<double>::quiet_NaN();
msg.std_dev_y = std::numeric_limits<double>::quiet_NaN();
msg.std_dev_z = std::numeric_limits<double>::quiet_NaN();
msg.sensor_temperature = std::numeric_limits<float>::quiet_NaN();
msg.zero_velocity_flag = std::numeric_limits<double>::quiet_NaN();
msg.source.resize(msg.n);
msg.sensor_model.resize(msg.n);
msg.type.resize(msg.n);
msg.obs_info.resize(msg.n);
bool hasAcc = false;
bool hasOmega = false;
hasImuMeas = false;
for (size_t i = 0; i < msg.n; i++)
{
qiLittleEndianParser(it, msg.source[i]);
qiLittleEndianParser(it, msg.sensor_model[i]);
qiLittleEndianParser(it, msg.type[i]);
qiLittleEndianParser(it, msg.obs_info[i]);
switch (msg.type[i])
{
case 0:
{
qiLittleEndianParser(it, msg.acceleration_x);
qiLittleEndianParser(it, msg.acceleration_y);
qiLittleEndianParser(it, msg.acceleration_z);
hasAcc = true;
break;
}
case 1:
{
qiLittleEndianParser(it, msg.angular_rate_x);
qiLittleEndianParser(it, msg.angular_rate_y);
qiLittleEndianParser(it, msg.angular_rate_z);
hasOmega = true;
break;
}
case 3:
{
int16_t temp;
qiLittleEndianParser(it, temp);
if (temp != -32768)
msg.sensor_temperature = temp / 100.0f;
else
msg.sensor_temperature = std::numeric_limits<float>::quiet_NaN();
std::advance(it, 22); // reserved
break;
}
case 4:
{
qiLittleEndianParser(it, msg.velocity_x);
qiLittleEndianParser(it, msg.velocity_y);
qiLittleEndianParser(it, msg.velocity_z);
qiLittleEndianParser(it, msg.std_dev_x);
qiLittleEndianParser(it, msg.std_dev_y);
qiLittleEndianParser(it, msg.std_dev_z);
if (use_ros_axis_orientation)
{
if (validValue(msg.velocity_y))
msg.velocity_y = -msg.velocity_y;
if (validValue(msg.velocity_z))
msg.velocity_z = -msg.velocity_z;
}
break;
}
case 20:
{
qiLittleEndianParser(it, msg.zero_velocity_flag);
std::advance(it, 16); // reserved
break;
}
default:
{
node->log(
log_level::DEBUG,
"Unknown external sensor measurement type in SBF ExtSensorMeas: " +
std::to_string(msg.type[i]));
std::advance(it, 24);
break;
}
}
}
if (it > itEnd)
{
node->log(log_level::ERROR, "Parse error: iterator past end.");
return false;
}
hasImuMeas = hasAcc && hasOmega;
return true;
};