2020-11-08 13:10:43 +00:00
|
|
|
/*!
|
|
|
|
* \file galileo_cnav_message.cc
|
|
|
|
* \brief Implementation of a Galileo CNAV Data message as described in
|
|
|
|
* Galileo High Accuracy Service E6-B Signal-In-Space Message Specification v1.2
|
|
|
|
* (April 2020)
|
2021-03-28 11:07:50 +00:00
|
|
|
* \author Carles Fernandez-Prades, 2020-2021 cfernandez(at)cttc.es
|
2020-11-08 13:10:43 +00:00
|
|
|
*
|
|
|
|
* -----------------------------------------------------------------------------
|
|
|
|
*
|
2020-12-30 12:35:06 +00:00
|
|
|
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
2020-11-08 13:10:43 +00:00
|
|
|
* This file is part of GNSS-SDR.
|
|
|
|
*
|
2021-03-28 11:07:50 +00:00
|
|
|
* Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors)
|
2020-11-08 13:10:43 +00:00
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
*
|
|
|
|
* -----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "galileo_cnav_message.h"
|
|
|
|
#include <boost/crc.hpp> // for boost::crc_basic, boost::crc_optimal
|
|
|
|
#include <boost/dynamic_bitset.hpp> // for boost::dynamic_bitset
|
2021-06-02 14:30:26 +00:00
|
|
|
#include <glog/logging.h>
|
|
|
|
#include <algorithm> // for reverse
|
|
|
|
#include <vector>
|
2020-11-08 13:10:43 +00:00
|
|
|
|
|
|
|
using CRC_Galileo_CNAV_type = boost::crc_optimal<24, 0x1864CFBU, 0x0, 0x0, false, false>;
|
|
|
|
|
|
|
|
|
2021-04-11 12:47:36 +00:00
|
|
|
bool Galileo_Cnav_Message::CRC_test(const std::bitset<GALILEO_CNAV_BITS_FOR_CRC>& bits, uint32_t checksum) const
|
2020-11-08 13:10:43 +00:00
|
|
|
{
|
2021-06-02 14:30:26 +00:00
|
|
|
CRC_Galileo_CNAV_type crc_galileo_e6b;
|
2020-11-08 13:10:43 +00:00
|
|
|
|
|
|
|
// Galileo CNAV frame for CRC is not an integer multiple of bytes
|
|
|
|
// it needs to be filled with zeroes at the start of the frame.
|
|
|
|
// This operation is done in the transformation from bits to bytes
|
|
|
|
// using boost::dynamic_bitset.
|
2021-06-02 14:30:26 +00:00
|
|
|
boost::dynamic_bitset<uint8_t> frame_bits(bits.to_string());
|
2020-11-08 13:10:43 +00:00
|
|
|
|
2021-06-02 14:30:26 +00:00
|
|
|
std::vector<uint8_t> bytes;
|
|
|
|
bytes.reserve(GALILEO_CNAV_BYTES_FOR_CRC);
|
2020-11-08 13:10:43 +00:00
|
|
|
boost::to_block_range(frame_bits, std::back_inserter(bytes));
|
|
|
|
std::reverse(bytes.begin(), bytes.end());
|
|
|
|
|
2021-06-02 14:30:26 +00:00
|
|
|
crc_galileo_e6b.process_bytes(bytes.data(), GALILEO_CNAV_BYTES_FOR_CRC);
|
2020-11-08 13:10:43 +00:00
|
|
|
|
2021-06-02 14:30:26 +00:00
|
|
|
const uint32_t crc_computed = crc_galileo_e6b.checksum();
|
2020-11-08 13:10:43 +00:00
|
|
|
if (checksum == crc_computed)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Galileo_Cnav_Message::read_HAS_page(const std::string& page_string)
|
|
|
|
{
|
|
|
|
const std::string has_page_bits = page_string.substr(0, GALILEO_CNAV_BITS_FOR_CRC);
|
2020-11-10 20:20:13 +00:00
|
|
|
const std::string CRC_data = page_string.substr(GALILEO_CNAV_BITS_FOR_CRC, GALILEO_CNAV_CRC_LENGTH);
|
2020-11-08 13:10:43 +00:00
|
|
|
const std::bitset<GALILEO_CNAV_BITS_FOR_CRC> Word_for_CRC_bits(has_page_bits);
|
2020-11-10 20:20:13 +00:00
|
|
|
const std::bitset<GALILEO_CNAV_CRC_LENGTH> checksum(CRC_data);
|
2021-06-02 14:30:26 +00:00
|
|
|
d_new_HAS_page = false;
|
|
|
|
has_page = Galileo_HAS_page();
|
|
|
|
d_flag_CRC_test = CRC_test(Word_for_CRC_bits, checksum.to_ulong());
|
2021-06-08 09:20:25 +00:00
|
|
|
if (d_flag_CRC_test == true)
|
2020-11-08 13:10:43 +00:00
|
|
|
{
|
2021-03-28 11:07:50 +00:00
|
|
|
// CRC correct: Read 24 bits of HAS page header
|
2020-11-10 20:20:13 +00:00
|
|
|
read_HAS_page_header(page_string.substr(GALILEO_CNAV_PAGE_RESERVED_BITS, GALILEO_CNAV_PAGE_HEADER_BITS));
|
|
|
|
bool use_has = false;
|
|
|
|
d_test_mode = false;
|
2021-03-28 11:07:50 +00:00
|
|
|
// HAS status as defined in ICD v1.2 Table 5 HAS Page Header
|
2021-06-02 14:30:26 +00:00
|
|
|
if (!d_page_dummy)
|
2020-11-10 20:20:13 +00:00
|
|
|
{
|
2021-06-02 14:30:26 +00:00
|
|
|
switch (d_has_page_status)
|
|
|
|
{
|
|
|
|
case 0: // HAS is in Test Mode
|
|
|
|
use_has = true;
|
|
|
|
d_test_mode = true;
|
|
|
|
break;
|
|
|
|
case 1: // HAS is in Operational Mode
|
|
|
|
use_has = true;
|
|
|
|
break;
|
|
|
|
case 2: // HAS is in "reserved" status
|
|
|
|
case 3: // Do not use HAS
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2020-11-10 20:20:13 +00:00
|
|
|
}
|
2021-06-02 14:30:26 +00:00
|
|
|
if (use_has or d_page_dummy)
|
2020-11-10 20:20:13 +00:00
|
|
|
{
|
2021-06-02 14:30:26 +00:00
|
|
|
// Store the 424 bits of encoded data (CNAV page) and the page header
|
|
|
|
has_page.has_message_string = page_string.substr(GALILEO_CNAV_PAGE_RESERVED_BITS + GALILEO_CNAV_PAGE_HEADER_BITS, GALILEO_CNAV_MESSAGE_BITS_PER_PAGE);
|
|
|
|
if (!d_page_dummy)
|
|
|
|
{
|
|
|
|
has_page.has_status = d_has_page_status;
|
|
|
|
has_page.reserved = d_has_reserved;
|
|
|
|
has_page.message_type = d_received_message_type;
|
|
|
|
has_page.message_id = d_received_message_id;
|
|
|
|
has_page.message_size = d_received_message_size;
|
|
|
|
has_page.message_page_id = d_received_message_page_id;
|
|
|
|
}
|
|
|
|
d_new_HAS_page = true;
|
2020-11-10 20:20:13 +00:00
|
|
|
}
|
2020-11-08 13:10:43 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-10 20:20:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
void Galileo_Cnav_Message::read_HAS_page_header(const std::string& page_string)
|
|
|
|
{
|
|
|
|
// check if dummy
|
|
|
|
if (page_string == "101011110011101111000011") // Equivalent to AF3BC3
|
|
|
|
{
|
|
|
|
d_page_dummy = true;
|
2021-06-02 14:30:26 +00:00
|
|
|
DLOG(INFO) << "HAS page with dummy header received.";
|
2020-11-10 20:20:13 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
d_page_dummy = false;
|
|
|
|
}
|
|
|
|
if (!d_page_dummy)
|
|
|
|
{
|
2021-03-28 11:07:50 +00:00
|
|
|
// ICD v1.2 Table 5: HAS page header
|
2020-11-10 20:20:13 +00:00
|
|
|
const std::bitset<GALILEO_CNAV_PAGE_HEADER_BITS> has_page_header(page_string);
|
|
|
|
d_has_page_status = read_has_page_header_parameter(has_page_header, GALILEO_HAS_STATUS);
|
2021-06-02 14:30:26 +00:00
|
|
|
d_has_reserved = read_has_page_header_parameter(has_page_header, GALILEO_HAS_RESERVED);
|
2020-11-10 20:20:13 +00:00
|
|
|
d_received_message_type = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_TYPE);
|
|
|
|
d_received_message_id = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_ID);
|
2021-03-28 11:07:50 +00:00
|
|
|
d_received_message_size = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_SIZE) + 1; // "0" means 1
|
2020-11-10 20:20:13 +00:00
|
|
|
d_received_message_page_id = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_PAGE_ID);
|
2020-11-17 18:22:32 +00:00
|
|
|
|
2021-06-08 09:20:25 +00:00
|
|
|
DLOG(INFO) << "HAS page header received " << page_string << ":\n"
|
|
|
|
<< "d_has_page_status: " << static_cast<float>(d_has_page_status) << "\n"
|
|
|
|
<< "d_has_reserved: " << static_cast<float>(d_has_reserved) << "\n"
|
|
|
|
<< "d_received_message_type: " << static_cast<float>(d_received_message_type) << "\n"
|
|
|
|
<< "d_received_message_id: " << static_cast<float>(d_received_message_id) << "\n"
|
|
|
|
<< "d_received_message_size: " << static_cast<float>(d_received_message_size) << "\n"
|
2021-06-02 14:30:26 +00:00
|
|
|
<< "d_received_message_page_id: " << static_cast<float>(d_received_message_page_id);
|
2020-11-10 20:20:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-04-11 12:47:36 +00:00
|
|
|
uint8_t Galileo_Cnav_Message::read_has_page_header_parameter(const std::bitset<GALILEO_CNAV_PAGE_HEADER_BITS>& bits, const std::pair<int32_t, int32_t>& parameter) const
|
2020-11-10 20:20:13 +00:00
|
|
|
{
|
|
|
|
uint8_t value = 0U;
|
|
|
|
for (int j = 0; j < parameter.second; j++)
|
|
|
|
{
|
|
|
|
value <<= 1U; // shift left
|
|
|
|
if (static_cast<int>(bits[GALILEO_CNAV_PAGE_HEADER_BITS - parameter.first - j]) == 1)
|
|
|
|
{
|
|
|
|
value += 1; // insert the bit
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|