Implement HAS message page decoding

Signed-off-by: Carles Fernandez <carles.fernandez@gmail.com>
This commit is contained in:
Carles Fernandez 2021-03-28 13:07:50 +02:00
parent c67ac1247f
commit e262dfc315
8 changed files with 162 additions and 65 deletions

View File

@ -24,6 +24,7 @@
#include "galileo_almanac.h"
#include "galileo_almanac_helper.h"
#include "galileo_ephemeris.h"
#include "galileo_has_data.h"
#include "galileo_iono.h"
#include "galileo_utc_model.h"
#include "geojson_printer.h"
@ -500,6 +501,7 @@ rtklib_pvt_gs::rtklib_pvt_gs(uint32_t nchannels,
d_galileo_utc_model_sptr_type_hash_code = typeid(std::shared_ptr<Galileo_Utc_Model>).hash_code();
d_galileo_almanac_helper_sptr_type_hash_code = typeid(std::shared_ptr<Galileo_Almanac_Helper>).hash_code();
d_galileo_almanac_sptr_type_hash_code = typeid(std::shared_ptr<Galileo_Almanac>).hash_code();
d_galileo_has_message_sptr_type_hash_code = typeid(std::shared_ptr<Galileo_HAS_data>).hash_code();
d_glonass_gnav_ephemeris_sptr_type_hash_code = typeid(std::shared_ptr<Glonass_Gnav_Ephemeris>).hash_code();
d_glonass_gnav_utc_model_sptr_type_hash_code = typeid(std::shared_ptr<Glonass_Gnav_Utc_Model>).hash_code();
d_glonass_gnav_almanac_sptr_type_hash_code = typeid(std::shared_ptr<Glonass_Gnav_Almanac>).hash_code();
@ -1315,6 +1317,10 @@ void rtklib_pvt_gs::msg_handler_telemetry(const pmt::pmt_t& msg)
d_user_pvt_solver->galileo_almanac_map[galileo_alm->PRN] = *galileo_alm;
}
}
else if (msg_type_hash_code == d_galileo_has_message_sptr_type_hash_code)
{
// Store HAS message and print its content
}
// **************** GLONASS GNAV Telemetry *************************
else if (msg_type_hash_code == d_glonass_gnav_ephemeris_sptr_type_hash_code)

View File

@ -216,6 +216,7 @@ private:
size_t d_galileo_utc_model_sptr_type_hash_code;
size_t d_galileo_almanac_helper_sptr_type_hash_code;
size_t d_galileo_almanac_sptr_type_hash_code;
size_t d_galileo_has_message_sptr_type_hash_code;
size_t d_glonass_gnav_ephemeris_sptr_type_hash_code;
size_t d_glonass_gnav_utc_model_sptr_type_hash_code;
size_t d_glonass_gnav_almanac_sptr_type_hash_code;

View File

@ -26,6 +26,7 @@
#include "display.h"
#include "galileo_almanac_helper.h" // for Galileo_Almanac_Helper
#include "galileo_ephemeris.h" // for Galileo_Ephemeris
#include "galileo_has_data.h" // For Galileo HAS messages
#include "galileo_iono.h" // for Galileo_Iono
#include "galileo_utc_model.h" // for Galileo_Utc_Model
#include "gnss_synchro.h"
@ -512,15 +513,15 @@ void galileo_telemetry_decoder_gs::decode_CNAV_word(float *page_symbols, int32_t
// 4. If we have a new full message, read it
if (d_cnav_nav.have_new_HAS_message() == true)
{
// TODO: Retrieve data from message and send it somewhere
// Galileo_HAS_data has_data = d_cnav_nav.get_HAS_data();
if (d_cnav_nav.is_HAS_message_dummy())
if (d_cnav_nav.is_HAS_message_dummy() == true)
{
std::cout << TEXT_MAGENTA << "New Galileo E6 HAS message received in channel " << d_channel << " from satellite " << d_satellite << TEXT_RESET << '\n';
std::cout << TEXT_MAGENTA << "New Galileo E6 HAS dummy message received in channel " << d_channel << " from satellite " << d_satellite << TEXT_RESET << '\n';
}
else
{
std::cout << TEXT_MAGENTA << "New Galileo E6 HAS dummy message received in channel " << d_channel << " from satellite " << d_satellite << TEXT_RESET << '\n';
const std::shared_ptr<Galileo_HAS_data> tmp_obj = std::make_shared<Galileo_HAS_data>(d_cnav_nav.get_HAS_data());
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
std::cout << TEXT_MAGENTA << "New Galileo E6 HAS message received in channel " << d_channel << " from satellite " << d_satellite << TEXT_RESET << '\n';
}
}
}
@ -649,7 +650,7 @@ int galileo_telemetry_decoder_gs::general_work(int noutput_items __attribute__((
if (abs(corr_value) >= d_samples_per_preamble)
{
d_preamble_index = d_sample_counter; // record the preamble sample stamp
DLOG(INFO) << "Preamble detection for Galileo satellite " << this->d_satellite;
LOG(INFO) << "Preamble detection for Galileo satellite " << this->d_satellite << " in channel " << this->d_channel;
d_stat = 1; // enter into frame pre-detection status
}
}

View File

@ -68,14 +68,16 @@ constexpr int32_t GALILEO_CNAV_CRC_LENGTH = 24;
constexpr int32_t GALILEO_CNAV_MESSAGE_BITS_PER_PAGE = 424;
constexpr int32_t GALILEO_CNAV_PAGE_HEADER_BITS = 24;
constexpr int32_t GALILEO_CNAV_PREAMBLE_LENGTH_BITS = 16;
constexpr int32_t GALILEO_CNAV_MAX_NUMBER_ENCODED_BLOCKS = 255;
constexpr int32_t GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK = 255;
constexpr int32_t GALILEO_CNAV_MT1_HEADER_BITS = 32;
constexpr int32_t GALILEO_CNAV_OCTETS_IN_SUBPAGE = 53;
constexpr int32_t GALILEO_CNAV_INFORMATION_VECTOR_LENGTH = 32;
constexpr int32_t HAS_MSG_MAX_SATS = 40;
constexpr int32_t HAS_MSG_MAX_SIGNALS = 16;
constexpr uint8_t HAS_MSG_GPS_SYSTEM = 0; // Table 8 ICD
constexpr uint8_t HAS_MSG_GALILEO_SYSTEM = 2; // Table 8 ICD
constexpr uint8_t HAS_MSG_GPS_SYSTEM = 0; // Table 8 ICD v1.2
constexpr uint8_t HAS_MSG_GALILEO_SYSTEM = 2; // Table 8 ICD v1.2
constexpr char GALILEO_CNAV_PREAMBLE[17] = "1011011101110000";

View File

@ -3,14 +3,14 @@
* \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)
* \author Carles Fernandez-Prades, 2020 cfernandez(at)cttc.es
* \author Carles Fernandez-Prades, 2020-2021 cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
@ -60,10 +60,11 @@ void Galileo_Cnav_Message::read_HAS_page(const std::string& page_string)
if (CRC_test(Word_for_CRC_bits, checksum.to_ulong()) == true)
{
d_flag_CRC_test = true;
// CRC correct: Read HAS page header
// CRC correct: Read 24 bits of HAS page header
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;
// HAS status as defined in ICD v1.2 Table 5 HAS Page Header
switch (d_has_page_status)
{
case 0: // HAS is in Test Mode
@ -73,11 +74,14 @@ void Galileo_Cnav_Message::read_HAS_page(const std::string& page_string)
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;
}
if (use_has)
{
// Process the 424 bits of encoded data
process_HAS_page(page_string.substr(GALILEO_CNAV_PAGE_RESERVED_BITS + GALILEO_CNAV_PAGE_HEADER_BITS, GALILEO_CNAV_MESSAGE_BITS_PER_PAGE));
}
}
@ -101,11 +105,12 @@ void Galileo_Cnav_Message::read_HAS_page_header(const std::string& page_string)
}
if (!d_page_dummy)
{
// ICD v1.2 Table 5: HAS page header
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);
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);
d_received_message_size = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_SIZE);
d_received_message_size = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_SIZE) + 1; // "0" means 1
d_received_message_page_id = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_PAGE_ID);
}
}
@ -116,14 +121,27 @@ void Galileo_Cnav_Message::process_HAS_page(const std::string& page_string)
if (d_current_message_id == d_received_message_id)
{
// if receiver pid was not there, store it.
if (std::find(d_list_pid.begin(), d_list_pid.end(), d_received_message_page_id) == d_list_pid.end())
if (d_received_message_page_id == 0)
{
if (d_received_message_type == 1) // contains satellite corrections
// reserved, ignore it
}
else
{
if (std::find(d_list_pid.begin(), d_list_pid.end(), d_received_message_page_id) == d_list_pid.end())
{
d_received_encoded_messages++;
d_list_pid.push_back(d_received_message_page_id);
// Store encoded page
d_encoded_message_type_1 += std::string(page_string);
if (d_received_message_type == 1) // contains satellite corrections
{
d_received_encoded_messages++;
d_list_pid.push_back(d_received_message_page_id);
// Pack encoded string into 53 octets and put it in
// the corresponding row of d_C_matrix.
for (int k = 0; k < GALILEO_CNAV_OCTETS_IN_SUBPAGE; k++)
{
std::string bits8 = page_string.substr(k * 8, 8);
std::bitset<8> bs(bits8);
d_C_matrix[d_received_message_page_id - 1][k] = static_cast<uint8_t>(bs.to_ulong());
}
}
}
}
}
@ -134,48 +152,112 @@ void Galileo_Cnav_Message::process_HAS_page(const std::string& page_string)
d_received_encoded_messages = 0;
d_new_message = false;
d_current_message_size = d_received_message_size;
// erase stored pages and start storing again
d_encoded_message_type_1.clear();
// erase stored pages and data, and start storing again
d_list_pid.clear();
d_HAS_data = Galileo_HAS_data();
if (d_received_message_type == 1)
{
d_encoded_message_type_1.reserve(GALILEO_CNAV_MAX_NUMBER_ENCODED_BLOCKS * GALILEO_CNAV_MESSAGE_BITS_PER_PAGE);
d_received_encoded_messages++;
d_list_pid.push_back(d_received_message_page_id);
d_encoded_message_type_1 += std::string(page_string);
// Pack encoded string into 53 octets and put it in
// the corresponding row of d_C_matrix.
for (int k = 0; k < GALILEO_CNAV_OCTETS_IN_SUBPAGE; k++)
{
std::string bits8 = page_string.substr(k * 8, 8);
std::bitset<8> bs(bits8);
d_C_matrix[d_received_message_page_id - 1][k] = static_cast<uint8_t>(bs.to_ulong());
}
}
}
if (d_received_encoded_messages == d_current_message_size)
{
// we have a full encoded message stored in d_encoded_message_type_1
// we have a full encoded message stored in d_C_matrix
d_received_encoded_messages = 0;
d_current_message_id = 0;
d_new_message = true;
decode_message_type1();
int res = decode_message_type1();
if (res == 0)
{
d_new_message = true;
}
else
{
d_new_message = false;
}
}
}
void Galileo_Cnav_Message::decode_message_type1()
int Galileo_Cnav_Message::decode_message_type1()
{
// TODO: Reed-Solomon decoding of d_encoded_message_type_1
// TODO: reordering
// decoded_message_type1 = ...
// read_HAS_message_type1(decoded_message_type1);
}
// All rows in d_C_matrix with no data are erasure positions
std::vector<int> erasure_positions;
erasure_positions.reserve(GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK - d_list_pid.size());
for (uint8_t mpid = 1; mpid <= GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK; mpid++)
{
if (std::find(d_list_pid.begin(), d_list_pid.end(), mpid) == d_list_pid.end())
{
erasure_positions.push_back(mpid - 1);
}
else
{
d_list_pid.remove(mpid);
}
}
void Galileo_Cnav_Message::read_HAS_message_type1(const std::string& message_string)
{
d_HAS_data = Galileo_HAS_data();
read_MT1_header(message_string);
read_MT1_body(message_string);
// Vertical decoding of d_C_matrix
for (int col = 0; col < GALILEO_CNAV_OCTETS_IN_SUBPAGE; col++)
{
std::vector<uint8_t> C_column(GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK, 0);
for (int row = 0; row < GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK; row++)
{
C_column[row] = d_C_matrix[row][col];
}
int result = rs.decode(C_column, erasure_positions);
if (result < 0)
{
// Decoding failed
d_C_matrix = {GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)};
d_M_matrix = {GALILEO_CNAV_INFORMATION_VECTOR_LENGTH, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE)};
return -1;
}
std::vector<uint8_t> M_column(C_column.begin(), C_column.begin() + GALILEO_CNAV_INFORMATION_VECTOR_LENGTH);
for (int i = 0; i < GALILEO_CNAV_INFORMATION_VECTOR_LENGTH; i++)
{
d_M_matrix[i][col] = M_column[i];
}
}
// Form the decoded HAS message by reading rows of d_M_matrix
std::string decoded_message_type_1;
decoded_message_type_1.reserve(d_current_message_size * GALILEO_CNAV_OCTETS_IN_SUBPAGE * 8);
for (uint8_t row = 0; row < d_current_message_size; row++)
{
for (int col = 0; col < GALILEO_CNAV_OCTETS_IN_SUBPAGE; col++)
{
std::bitset<8> bs(d_M_matrix[row][col]);
decoded_message_type_1 += bs.to_string();
}
}
// reset d_C_matrix and d_M_matrix for next decoding
d_C_matrix = {GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)};
d_M_matrix = {GALILEO_CNAV_INFORMATION_VECTOR_LENGTH, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)};
// Trigger HAS message content reading
read_MT1_header(decoded_message_type_1);
read_MT1_body(decoded_message_type_1);
return 0;
}
void Galileo_Cnav_Message::read_MT1_header(const std::string& message_string)
{
// ICD v1.2 Table 6: MT1 Message Header.
const std::bitset<GALILEO_CNAV_MT1_HEADER_BITS> has_mt1_header(message_string);
d_HAS_data.header.toh = read_has_message_header_parameter_uint16(has_mt1_header, GALILEO_MT1_HEADER_TOH);
d_HAS_data.header.mask_id = read_has_message_header_parameter_uint8(has_mt1_header, GALILEO_MT1_HEADER_MASK_ID);
@ -192,6 +274,7 @@ void Galileo_Cnav_Message::read_MT1_header(const std::string& message_string)
void Galileo_Cnav_Message::read_MT1_body(const std::string& message_string)
{
// ICD v1.2 Table 7: MT1 Message Body.
auto message = std::string(message_string.begin() + GALILEO_CNAV_MT1_HEADER_BITS, message_string.end()); // Remove header
int Nsat = 0;
if (d_HAS_data.header.mask_flag)

View File

@ -3,14 +3,14 @@
* \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)
* \author Carles Fernandez-Prades, 2020 cfernandez(at)cttc.es
* \author Carles Fernandez-Prades, 2020-2021 cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
@ -21,10 +21,12 @@
#include "Galileo_CNAV.h"
#include "galileo_has_data.h"
#include "reed_solomon.h"
#include <bitset>
#include <cstdint>
#include <list>
#include <string>
#include <vector>
/** \addtogroup Core
* \{ */
@ -68,23 +70,24 @@ private:
bool CRC_test(std::bitset<GALILEO_CNAV_BITS_FOR_CRC> bits, uint32_t checksum) const;
void read_HAS_page_header(const std::string& page_string);
void process_HAS_page(const std::string& page_string);
void decode_message_type1();
void read_HAS_message_type1(const std::string& message_string);
void read_MT1_header(const std::string& message_string);
void read_MT1_body(const std::string& message_string);
int decode_message_type1();
uint8_t read_has_page_header_parameter(std::bitset<GALILEO_CNAV_PAGE_HEADER_BITS> bits, const std::pair<int32_t, int32_t>& parameter) const;
uint8_t read_has_message_header_parameter_uint8(std::bitset<GALILEO_CNAV_MT1_HEADER_BITS> bits, const std::pair<int32_t, int32_t>& parameter) const;
uint16_t read_has_message_header_parameter_uint16(std::bitset<GALILEO_CNAV_MT1_HEADER_BITS> bits, const std::pair<int32_t, int32_t>& parameter) const;
bool read_has_message_header_parameter_bool(std::bitset<GALILEO_CNAV_MT1_HEADER_BITS> bits, const std::pair<int32_t, int32_t>& parameter) const;
uint8_t read_has_message_body_uint8(const std::string& bits) const;
uint16_t read_has_message_body_uint16(const std::string& bits) const;
uint64_t read_has_message_body_uint64(const std::string& bits) const;
int16_t read_has_message_body_int16(const std::string& bits) const;
Galileo_HAS_data d_HAS_data{};
std::string d_encoded_message_type_1;
ReedSolomon rs = ReedSolomon();
std::vector<std::vector<uint8_t>> d_C_matrix{GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)}; // 255 x 53
std::vector<std::vector<uint8_t>> d_M_matrix{GALILEO_CNAV_INFORMATION_VECTOR_LENGTH, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)}; // 32 x 53
std::list<uint8_t> d_list_pid;
uint8_t d_has_page_status{};

View File

@ -409,21 +409,21 @@ uint8_t ReedSolomon::galois_mul_table(uint8_t a, uint8_t b) const
return 0;
}
uint8_t x = log_table[a];
uint8_t y = log_table[b];
uint8_t x = d_log_table[a];
uint8_t y = d_log_table[b];
uint8_t log_mult = (x + y) % d_symbols_per_block;
return antilog[log_mult];
return d_antilog[log_mult];
}
void ReedSolomon::init_log_tables()
{
log_table[0] = 0; // dummy value
d_log_table[0] = 0; // dummy value
for (int i = 0, x = 1; i < d_symbols_per_block; x = galois_mul(x, d_min_poly), i++)
{
log_table[x] = i;
antilog[i] = x;
d_log_table[x] = i;
d_antilog[i] = x;
}
}

View File

@ -85,7 +85,7 @@ public:
* of the input vector.
*
* The second parameter is optional, and contains a vector of erasure
* positions to be passed to the decoding algorithm.
* positions to be passed to the decoding algorithm. Defaults to empty.
*
* Returns the number of corrected errors or -1 if decoding failed.
*/
@ -105,36 +105,37 @@ public:
private:
static const int d_symbols_per_block = 255; // the total number of symbols in a RS block.
int decode_rs_8(uint8_t* data, const int* eras_pos, int no_eras, int pad) const;
int mod255(int x) const;
int rs_min(int a, int b) const;
int decode_rs_8(uint8_t* data, const int* eras_pos, int no_eras, int pad) const;
uint8_t galois_mul(uint8_t a, uint8_t b) const;
uint8_t galois_add(uint8_t a, uint8_t b) const;
uint8_t galois_mul_table(uint8_t a, uint8_t b) const;
void encode_rs_8(const uint8_t* data, uint8_t* parity) const;
void init_log_tables();
void init_alpha_tables();
void init_log_tables(); // initialize d_log_table and d_antilog
void init_alpha_tables(); // initialize d_alpha_to, d_index_of
std::array<uint8_t, 256> log_table{};
std::array<uint8_t, 255> antilog{};
std::array<uint8_t, 256> d_alpha_to{};
std::array<uint8_t, 256> d_index_of{};
std::vector<std::vector<uint8_t>> d_genmatrix;
std::vector<uint8_t> d_genpoly_coeff;
std::vector<uint8_t> d_genpoly_index;
std::array<uint8_t, 256> d_alpha_to{}; // used for decoding
std::array<uint8_t, 256> d_index_of{}; // used for decoding
std::array<uint8_t, 256> d_log_table{}; // used for encoding
std::array<uint8_t, 255> d_antilog{}; // used for encoding
size_t d_data_in_block;
std::vector<std::vector<uint8_t>> d_genmatrix; // used for encoding
std::vector<uint8_t> d_genpoly_coeff; // used for encoding
std::vector<uint8_t> d_genpoly_index; // used for encoding
size_t d_data_in_block; // number of information symbols in a block
int d_nroots; // number of parity symbols in a block
int d_prim; // The primitive root of the generator poly.
int d_pad; // the number of pad symbols in a block.
int d_prim; // The primitive root of the generator poly
int d_pad; // the number of pad symbols in a block
int d_iprim; // prim-th root of 1, index form
int d_fcr; // first consecutive root
uint8_t d_min_poly;
uint8_t d_a0;
uint8_t d_min_poly; // primitive polynomial
uint8_t d_a0; // auxiliar variable
};
/** \} */