1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2025-04-08 03:36:44 +00:00

[TAS-242][FEAT] Implement alert message handling

* Test show that behavior is as expected.
* interestingly, the first batch of Tags after OAM received fail, then succeeds, until OAM is verified and OSNMA is disabled.
This commit is contained in:
cesaaargm 2024-07-31 17:08:06 +02:00
parent 02c5d26dcc
commit 2db37f384e
4 changed files with 92 additions and 47 deletions
src
algorithms/telemetry_decoder/gnuradio_blocks
core/libs
tests/unit-tests/signal-processing-blocks/osnma

@ -370,9 +370,6 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
// 1. De-interleave
std::vector<float> page_part_symbols_soft_value(frame_length);
deinterleaver(GALILEO_INAV_INTERLEAVER_ROWS, GALILEO_INAV_INTERLEAVER_COLS, page_part_symbols, page_part_symbols_soft_value.data());
bool flag_osnma_adkd_4_gst = false;
bool flag_osnma_adkd_4_utc = false;
// 2. Viterbi decoder
// 2.1 Take into account the NOT gate in G2 polynomial (Galileo ICD Figure 13, FEC encoder)
for (int32_t i = 0; i < frame_length; i++)
@ -443,8 +440,7 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
// 4. Push the new navigation data to the queues
// extract OSNMA bits, reset container.
bool check_size_is_ok = d_inav_nav.get_osnma_adkd_0_12_nav_bits().size() == 549;
if (check_size_is_ok)
if (d_inav_nav.get_osnma_adkd_0_12_nav_bits().size() == 549)
{
DLOG(INFO) << "Galileo OSNMA: new ADKD=0/12 navData from " << d_satellite << " at TOW_sf=" << d_inav_nav.get_TOW5() - 25;
const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string, uint32_t>>( // < PRNd , navDataBits, TOW_Sosf>
@ -452,14 +448,9 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
d_inav_nav.get_osnma_adkd_0_12_nav_bits(),
d_inav_nav.get_TOW5() - 25);
this->message_port_pub(pmt::mp("OSNMA_from_TLM"), pmt::make_any(tmp_obj_osnma));
DLOG(INFO) << "|---> Galileo OSNMA :: Sending Telemetry Decoder NavData (PRN_d="
<< static_cast<int>(d_satellite.get_PRN())
<< ", TOW=" << static_cast<int>(d_inav_nav.get_TOW5() - 25) << ")"; //: 0b" << d_inav_nav.get_osnma_adkd_0_12_nav_bits();
d_inav_nav.reset_osnma_nav_bits_adkd0_12();
}
check_size_is_ok = d_inav_nav.get_osnma_adkd_4_nav_bits().size() == 141;
if (check_size_is_ok)
if (d_inav_nav.get_osnma_adkd_4_nav_bits().size() == 141)
{
DLOG(INFO) << "Galileo OSNMA: new ADKD=4 navData from " << d_satellite << " at TOW_sf=" << d_inav_nav.get_TOW6() - 5;
const auto tmp_obj = std::make_shared<std::tuple<uint32_t, std::string, uint32_t>>( // < PRNd , navDataBits, TOW_Sosf> // TODO conversion from W6 to W_Start_of_subframe
@ -467,9 +458,6 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
d_inav_nav.get_osnma_adkd_4_nav_bits(),
d_inav_nav.get_TOW6() - 5);
this->message_port_pub(pmt::mp("OSNMA_from_TLM"), pmt::make_any(tmp_obj));
DLOG(INFO) << "|---> Galileo OSNMA :: Sending Telemetry Decoder NavData (PRN_d="
<< static_cast<int>(d_satellite.get_PRN())
<< ", TOW=" << static_cast<int>(d_inav_nav.get_TOW6() - 5) << ")"; //: 0b" << d_inav_nav.get_osnma_adkd_4_nav_bits();
d_inav_nav.reset_osnma_nav_bits_adkd4();
}
@ -588,8 +576,6 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
d_delta_t = tmp_obj->A_0G + tmp_obj->A_1G * (static_cast<double>(d_TOW_at_current_symbol_ms) / 1000.0 - tmp_obj->t_0G + 604800 * (std::fmod(static_cast<float>(d_inav_nav.get_Galileo_week() - tmp_obj->WN_0G), 64.0)));
DLOG(INFO) << "delta_t=" << d_delta_t << "[s]";
flag_osnma_adkd_4_utc = true;
}
if (d_inav_nav.have_new_almanac() == true) // flag_almanac_4 tells if W10 available.
@ -623,16 +609,6 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
DLOG(INFO) << "Current parameters:";
DLOG(INFO) << "d_TOW_at_current_symbol_ms=" << d_TOW_at_current_symbol_ms;
DLOG(INFO) << "d_nav.WN_0=" << d_inav_nav.get_Galileo_week();
flag_osnma_adkd_4_gst = true;
}
// get osnma message if the needed nav data is available
bool adkd_4_nav_data_available = flag_osnma_adkd_4_utc && flag_osnma_adkd_4_gst; // supposition: data did not change bt. flags reset and now.
// bool adkd_4_nav_data_available = d_inav_nav.get_osnma_adkd_4_nav_bits().size() == 141; // newApproach: let decoder decide when block starts and let it fill the data, and just check for length
if (adkd_4_nav_data_available /*&& d_inav_nav.is_TOW5_set() not needed cause W6 has TOW also.*/)
{
}
auto newOSNMA = d_inav_nav.have_new_nma();
if (d_band == '1' && newOSNMA)

@ -142,12 +142,11 @@ void osnma_msg_receiver::msg_handler_osnma(const pmt::pmt_t& msg)
} // OSNMA frame received
else if (msg_type_hash_code == typeid(std::shared_ptr<std::tuple<uint32_t, std::string, uint32_t>>).hash_code()) // Navigation data bits for OSNMA received
{
// TODO - PRNa is a typo here, I think for d_satellite_nav_data, is PRN_d the name to use
const auto inav_data = wht::any_cast<std::shared_ptr<std::tuple<uint32_t, std::string, uint32_t>>>(pmt::any_ref(msg));
uint32_t PRNa = std::get<0>(*inav_data);
uint32_t PRNd = std::get<0>(*inav_data);
std::string nav_data = std::get<1>(*inav_data);
uint32_t TOW = std::get<2>(*inav_data);
d_nav_data_manager->add_navigation_data(nav_data, PRNa, TOW);
d_nav_data_manager->add_navigation_data(nav_data, PRNd, TOW);
}
else
{
@ -173,6 +172,9 @@ void osnma_msg_receiver::msg_handler_osnma(const pmt::pmt_t& msg)
void osnma_msg_receiver::process_osnma_message(const std::shared_ptr<OSNMA_msg>& osnma_msg)
{
if (d_flag_alert_message && (d_public_key_verified || d_kroot_verified)){
return;
}
read_nma_header(osnma_msg->hkroot[0]);
// Check for corner cases: renewal, revocation
@ -180,7 +182,8 @@ void osnma_msg_receiver::process_osnma_message(const std::shared_ptr<OSNMA_msg>&
LOG(WARNING) << "Galileo OSNMA: NMAS invalid (RES), skipping osnma message";
return;
}
// TODO - trusting the NMAS and CPKS shall be done upon PKR verification or Tag verification.
// It's ok to activate the flags, but the final decision should happen after verifying it.
if (d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 4 /* NPK */ && d_GST_PKR_PKREV_start == 0){
d_flag_PK_renewal = true;
d_GST_PKR_PKREV_start = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0);
@ -195,7 +198,7 @@ void osnma_msg_receiver::process_osnma_message(const std::shared_ptr<OSNMA_msg>&
std::cout << "Galileo OSNMA: Public Key Renewal :: Finished at GST=" << duration_hours << ", Duration=" << duration_hours << " h" << std::endl;
}
if(d_osnma_data.d_nma_header.nmas == 3 /* DU */ && d_osnma_data.d_nma_header.cpks == 5 /* PKREV */ && d_GST_PKR_PKREV_start == 0){
if (d_osnma_data.d_nma_header.nmas == 3 /* DU */ && d_osnma_data.d_nma_header.cpks == 5 /* PKREV */ && d_GST_PKR_PKREV_start == 0){
d_flag_PK_revocation = true;
d_number_of_blocks[d_osnma_data.d_dsm_header.dsm_id] = 0;
d_public_key_verified = false;
@ -214,6 +217,14 @@ void osnma_msg_receiver::process_osnma_message(const std::shared_ptr<OSNMA_msg>&
std::cout << "Galileo OSNMA: Public Key Revocation :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << ", Duration=" << duration_hours << "h" << std::endl;
}
if (d_osnma_data.d_nma_header.nmas == 3 /* DU */ && d_osnma_data.d_nma_header.cpks == 7 /* AM */ && d_GST_PKR_AM_start == 0){
d_flag_alert_message = true;
d_GST_PKR_AM_start = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0);
d_public_key_verified = false;
d_kroot_verified = false;
LOG(INFO) << "Galileo OSNMA: Alert message :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]";
std::cout << "Galileo OSNMA: Alert message :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << std::endl;
}
read_dsm_header(osnma_msg->hkroot[1]);
read_dsm_block(osnma_msg);
@ -533,8 +544,9 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
<< ", WN=" << static_cast<uint32_t>(d_osnma_data.d_dsm_kroot_message.wn_k)
<< ", TOW=" << static_cast<uint32_t>(d_osnma_data.d_dsm_kroot_message.towh_k) * 3600;
// local_time_verification(osnma_msg); // FIXME TODO: real time verification needed
// If new PK verified and the new KROOT arrived, set the new PK before attempting verification
if(d_flag_PK_renewal && d_osnma_data.d_dsm_kroot_message.pkid == d_new_public_key_id && d_flag_NPK_set == false){
// set new public key to be used.
d_crypto->set_public_key(d_new_public_key);
d_crypto->store_public_key(PEMFILE_DEFAULT);
d_flag_NPK_set = true;
@ -551,9 +563,15 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
{
std::cout << "Galileo OSNMA: DSM-KROOT authentication successful!" << std::endl;
LOG(INFO) << "Galileo OSNMA: DSM-KROOT authentication successful!";
LOG(INFO) << "Galileo OSNMA: NMA Status is " << d_dsm_reader->get_nmas_status(d_osnma_data.d_nma_header.nmas) << ", "
<< "Chain in force is " << static_cast<uint32_t>(d_osnma_data.d_nma_header.cid) << ", "
<< "Chain and Public Key Status is " << d_dsm_reader->get_cpks_status(d_osnma_data.d_nma_header.cpks);
if (d_flag_alert_message){
LOG(WARNING) << "Galileo OSNMA: DSM-KROOT :: Alert message verification :: SUCCESS. ";
}
else
{
LOG(INFO) << "Galileo OSNMA: NMA Status is " << d_dsm_reader->get_nmas_status(d_osnma_data.d_nma_header.nmas) << ", "
<< "Chain in force is " << static_cast<uint32_t>(d_osnma_data.d_nma_header.cid) << ", "
<< "Chain and Public Key Status is " << d_dsm_reader->get_cpks_status(d_osnma_data.d_nma_header.cpks);
}
// Save DSM-Kroot and NMA header into a permanent storage
if (d_flag_hot_start){
d_flag_hot_start = false;
@ -565,6 +583,9 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
{
LOG(WARNING) << "Galileo OSNMA: DSM-KROOT authentication failed.";
std::cerr << "Galileo OSNMA: DSM-KROOT authentication failed." << std::endl;
if (d_flag_alert_message){
d_flag_alert_message = false;
}
d_count_failed_Kroot ++;
}
}
@ -607,9 +628,10 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
}
}
uint32_t l_dp_bytes = dsm_msg.size();
if (d_osnma_data.d_dsm_pkr_message.npkt == 4)
if (d_osnma_data.d_dsm_pkr_message.npkt == 4 && d_osnma_data.d_dsm_pkr_message.npktid == 0)
{
LOG(WARNING) << "Galileo OSNMA: OAM received";
LOG(WARNING) << "Galileo OSNMA: DSM-PKR :: Alert message received. Verifying it.";
std::cout << "Galileo OSNMA: DSM-PKR :: Alert message received. Verifying it." << std::endl;
l_npk_bytes = l_dp_bytes - 130; // bytes
}
@ -624,6 +646,7 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
if (l_dp_bytes != check_l_dp_bytes)
{
LOG(WARNING) << "Galileo OSNMA: Failed length reading of DSM-PKR message";
d_flag_alert_message = false;
}
else
{
@ -635,10 +658,7 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
// TODO: kroot fields are 0 in case no DSM-KROOT received yet, need to take this into account.
// std::vector<uint8_t> mi; // (NPKT + NPKID + NPK)
LOG(INFO) << "Galileo OSNMA: DSM-PKR with CID=" << static_cast<uint32_t>(d_osnma_data.d_nma_header.cid)
<< ", PKID=" << static_cast<uint32_t>(d_osnma_data.d_dsm_pkr_message.npktid)
/*<< ", WN=" << static_cast<uint32_t>(d_osnma_data.d_dsm_kroot_message.wn_k)
<< ", TOW=" << static_cast<uint32_t>(d_osnma_data.d_dsm_kroot_message.towh_k) * 3600*/
<< " received";
<< ", PKID=" << static_cast<uint32_t>(d_osnma_data.d_dsm_pkr_message.npktid) << " received";
// Public key verification against Merkle tree root.
bool verification = verify_dsm_pkr(d_osnma_data.d_dsm_pkr_message);
if (verification)
@ -648,7 +668,12 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
if (d_flag_PK_renewal){
d_new_public_key = d_osnma_data.d_dsm_pkr_message.npk;
}
else {
else if (d_flag_alert_message){
LOG(WARNING) << "Galileo OSNMA: DSM-PKR verification :: Alert message verification :: SUCCESS. OSNMA disabled. Contact Galileo Service Centre";
std::cout << "Galileo OSNMA: DSM-PKR verification :: Alert message verification :: SUCCESS. OSNMA disabled. Contact Galileo Service Centre" << std::endl;
}
else{
d_crypto->d_PublicKeyType = PKT;
d_crypto->set_public_key(d_osnma_data.d_dsm_pkr_message.npk);
d_crypto->store_public_key(PEMFILE_DEFAULT);
@ -659,6 +684,10 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
LOG(ERROR) << "Galileo OSNMA: DSM-PKR verification :: FAILURE";
d_public_key_verified = false;
d_count_failed_pubKey ++;
if (d_flag_alert_message){
d_flag_alert_message = false; // disregard message as its authenticity could not be verified.
}
}
}
}
@ -692,7 +721,8 @@ void osnma_msg_receiver::read_and_process_mack_block(const std::shared_ptr<OSNMA
}
d_osnma_data.d_nav_data.set_tow_sf0(osnma_msg->TOW_sf0);
bool can_process_mack_block = d_osnma_data.d_nma_header.nmas != 3; // Don't Use
bool can_process_mack_block = (d_osnma_data.d_nma_header.nmas != 3 && d_kroot_verified) || // NMAS different than DU
(d_osnma_data.d_nma_header.nmas == 3 && !d_kroot_verified); // NMAS is DU, but must be disregarded
bool can_verify_tesla_key = d_kroot_verified || d_tesla_key_verified; // Either of those suffices for verifying the incoming TESLA key
bool can_parse_tag_fields = d_osnma_data.d_dsm_kroot_message.ts != 0; // calculating the number of tags is based on the TS of the DSM-KROOT.
if (can_verify_tesla_key && can_parse_tag_fields && can_process_mack_block)

@ -62,8 +62,8 @@ osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath,
class osnma_msg_receiver : public gr::block
{
public:
~osnma_msg_receiver() = default; //!< Default destructor
std::unique_ptr<Gnss_Crypto> d_crypto; // access to cryptographic functions
~osnma_msg_receiver() = default; //!< Default destructor
std::unique_ptr<Gnss_Crypto> d_crypto; // access to cryptographic functions
void msg_handler_osnma(const pmt::pmt_t& msg); // GnssCrypto and the message handler are needed by public method within TestVectors fixture
private:
friend osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath);
@ -134,6 +134,7 @@ private:
uint32_t d_GST_0{};
uint32_t d_GST_SIS{};
uint32_t d_GST_PKR_PKREV_start{};
uint32_t d_GST_PKR_AM_start{};
uint8_t d_Lt_min{}; // minimum equivalent tag length
uint8_t d_Lt_verified_eph{0}; // verified tag bits - ephemeris
@ -152,6 +153,7 @@ private:
uint8_t d_new_public_key_id{};
std::vector<uint8_t> d_new_public_key;
bool d_flag_NPK_set{false};
bool d_flag_alert_message{false};
// Provide access to inner functions to Gtest
uint32_t d_count_successful_tags{0};
@ -169,6 +171,7 @@ private:
FRIEND_TEST(OsnmaTestVectors, NominalTestConf2);
FRIEND_TEST(OsnmaTestVectors, PublicKeyRenewal);
FRIEND_TEST(OsnmaTestVectors, PublicKeyRevocation);
FRIEND_TEST(OsnmaTestVectors, AlertMessage);
};

@ -181,6 +181,42 @@ TEST_F(OsnmaTestVectors, PublicKeyRevocation)
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
TEST_F(OsnmaTestVectors, AlertMessage){
// Arrange
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_3/PublicKey/OSNMA_PublicKey_20231007201500_PKID_1.crt";
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_3/MerkleTree/OSNMA_MerkleTree_20231007201500_PKID_1.xml";
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
std::tm input_time_step1 = {0, 45, 18, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step2 = {0, 45, 19, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::vector<std::tm> input_times = { input_time_step1, input_time_step2 };
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/oam_step1/07_OCT_2023_GST_18_45_01.csv");
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/oam_step2/07_OCT_2023_GST_19_45_01.csv");
if (testVectors_step1.empty() || testVectors_step2.empty())
{
ASSERT_TRUE(false);
}
std::vector<std::vector<TestVector>> testVectors = { testVectors_step1, testVectors_step2};
// Act
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
// Assert
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
ASSERT_EQ(osnma->d_count_failed_tags, 0);
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
// Auxiliary functions for the OsnmaTestVectorsSimulation test fixture.
// Essentially, they perform same work as the telemetry decoder block, but adapted to the osnma-test-vector files.
bool OsnmaTestVectors::feedOsnmaWithTestVectors(osnma_msg_receiver_sptr osnma_object, std::vector<std::vector<TestVector>> testVectors, std::vector<std::tm> startTimesFiles){
@ -338,7 +374,7 @@ bool OsnmaTestVectors::feedOsnmaWithTestVectors(osnma_msg_receiver_sptr osnma_ob
tv.svId,
nav_data_ADKD_0_12,
osnmaMsg_sptr->TOW_sf0);
LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d=" << static_cast<int>(tv.svId) << ", TOW=" << static_cast<int>(osnmaMsg_sptr->TOW_sf0) << "): 0b" << nav_data_ADKD_0_12;
// LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d=" << static_cast<int>(tv.svId) << ", TOW=" << static_cast<int>(osnmaMsg_sptr->TOW_sf0) << "): 0b" << nav_data_ADKD_0_12;
osnma_object->msg_handler_osnma(pmt::make_any(tmp_obj_osnma));
}
}
@ -375,7 +411,7 @@ bool OsnmaTestVectors::feedOsnmaWithTestVectors(osnma_msg_receiver_sptr osnma_ob
tv.svId,
nav_data_ADKD_4,
osnmaMsg_sptr->TOW_sf0);
LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d=" << static_cast<int>(tv.svId) << ", TOW=" << static_cast<int>(osnmaMsg_sptr->TOW_sf0) << "): 0b" << nav_data_ADKD_4;
// LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d=" << static_cast<int>(tv.svId) << ", TOW=" << static_cast<int>(osnmaMsg_sptr->TOW_sf0) << "): 0b" << nav_data_ADKD_4;
osnma_object->msg_handler_osnma(pmt::make_any(tmp_obj_osnma));
}
}